procmail
[Top] [All Lists]

A file server in procmail: advices required

1996-11-11 08:13:45
I need to implement a mail file server (the "clients" are mostly in Africa,
where many people still have only email access to the net). After reading
the procmailex man page, I made a prototype in procmail but I would like to
have some advices, specially on security issues.

First, the background. I need the server to be able to serve a whole
directory tree (many people will put files in the server, each will be
responsible of a directory). So I have to accept "/" in the file names.
Apart from it, I accept only letters, numbers, underscores and dashes (this
limits the name of the files in the directory but it doesn't seem a big
deal).

The server needs to be quite robust, because clients will make mistakes
(bad spelling of commands or filenames, etc) and I have to try to catch
them and to provide sensible diagnostics, both to the maintainer and to the
client.

Crackers could request /etc/passwd. So, I append "./" in front of the file
names. I test ".." anywhere in the file name to be sure people will not
come up to another directory and, since I do not accept """ (the quote) in
the file name, I think I'm safe from requests like ."."/etc/passwd.

Does anyone have comments to make? Do you know an already made fileserver
somewhere (I got many examples, such as the small one in procmailex or the
procmail library of Alan Stebbens <aks(_at_)sgi(_dot_)com> but I did not find a
ready-to-go software with my requirements)?

------------
Here is the prototype, for those who are interested to review it. It is the
part of a lerger .procmailrc, the same account processes other services:

# File server
#VERBOSE=on
ME="fileserver(_at_)domain(_dot_)org"
URL="http://www.domain.org/";
MAINTAINER="bortzmeyer"
#MAKE_REPLY="formail -rt -A'From: $ME' -A'X-URL: $URL' -iOld-Subject
-A'MIME-Version: 1.0' -A'Content-type: text/plain; charset=iso-8859-1'
-A'Content-Transfer-Encoding: 8bit' "
MAKE_REPLY="formail -rt -A'From: $ME' -A'X-URL: $URL' -iOld-Subject "
SEND_HELP="cat messages/help"

# The file server. First, is it a request to it?
:0
* $ ^TO$ME
{

        MAILDIR=/public # chdir to the fileserver directory

        # If request looks OK, do nothing
        # Check it doesn't come form a daemon, it is not a loop, etc.
        # We do not reply to these (may be we should reply to unknown
        # commands?)
        :0f
        * !^FROM_DAEMON
        * $ !^X-Loop: $ME
        | formail -A"X-loop: $ME"

        # Former conditions are false (i.e., something is wrong in the
        # request.
        :0E
        {
                :0f
                | formail -i"X-Diagnostic: ERROR: Not safe to reply (No
reply sent)"
                :0
                ! $MAINTAINER
        }

        # Check it is a request we understand,
        # File name is mandatory for get requests, directory name is not
        # for ls requests.
        :0
        * !^Subject: *(send( *file|)|get|retrieve) *[-A-Za-z0-9_\./]+ *$
        * !^Subject: *(ls|index|dir(ectory|)) *[-A-Za-z0-9_\./]* *$
        * !^Subject: *help
        {
                :0c
                | ( sh -c "$MAKE_REPLY" ; \
                        echo "Unknown request: see the documentation:" ; \
                        echo -e "\n\n----\n\n" ; \
                        sh -c "$SEND_HELP" ) | $SENDMAIL -t

                :0
                {

                        :0f
                        | formail -i"X-Diagnostic: ERROR: Unknown request
(reply sent)"

                        :0
                        ! $MAINTAINER

                }

        }

        # Canonicalize GET requests
        :0f
        * ^Subject: *(send( *file|)|get|retrieve) *\/[-A-Za-z0-9_\./]+ *$
        | formail -i"Subject: GET $MATCH"

        :0
        * ^Subject: GET
        {

                # Check it doesn't include .. (cheating attempt), etc.
                :0
                * ^Subject: GET .*\.\.
                {
                        :0f
                        | formail -i"X-Diagnostic: ERROR: SECURITY PROBLEM
(no reply sent)"
                        :0
                        ! $MAINTAINER
                }

                :0c
                * ? test -r $MATCH
                | ( sh -c "$MAKE_REPLY" ; \
                        echo "Your request of ${MATCH}: " ; \
                        echo -e "\n\n------------\n\n" ; \
                        cat ./$MATCH )  | $SENDMAIL -t

                :0E
                {

                        :0f
                        | formail -i"X-Diagnostic: ERROR: File does not
exist (reply sent)"

                        :0c
                        | (sh -c "$MAKE_REPLY" ; \
                                echo "Sorry, file $MATCH does not exist." ; \
                                echo "May be you should check the spelling." ) \
                                        | $SENDMAIL -t
                }

                :0f
                * !^X-Diagnostic: ERROR
                | formail -i"X-Diagnostic: OK (File $MAILDIR/$MATCH sent)"

        }

        # No directory name indicated
        :0
        * ^Subject: *(index|ls|dir(ectory|)) *$
        {

                SUBJECT=`formail -xSubject:`

                :0f
                | formail -i"Subject: $SUBJECT /"

        }

        # Canonicalize INDEX requests
        :0f
        * ^Subject: *(index|ls|dir(ectory|)) *\/[-A-Za-z0-9_\./]+ *$
        | formail -i"Subject: INDEX $MATCH"

        :0
       * ^Subject: INDEX \/[-A-Za-z0-9_\./]+ *$
        {

                DIR=$MATCH

                # Check it doesn't include .. (cheating attempt), etc.
                :0
                * ^Subject: INDEX .*\.\.
                {
                        :0f
                        | formail -i"X-Diagnostic: ERROR: SECURITY PROBLEM
(no reply sent)"
                        :0
                        ! $MAINTAINER
                }

                :0c
                * ? test -d ./$DIR
                | ( sh -c "$MAKE_REPLY" ; \
                        echo "Your request of the list of files in ${DIR}: " ; \
                        echo -e "\n\n------------\n\n" ; \
                        ls -ld ./$DIR ; echo "" ; ls -l ./$DIR )  \
                                | $SENDMAIL -t

                :0E
                {

                        :0f
                        | formail -i"X-Diagnostic: ERROR: Directory does
not exist (reply sent)"

                        :0c
                        | (sh -c "$MAKE_REPLY" ; \
                                echo "Sorry, directory $DIR does not exist." ; \
                                echo "May be you should check the spelling." ) \
                                        | $SENDMAIL -t
                }

                :0f
                * !^X-Diagnostic: ERROR
                | formail -i"X-Diagnostic: OK (Index $MAILDIR/$DIR sent)"

        }
        :0
        * ^Subject: *help
        {
                :0c
                | (sh -c "$MAKE_REPLY" ; \
                        sh -c "$SEND_HELP" ) \
                                        | $SENDMAIL -t
                :0f
                * !^X-Diagnostic: ERROR
                | formail -i"X-Diagnostic: OK (Help sent)"

        }

}

:0
! $MAINTAINER


------------
Here is its documentation:

Hello,

(I'm not a native English speaker, so there are many faults
here. I don't mind remarks and fixes.)

This is the file server documentation. This server
can send you back, by electronic mail, any file publically
available here. Requests have to be send to <fileserver(_at_)domain(_dot_)org>
and the actual commands have to be put in the subject field, NOT
in the message body.

Three commands are understood by the server: GET, INDEX and HELP.
The first one sends back the request file, the second one sends
a listing of all files in the named directory, the third one sends
this text.

Here is an example of a file request:

From: yourself(_at_)domain(_dot_)org
To: fileserver(_at_)domain(_dot_)org
Subject: get Globenet/statuts

This message, after being processed by the file server, will
send you the file "Globenet/statuts".

If you don't know the exact name of a file or if you're unsure,
just send an INDEX request like this one:

From: prenom(_dot_)nom(_at_)domain(_dot_)org
To: fileserver(_at_)domain(_dot_)org
Subject: Index Globenet

This message, once received by the file server, will send
you back the list of all files in the "Globenet" directory.

This list uses the Unix "ls" format. If you find it hard to decipher,
here is an explanation of the various fields it includes:

Type and       Person in charge   Size of  Date   Name of
protections    (here "bortz")     file in  of     file
(d = "directory")                 bytes    file
----------------------------------------------------------------
-rw-r--r--   1 bortz    users      662740 Nov  8 14:54 my_life.txt
drwxr-xr-x   8 bortz    users         512 Nov  7 11:59 tutorial

Possible traps and pitfalls:

About the file names: the server is an Unix machine and hence is
case-sensitive (difference between uppercase and lowercase). "get
File" and "get FILE" are two different requests.
Pay attention to the spelling of the file. (Requests are case-
insensitive, "GET" is the same thing as "gEt".)

The server accepts some aliases for commands like SEND for GET,
LS or DIR for INDEX, etc.

For safety reasons, all request are stored and kept.


<Prev in Thread] Current Thread [Next in Thread>