Security API: Difference between revisions

From wiki.zmanda.com
Jump to navigation Jump to search
(→‎The Problem: restate, give overview)
(move in some new description of the amandad startup)
Line 11: Line 11:


Both Amanda backup {{man|8|amdump}} and recovery process {{man|8|amrecover}} use this protocol. Psuedo code to how  to do security setup on the Amanda server and security setup from Amanda service on the client are in the next two sections.
Both Amanda backup {{man|8|amdump}} and recovery process {{man|8|amrecover}} use this protocol. Psuedo code to how  to do security setup on the Amanda server and security setup from Amanda service on the client are in the next two sections.
=== Starting 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.  <tt>amdump</tt> is a shortcut for the services required for amdump: <tt>noop</tt>, <tt>sendsize</tt>, <tt>sendbackup</tt>, and <tt>selfcheck</tt>.
Once amandad is started, it begins listening for protocol packets on the [[Security API]] connection.  It creates streams as required by the service - see the [[Amandad Service Protocol]].


==Server pseudo code==
==Server pseudo code==

Revision as of 05:40, 4 February 2010

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.

Context

Amanda communication takes place between two hosts. 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. While 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.

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

Both Amanda backup amdump(8) and recovery process amrecover(8) use this protocol. Psuedo code to how to do security setup on the Amanda server and security setup from Amanda service on the client are in the next two sections.

Starting 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 on the Security API connection. It creates streams as required by the service - see the Amandad Service Protocol.

Server pseudo code

  • 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, 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 the security_stream if needed.
  • 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);

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

Client pseudo code

  • On the client side, it's amandad does all the security setup, your service should only work with 8 fd opened by amandad.
 fd 0:  Where you read the REQ packet
    1:  Where to write your REP packet.
    50: writer fd for the first security_stream
    51: reader fd for the first security_stream
    52: writer fd for the second security_stream
    53: reader fd for the second security_stream
    54: writer fd for the third security_stream
    55: reader fd for the third security_stream
  • Read the REQ packect from fd 0 (stdin) until EOF.
  • Write the REP packet to fd 1 (stdout) and close it. To stream data to the server, the REP packet must have a "CONNECT" line.
 printf("CONNECT TAG1 %d TAG2 %d\n", DATA_FD_OFFSET, DATA_FD_OFFSET+1);

The TAG can be anything, there is a maximum of 3 data streams.

The API

The security API was designed to be a layer in between the core logic of Amanda and the transport and authentication of the protocol and dumps.

The component server and client programs now deal with abstract concepts instead of concrete udp and tcp handles.

The prefix "security_" is reserved for use as the namespace of this API. protocol packet transmission functions

These functions exist for transmitting pkt_t's between the client and server.

These functions operate on security_handle_t objects. These objects are described later.

security_getdriver

const security_driver_t *security_getdriver(const char *drivername);

Given a security type ("KRB4", "BSD", "SSH", etc), returns a pointer to that type's security_driver_t (section 4.1), or NULL if no driver exists.

security_connect

void security_connect(const security_driver_t *h, const char *hostname, char *(*conf_fn)(char *arg, void *arg), void (*fn)(void *arg, security_handle_t *h, security_status_t s), void *arg);

Given a security driver, and a hostname, calls back with a security_handle_t (section 4.2) that can be used to communicate with that host. The status arg to the callback is reflects the success of the request. Error messages can be had via security_geterror().

This is expected to be the Amanda server's interface for setting up connections to clients.

conf_fn is used to determine configuration information. If NULL, no configuration information is available.

security_accept

void security_accept(const security_driver_t *h, int in, int out, void (*callback)(security_handle_t *, pkt_t *));

Given a security driver, an input file descriptor, and an output file descriptor, and a callback, when new connections are detected on the given file descriptors, the function is called with a newly created security handle and the initial packet received.

This is expected to be the Amanda daemon's interface for setting up incoming connections from the Amanda server. The file descriptors are typically 0 and 1 (stdin/stdout).

This function uses the event interface, and only works properly when event_loop() is called later in the program.

security_close

void security_close(security_handle_t *h);

Closes a connection created by a security_connect() or security_accept().

security_sendpkt

int security_sendpkt(security_handle_t *h, const pkt_t *pkt);

Transmits a pkt_t over a security handle. Returns 0 on success, or negative on error. A descriptive error message can be obtained via security_geterror().

security_recvpkt

int security_recvpkt(security_handle_t *h, void (*callback)(void *arg, pkt_t *pkt, security_status_t), void *arg, int timeout);

Requests that when incoming packets arrive for this handle, the given function is called with the given argument, the received packet, and the status of the reception.

If a packet does not arrive within the number of seconds specified in the 'timeout' argument, RECV_TIMEOUT is passed in the status argument of the timeout.

On receive error, the callback's status argument will be set to RECV_ERROR. An error message can be retrieved via security_geterror().

On successful reception, RECV_OK will be passed in the status argument, and the pkt argument will point to a valid packet.

This function uses the event interface. Callbacks will only be generated when event_loop() is called.

security_recvpkt_cancel

int security_recvpkt_cancel(security_handle_t *h);

Cancels a previous recvpkt request for this handle.

security_geterror

const char *security_geterror(security_handle_t *h);

Returns a descriptive error message for the last error condition on this handle.

security_seterror

void security_seterror(security_handle_t *h, const char *msg, ...);

Sets the string that security_geterror() returns.

security_handleinit

void security_handleinit(security_handle_t *, const security_driver_t *);

Initializes a security_handle_t. This is meant to be called only by security drivers to initialize the common part of a newly allocated security_handle_t.

stream functions

These functions exist for transmitting random data over a stream-like connection.

These functions operate on security_stream_t objects, which are described later.

security_stream_server

security_stream_t *security_stream_server(security_handle_t *h);

Creates the server end of a security stream, and will receive a connection from the host on the other end of the security handle passed.

Returns a security_stream_t on success, and NULL on error. Error messages can be obtained by calling security_geterror() on the security handle associated with this stream.

security_stream_accept

int security_stream_accept(security_stream_t *);

Given a security stream created by security_stream_server, blocks until a connection is made from the remote end.

Returns 0 on success, and -1 on error. Error messages can be obtained by calling security_stream_geterror().

security_stream_client

security_stream_t *security_stream_client(security_handle_t *h, int id);

Creates the client end of a security stream, and connects it to the machine on the other end of the security handle. The 'id' argument identifies which stream on the other end to connect to.

Returns a security_stream_t on success, and NULL on error. Error messages can be obtained by calling security_geterror() on the security handle associated with this stream.

security_stream_close

void security_stream_close(security_stream_t *s);

Closes a security stream and frees up resources associated with it.

security_stream_auth

int security_stream_auth(security_stream_t *s);

Authenticate a connected security stream.

Returns 0 on success, and -1 on error. Error messages can be obtained by calling security_stream_geterror().

security_stream_id

int security_stream_id(security_stream_t *s);

Returns an identifier which can be used to connect to this security stream with security_stream_client().

Typical usage would be for one end of a connection to create a stream with security_stream_server(), and then transmit the id for that stream to the other side. The other side will then connect to that id with security_stream_client().

security_stream_write

int security_stream_write(security_stream_t *s, const void *buf, size_t bufsize);

Writes a chunk of data to the security stream. Returns 0 on success, or negative on error. Error messages can be obtained by calling security_stream_geterror().

security_stream_read

void security_stream_read(security_stream_t *s, void (*callback)(void *arg, void *buf, int bufsize), void *arg);

Requests that when data is ready to be read on this stream, the given function is called with the given arg, a buffer full of data, and the size of that buffer.

On error, the bufsize will be negative. An error message can be retrieved by calling security_stream_geterror().

This function uses the event interface. Callbacks will only be generated while in event_loop().

security_stream_read_sync

size_t security_stream_read_sync(security_stream_t *s, void **buf);

Return a buffer of data read from the stream. This function will block until something can be read, but other event will be fired. A pointer to the data is returned in *buf and the size of the buffer is returned.

On error, the size will be negative. An error message can be retrieved by calling security_stream_geterror(). This function uses the event interface.

security_stream_read_cancel

void security_stream_read_cancel(security_stream_t *s);

Cancels a previous read request.

security_stream_geterror

const char *security_stream_geterror(security_stream_t *h);

Returns a descriptive error message for the last error condition on this stream.

security_stream_seterror

void security_stream_seterror(security_stream_t *h, const char *msg, ...);

Sets the string that security_stream_geterror() returns.

Data Types

All visible data types are meant to be opaque to the caller. At no time should a caller have to access a member of an y data type directly. The API should always be used instead.

security_driver_t

This is a static object containing function vectors that implement the API for a particular security type.

security_handle_t

This is an object that describes a protocol connection to a remote server. There is one security_handle_t per request, and there can be many to the same remote host.

security_stream_t

This is an object that describes a data connection to a remote host. It is always associated and derived from a security_handle_t. Arbitrary data can be passed over a security stream.

security_status_t

This is an enumerated type that is passed to the callback of security_recvpkt and security_connect. The possible values it can have are:

S_OK - the pkt_t was received fine S_TIMEOUT - no pkt_t was received within the time specified in the timeout argument to security_recvpkt(). S_ERROR - an error occurred during reception. Call security_geterror() for more information.

SECURITY DRIVERS

Each security type is defined by a struct of function vectors. These methods implement the details of this security type.

This section will document each element of security_driver_t.

name

const char *name;

This is the name of the driver. This is used by security_getdriver() to associate a name with a driver type.

connect

void (*connect)(const char *hostname, void (*fn)(void *, security_handle_t *, security_status_t), void *);

This is the implementation of security_connect(). It actually sets up the connection, and then returns a structure describing the connection. The first element of this structure MUST be a security_handle_t, because it will be cast to that after it is passed up to the caller.

The first argument is the host to connect to. The second argument is a function to call when a connection is made. The third argument is passed to the callback.

The callback takes three arguments. The first is the caller supplied void pointer. The second is a newly allocated security handle. The third is a security_status_t flag indicating the success or failure of the operation.

accept

void (*accept)(int in, int out, void (*callback)(security_handle_t *handle, pkt_t *pkt));

This is the implementation of security_accept(). It is passed the input and output file descriptors and a callback. The callback takes a security handle argument and also an initial packet received for that handle.

close

void (*close)(void *handle);

The implementation of security_close().

sendpkt

int (*sendpkt)(void *handle, pkt_t *pkt);

The implementation of security_sendpkt(). Security information is usually added by the driver before transmission.

recvpkt

void (*recvpkt)(void *handle, void (*callback)(void *arg, pkt_t *pkt, security_status_t), void *arg);

The implementation of security_recvpkt(). It will typically be layered onto the event interface somehow. It can assume that a caller will eventually call event_loop().

recvpkt_cancel

void (*recvpkt_cancel)(void *handle);

The implementation of security_recvpkt_cancel(). Drivers should allow this to be run even if no recvpkt was scheduled, or if one was previously cancelled.

stream_server

void *(*stream_server)(void *handle);

Implementation of security_stream_server(). This function returns a object describing the stream. The first member of this object MUST be a security_stream_t, because it will be cast to that.

stream_accept

int (*stream_accept)(void *stream);

After calling stream_server, stream_accept must be called on the stream before it is fully connected.

stream_client

void *(*stream_client)(void *handle, int id);

Implementation of security_stream_client(). The id argument is something returned by security_stream_id(). Again, the handle is referenced counted.

This function returns a object describing the stream. The first member of this object MUST be a security_stream_t, because it will be cast to that.

stream_close

void (*stream_close)(void *stream);

Close and free up resources for an open stream.

stream_auth

int (*stream_auth)(void *stream);

Authenticate a connected stream.

stream_id

int (*stream_id)(void *stream);

Return a unique id for this stream. This is to be used by stream_client() to connect to this stream.

stream_write

int (*stream_write)(void *stream, const void *buf, size_t bufsize);

Implementation of security_stream_write.

stream_read

void (*stream_read)(void *stream, void (*callback)(void *arg, void *buf, int bufsize), void *arg);

Implementation of security_stream_read.

stream_read_cancel

void (*stream_read_cancel)(void *stream);

Implementation of security_stream_read_cancel.