Security API

From wiki.zmanda.com
Jump to navigation Jump to search

Introduction

This is a document of the API for defining and utilizing multiple security and transport mechanisms for the Amanda network protocol. The goal of this API is to allow several different forms of communication and authentication to exist between the Amanda server and its clients. This is variously known as the "Secure API", "Security API", and "Authentication"

Overview

The security API connects two Amanda processes over the network. One process, the initiator, connects to the other, the target. During backup operations, the initiator is the server. For amrecover(8), the client is the initiator.

Once two processes are connected and any necessary authentication has taken place, the communication channel consists of a message-based protocol coupled with an arbitrary number of streams. The protocol is used to set up any streams that will be used in a conversation. In practice, the initiator of a connection can be any application, the target is always an amandad instance. Amandad implements the Amanda protocol, and implements numerous special cases for common Amanda applications.

Note that streams cannot be half-closed. All uses of streams in Amanda are currently terminated by the service, although nothing in the protocol itself dictates this.

For the user-level perspective on the Security API, see amanda-auth(7).

Implementation

The implementation of the Security API is some of the most tangled, buggy code you have ever seen. It works, but only because the bugs are carefully balanced against one another. See /Insanity for Dustin's notes on just how bad this is.

Driver Details

Amanda provides several bundled drivers, with names like "BSD", "BSDTCP", or "SSH". This section describes the operation of these drivers, as bounded by TCP/IP (or UDP/IP) and the Amanda Protocol.

BSDTCP

This driver multiplexes the protocol and all streams over a single TCP connection. This is accomplished using TCPM Framing to tokenize writes for each stream. A connection is initiated by making a TCP connection from a reserved port to port 10080 on the remote system. In general, inetd or xinetd takes care of invoking amandad in response to this connection, but this is just an implementation detail.

Any REQ packet is prefixed before transmission with the string

SECURITY USER username\n

On receipt, a REQ packet is scanned for this string, and the usual BSD, ahem, "authentication" steps are taken:

  • verify that the peer port is reserved (less than 1024)
  • verify forward and reverse DNS for the peer address match
  • verify the supplied username appears in .amandahosts, paired with the matching hostname

LOCAL

This is similar to BSDTCP, but instead of using a TCP connection, the initiator directly executes amandad with pipes set up for its input and output. The protocol and all streams are multiplexed over these pipes, again using TCPM Framing to tokenize writes for each stream.

No authentication is performed.

SSH

This is similar to LOCAL, but instead of invoking amandad directly, the initiator invokes an SSH command designed to execute amandad on the remote system. Everything else is identical to LOCAL auth.

Again, no authentication (beyond that done by SSH itself) is performed.

BSD, BSDUDP

These protocols are old, crusty, and difficult to debug. Avoid using them, and certainly avoid implementing anything new with them!

Protocol

See Amanda Protocol for the protocol that is used with a security handle.

Implementation

Starting amandad

In most cases, the target is implemented by amandad. The mechanism by which amandad is started depends on the authentication. For the BSD* authentications, it's started from (x)inetd with its stdin and stdout tied to the socket that triggered the startup. For most other authentications, like ssh or local, it is started directly, but again stdin and stdout are used for communication.

For some authentication mechanisms, amandad expects to be started as root, and immediately drops root privileges. For others, it expects to start as CLIENT_LOGIN.

The program takes the following command-line arguments:

-auth=$auth
specifies the authentication mechanism in use (default "BSD")
-no-exit
do not exit when all requests have been satisfied (connection-oriented authentications only; all others wait 30s after the last packet before exiting)
-tcp=$port
-udp=$port
bind to and listen on a port (for debugging of BSD* auths)

If any other command-line options are present, they are assumed to be service names, and amandad will only launch the named services. amdump is a shortcut for the services required for amdump: noop, sendsize, sendbackup, and selfcheck.

Once amandad is started, it begins listening for protocol packets. It creates streams as required by the service - see the Amandad Service Protocol.

Using the API

The Security API is (as of this writing) only available from C code. The interface is well-described in common-src/security.h.

Initiating a Security API Connection

To initiate a connection:

  • Get a security_driver for the auth.
 serdrv = security_getdriver(auth);
  • Build a REQ string.
 req = "text for the REQ packet";
  • Send the REQ to server_name and schedule program_response to be called on receipt of the REP packet.
 protocol_sendreq(server_name, secdrv, generic_get_security_conf,
                  req, STARTUP_TIMEOUT, program_response, &response);
  • It's protocol_run() that will do the amanda protocol exchange with the client; it will call program_response when the REP packet is received.
 protocol_run();
  • In program_response, you should
 check the REP packet
 call security_close_connection if you don't plan to send another request to that host
 open any security streams required
  • You should now register some function to read from all security_stream otherwise you will lose data
 security_stream_read(security-stream, reg_read_function,)
  • Process all security events
 event_loop(0);

The amservice(8) utility's source code provides a simple example of this process.

It's possible to not register readers for all stream and use security_stream_read_sync() which is like event_loop(0) but returns as soon as something is read for that security_stream.

Implementing a Service

See Amandad Service Protocol for more detail on the interactions between amandad and service executables.