Sieve Extprograms plugin for Pigeonhole

Relevant specifications
=======================

	doc/rfc/spec-bosch-sieve-extprograms.txt

Introduction
============

Sieve (RFC 5228) is a highly extensible machine language specifically tailored
for internet message filtering. For the Dovecot Secure IMAP server, Sieve
support is provided by the Pigeonhole Sieve plugin. This package includes a
plugin for Pigeonhole called "sieve_extprograms", which extends the Sieve 
filtering implementation with action commands for invoking a predefined set of
external programs. Messages can be piped to or filtered through those programs
and string data can be input to and retrieved from those programs.

The Sieve language is explicitly designed to be powerful enough to be useful yet
limited in order to allow for a safe server-side filtering system. Therefore,
the base specification of the language makes it impossible for users to do
anything more complex (and dangerous) than write simple mail filters. One of the
consequences of this security-minded design is that users cannot execute
external programs from their mail filter. Particularly for server-side filtering
setups in which mail accounts have no corresponding system account, allowing the
execution of arbitrary programs from the mail filter can be a significant
security risk. However, such functionality can also be very useful, for instance
to easily implement a custom action or external effect that Sieve normally
cannot provide.

The "sieve_extprograms" plugin provides an extension to the Sieve filtering
language adding new action commands for invoking a predefined set of external
programs. To mitigate the security concerns, the external programs cannot be
chosen arbitrarily; the available programs are restricted through administrator
configuration.

This extension is specific to the Pigeonhole Sieve implementation for the
Dovecot Secure IMAP server. It will therefore most likely not be supported by
web interfaces or GUI-based Sieve editors. This extension is primarily meant for
use in small setups or global scripts that are managed by the systems
administrator.

Implementation Status
---------------------

The "vnd.dovecot.pipe", "vnd.dovecot.filter" and "vnd.dovecot.execute" Sieve
language extensions introduced by this plugin are vendor-specific with draft
status and their implementation for Pigeonhole is experimental, which means that
the language extensions are still subject to change and that the current
implementation is not thoroughly tested.

Configuration
=============

The plugin is activated by adding it to the sieve_plugins setting:

sieve_plugins = sieve_extprograms

This plugin registers the "vnd.dovecot.pipe", "vnd.dovecot.filter" and
"vnd.dovecot.execute" extensions with the Sieve interpreter. However, these
extensions are not enabled by default and thus need to be enabled explicitly. It
is recommended to restrict the use of these extensions to global context by
adding these to the "sieve_global_extensions" setting. If personal user scripts
also need to directly access external programs, the extensions need to be added
to the "sieve_extensions" setting. 

The commands introduced by the Sieve language extensions in this plugin can
directly pipe a message or string data to an external program (typically a shell
script) by forking a new process. Alternatively, these can connect to a unix
socket behind which a Dovecot script service is listening to start the external
program, e.g. to execute as a different user or for added security.

The program name specified for the new Sieve "pipe", "filter" and "execute"
commands is used to find the program or socket in a configured directory.
Separate directories are specified for the sockets and the directly executed
binaries. The socket directory is searched first. Since the use of "/" in
program names is prohibited, it is not possible to build a hierarchical
structure.

Directly forked programs are executed with a limited set of environment
variables: HOME, USER, HOST, SENDER, RECIPIENT and ORIG_RECIPIENT. Programs
executed through the script-pipe socket service currently have no environment
set at all.

If a shell script is expected to read a message or string data, it must fully
read the provided input until the data ends with EOF, otherwise the Sieve action
invoking the program will fail. The action will also fail when the shell script
returns a nonzero exit code. Standard output is available for returning a
message (for the filter command) or string data (for the execute command) to the
Sieve interpreter. Standard error is written to the LDA log file. 

The three extensions introduced by this plugin - "vnd.dovecot.pipe",
"vnd.dovecot.filter" and "vnd.dovecot.pipe" - each have separate but similar
configuration. The settings that specify a time period are specified in
s(econds), unless followed by a d(ay), h(our) or m(inute) specifier character.
The following configuration settings are used, for which "<extension>" in the
setting name is replaced by either "pipe", "filter" or "execute" depending on
which extension is being configured.

sieve_<extension>_socket_dir =
  Points to a directory relative to the Dovecot base_dir where the plugin looks
  for script service sockets. 

sieve_<extension>_bin_dir =
  Points to a directory where the plugin looks for programs (shell scripts) to
  execute directly and pipe messages to.

sieve_<extension>_exec_timeout = 10s
  Configures the maximum execution time after which the program is forcibly
  terminated.

sieve_<extension>_input_eol = crlf
  Determines the end-of-line character sequence used for the data piped to
  external programs. The default is currently "crlf", which represents a
  sequence of the carriage return (CR) and line feed (LF) characters. This
  matches the Internet Message Format (RFC5322) and what Sieve itself uses as a
  line ending. Set this setting to "lf" to use a single LF character instead.

Examples
--------

Example 1: socket service for "pipe" and "execute"

plugin {
  sieve = ~/.dovecot.sieve

  sieve_plugins = sieve_extprograms
  sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute

  # pipe sockets in /var/run/dovecot/sieve-pipe
  sieve_pipe_socket_dir = sieve-pipe        

  # execute sockets in /var/run/dovecot/sieve-execute
  sieve_execute_socket_dir = sieve-execute
}

service sieve-pipe-script {
  # This script is executed for each service connection
  executable = script /usr/lib/dovecot/sieve-extprograms/sieve-pipe-action.sh

  # use some unprivileged user for execution
  user = dovenull

  # socket name is program-name in Sieve (without sieve-pipe/ prefix)
  unix_listener sieve-pipe/sieve-pipe-script {
  }
}

service sieve-execute-action {
  # This script is executed for each service connection
  executable = script /usr/lib/dovecot/sieve-extprograms/sieve-execute-action.sh

  # use some unprivileged user for execution
  user = dovenull

  # socket name is program-name in Sieve (without sieve-execute/ prefix)
  unix_listener sieve-execute/sieve-execute-action {
  }
}

Example 2: direct execution for "pipe" and "filter"

plugin {
  sieve = ~/.dovecot.sieve

  sieve_plugins = sieve_extprograms
  sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.filter

  # This directory contains the scripts that are available for the pipe command.
  sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe

  # This directory contains the scripts that are available for the filter
  # command.
  sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter
}

Using
=====

Refer to doc/rfc/spec-bosch-sieve-extprograms.txt for a specification of the
Sieve language extensions.

