Codebase list stealth / 7d9a9a9c-3361-4e5f-9a4d-1987ac2ff574/main README.flow
7d9a9a9c-3361-4e5f-9a4d-1987ac2ff574/main

Tree @7d9a9a9c-3361-4e5f-9a4d-1987ac2ff574/main (Download .tar.gz)

README.flow @7d9a9a9c-3361-4e5f-9a4d-1987ac2ff574/mainraw · history · blame

Stealth may run in one of three modes:

    1. Foreground, where stealth performs one integrity scan and terminates;

    2. Background, where stealth, after performing its initial integrity scan
       waits for additional commands and/or performs additional integrity scans
       after (possibly random) time intervals.

    3. IPC, where stealth sends commands to a stealth daemon process.

Communication between stealth processes running in modes 2 and 3 is realized
through a unix domain socket, which is created by the stealth daemon process. 

Figure documentation/images/communication.jpg shows the organization:

    * Stealth --daemon starts the daemon process, its childprocess
        (stealth/childprocess) starting the ipcInterface thread (separate
        threads are shown in light green), implemented by the
        stealth/ipcinterface function.

    * ChildProcess is the main thread of the running stealth daemon. It
        performs all the tasks (through stealth/doTasks). 

        DoChores performs some bookkeeping tasks, eventually passing control
        to stealth/processrequests. 

        JobsHandler runs the current task, which may be an integrity
        scan. 

    * IpcInterface is a separate thread preparing the next task for
        jobsHandler. It creates the Unix domain server socket, and waits for
        incoming request. Incoming requests are verified, and if OK they are
        forwarded to jobsHandler. IpcInterface's mode of operation is
        described in more detail below.

    * Stealth-ipc processes handle ipc requests. These processes connect to
        the stealth daemon's unix domain socket, passing it the received
        request. It then waits for a reply from the daemon. If the daemon
        reports an error the error message is shown, and stealth running in
        ipc mode terminates with exit value 1. Otherwise exit value 0 is
        returned and no additional output is shown.

Figure documentation/images/ipcinterface.jpg shows the inner workings of
the ipcInterface function.

    * Each cycle of ipcInterface's main loop handles one incoming
        connection. If a TERMINATE request is received, the ipcInterface
        thread ends. A TERMINATE request also ends the main (jobsHandler)
        thread. Once both threads have terminated the stealth daemon ends.

    * Incoming requests are validated (by Stealth::incomingRequest), and (if
        valid) initially stored in the local Runmode incoming, processing may
        be suspended until processRequests is ready to accept the next command
        (see below at nextTask's description).

    * Incoming requests are validated. For each possible request a matching
        ...request function handles the valiadation. If a request cannot be
        granted (i.e., is invalid), an error message is sent to the
        stealth-ipc process and another cycle starts.

        Given a current task (mode of operation) the following requests are
        valid: 

        ----------------------------------------------------------
        Actual Job:            Valid Incoming Request:
        ----------------------------------------------------------
        INTEGRITY_SCAN          RELOAD, RERUN, SUSPEND, TERMINATE
        RELOAD                  TERMINATE
        SUSPEND                 RELOAD, RESUME, SUSPEND, TERMINATE
        ----------------------------------------------------------

        Requesting SUSPEND while in SUSPEND mode has no effect (validation
        returns `nop'): in this case the stealth-ipc process is informed that
        there is no error but processRequests doesn't actually receive a new
        command to process

    * If a request is valid, the incoming request is stored in
        d_pending. Ongoing jobs may inspect d_pending to see whether they
        should prematurely stop (e.g., when a TERMINATE request arrives while
        an integrity scan is being executed, the scan will end after
        completing the command currently being processed)

    * Next, ipcInterface waits until jobsHandler is ready for the next task.
        When ready, notifyTask sets the next task in d_task, and d_pending is
        reset, after which jobsHandler is notified that it has another task to
        handle.

    * The next task may be TERMINATE. If so, ipcInterface ands (and
        jobsHandler will also end).



The Stealth::jobsHandler function handles incoming requests. This function
loops until (at the end of the loop) the d_task has been set to TERMINATE. At
the top of the loop the next job is determined. Once determined, the
requested task starts.

Figure documentation/images/nextjob.jpg shows the inner workings of
Stealth::nextJob.

When returning from nextJob the d_job binary Semaphore has decayed to zero. It
is incremented again by ipcInterface once a command is requested by an
ipc-stealth process and by some of the task-handling functions (reload,
resume, rerun).

Since ipcInterface and some of the task-handling functions may set the next
command to process, the permission to set the next command is set by the d_ipc
semaphore. Once ipcInterface returns from d_ipc.wait() d_ipc blocks until it
is incremented by d_ipc.notify(). Initially d_ipc blocks, but it is
incremented by startScan and suspend.

When nextJob returns the next task has been set. This is realized as follows:

    * If at daemon startup --repeat was not specified then nextJob simply
waits for a command which is received from an ipc-stealth process through
ipcInterface (alternatively, the command may be set by the mentioned
request-processing functions). 

    * time-limited wait: otherwise, nextJobs waits until either a command was
        set (as with the plain wait), or until a (random) delay has passed.
    
    * If no timeout occurred, then a command was specified and nextJob
        returns with the next command.

    * Otherwise, in suspend mode or while an integrity scan is still active,
        nextJob loops back to the time-limited wait step.

    * Otherwise, --repeat was specified, and the waiting time has passed, so
        the next task automatically becomes INTEGRITY_SCAN (and nextJob
        returns)


Integrity scans are performed by Stealth::integrityScan.

    



Flow control is handled by communicating through a Unix Domain Socket, which
uses the run-file. Requests are:

    suspend,
    resume,
    rerun       - rerun the full itegrity scan
    reload      - after the current scan has completed (it may already have
                    completed) load a new policy- and possibly skip-file
                    and perform another integrity scan.
    terminate

    Requests are received through the Unix domain socket defined by the run-file
    the daemon performs the request and replies with an answer indicating
    success or failure. Success is inferred when an empty line is received.
    Failure information contains an indication of the nature of the failure.

Requests are passed to the daemon from the function Stealth::contactPeer.

|    - the pid file is locked
|    - the request is written to the pid-file
|    - the pid file is unlocked.
| 
| A request is written to the PID file, followed by sending the daemon a SIGUSR1
| signal. When the daemon has completed the request it sends the requesting
| process a SIGUSR1 signal; 


Stealth uses a RunMode object to keep track of its current mode of operation
| now OBS? and an IPC object to handle the inter process communication.

The member Stealth::policyMode handles the actual commands.

policyMode:
==============
policyMode forks if a Stealth should run as a daemon, in which case the
fork's child process performs the daemon's tasks, and the parent process
immediately finishes. 

When a daemon starts its childProcess prepares the daemon (using
Fork::prepareDaemon), and the communication thread starts, defining the
unix domain server socket. Hereafter 'doTasks' starts.

communicator:
=============

The communicator creates the LocalServerSocket, and loops while d_run.mode()
is unequal TERMINATE. When a request arrives, the matching function is
called. These functions should return an empty string indicating that the
request can be handled. Otherwise the returned string contains an error
message which is returned to the requestor. 

If the request can be handled d_chore is notified, and the communicator waits
for d_communicate to return, indicating that the request has been
handled. When d_communicate.wait() returns the contents of d_result is
returned to the requestor. Again: an empty string indicates that the request
was successfully processed.

doTasks:
=========

With foreground runs 'doTasks' is directly started.

The flow of control handling requests is defined in processRequests,
called from doTasks. ProcessRequests defines a loop basically processing a
request and waiting for the next request ((waitForRequest)

The file integrity scan itself is handled by an IntegrityScanner object.

| 
| Communications with the daemon use the run-file and signals SIGUSR1 (and
| SIGTERM). The run-file by default contains the pid of the daemon process. The
| signalling stealth process adds a request to the
| file. The daemon reads the request and honors it if possible.


processRequests:
================

Stealth (when performing integrity scans) starts up with an initial mode
INTEGRITY_SCAN. At processRequests netTask() returns the next mode, which is
then processed by 'process'. 

NextTask() inspects the current mode. If it's WAIT wait() is called, which 



Possible task requests:
=======================
rerun:      OK in mode WAIT
suspend:    OK in mode SCAN, WAIT, SUSPEND
resume:     OK in mode SUSPEND
reload:     OK in mode SCAN, WAIT, SUSPEND
terminate:  always OK.