haserl(1)
| haserl(1) | General Commands Manual | haserl(1) |
NAME
haserl - Html And Shell Embedded Report Language
SYNOPSIS
#!/usr/bin/haserl [ shell=pathspec ] [
--upload-dir=dir ] [ --upload-limit=
limit ] [ --verbatim ] [ --accept-all ]
[ text ] [ <? shell script ?> ] [ text ] ...
DESCRIPTION
Haserl is a small cgi wrapper that enables shell scripts to be embedded into html documents. It is very small, so it can be used in embedded environments, or where something like PHP is too big.
It combines three features into a small cgi engine:
- It transparently parses POST and GET requests, placing form-elements as name=value pairs into the environment for the CGI script to use. This is somewhat like the uncgi wrapper.
- It prints the contents of the script as html, and conditionally interprets text within <? ... ?> as shell script. This makes writing haserl scripts somewhat like writing PHP CGI scripts.
- It can optionally be installed to drop its permissions to the owner of the script, giving it some of the security features of suexec or cgiwrapper.
OPTIONS SUMMARY
This is a summary of the command-line options. Please see the OPTIONS section under the long option name for a complete description.
-a --accept-all
-s, --shell
-U, --upload-dir
-u, --upload-limit
-V, --verbatim
OPTIONS
- --accept-all
- The program normally accepts POST data only when the REQUEST_METHOD is POST and only accepts data on the URL data when the REQUEST_METHOD is GET. This option allows both POST and URL data to be accepted regardless of the REQUEST_METHOD. When this option is set, the REQUEST_METHOD takes precedence (e.g. if the method is POST, FORM_variables are taken from COOKIE data, GET data, and POST data, in that order. If the method is GET, FORM_variables are taken from COOKIE data, POST data, and GET data.) The default is not to accept all input methods - just the COOKIE data and the REQUEST_METHOD.
- --shell=pathspec
- Specify an alternative bash-like shell to use. Also can be used to specify command-line options to a shell. For instance, --shell='/bin/dash -x' will use the dash shell with the debugging option. Defaults to "/bin/sh"
- --upload-dir=pathspec
- Specify an alternative location to place uploaded files from the client. The cgi must have create rights to in this directory. Defaults to "/tmp".
- --upload-limit=limit
- Allow a mime-encoded file up to limit KB to be uploaded. The default is 0KB (no uploads allowed). To remain compatible with haserl versions 0.8.0 and earlier, specifying this option with no parameter will allow a 2MB upload. Note that mime-encoding adds 33% to the size of the data. Defaults to "0", or no uploads allowed.
- --verbatim
- Early versions of haserl would print segments of html that contained only whitespace as is (verbatim). For instance:
<? echo -n "world" ?>
OVERVIEW OF OPERATION
In general, the web server sets up several environment variables, and then uses fork or another method to run the CGI script. If the script uses the haserl interpreter, the following happens:
- If haserl is installed suid root, then uid/gid is set to the owner
of the script.
The environment is scanned for HTTP_COOKIE, which may have been set by the web server. If it exists, the parsed contents are placed in the local environment.
The environment is scanned for REQUEST_METHOD, which was set by the web server. Based on the request method, standard input is read and parsed. The parsed contents are placed in the local environment.
haserl forks and a sub-shell (typically /bin/sh) is started. File descriptor 5 is reserved for a special communication channel with the parent process.
The script is tokenized, parsing haserl code blocks from raw text.
The tokenized script is processed. If the token is raw text, it is sent to stdout (to the web client.) If the token is a command block, the shell script is sent to the sub-shell, with the following command appended:
- echo $? >&5
- This last command sends the final exit status of the script back to the
parent process.
When the last token has been processed, the sub-shell is closed and the haserl interpreter terminates.
CLIENT SIDE INPUT
The haserl interpreter will transparently process data sent via the GET or POST method from the client. The name of the variable follows the name given by the client, except that a prefix (typically FORM_) is prepended. For example, if the client sends "foo=bar", the environment variable is FORM_foo=bar.
For the GET method, data sent in the form %xx is translated into the characters they represent.
For the POST method, if the data is sent using multipart/form-data encoding, the data is automatically decoded. This is typically used when files are uploaded from a web client.
- NOTE
- When a file is uploaded to the web server, it is stored in the upload-dir directory. TheFORM_variable=variable contains the name of the file uploaded (as specified by the client.) The FORM_variable_name= variable contains the name of the file in upload-dir that contains the uploaded content. To prevent malicious clients from filling up upload-dir on your web server, file uploads are only allowed when the --upload-limit option is used to specify how large a file can be uploaded.
If the client sends data both by POST and GET methods, then haserl will parse only the data that corresponds with the REQUEST_METHOD variable set by the web server, unless the accept-all option has been set. For example, a form called via POST method, but having a URI of some.cgi?foo=bar&otherdata=something will have the POST data parsed, and the foo and otherdata variables are ignored.
If the web server defines a HTTP_COOKIE environment variable, the cookie data is parsed. Cookie data is parsed before the GET or POST data, so in the event of two variables of the same name, the GET or POST data overwrites the cookie information.
LANGUAGE
The following language structures are recognized by haserl.
- RUN
-
<? [shell script] ?>Anything enclosed by <? ?> tags is sent to the sub-shell for execution. The text is sent verbatim. - IF / THEN / ELSE
-
The shell script is run. If the final result ($?) is 0, the following tokens are evaluated. If the final result is not 0, then the optional else (<?el) clause is evaluated. It is possible to run multiple shell commands within the <?if tag; the last command run determines the final result.
<?if [shell script]?>
[ <?el [optional comment] ?> ]
<?fi [optional comment] ?>Note: Commands can be nested. It is possible to have a mixture of raw text and <?.. tokens enclosed inside an if or elsetoken.
- ABORT
-
<?ab [optional comment] ?>Abort the script. This is useful if an <?if test results in a condition where further CGI processing should stop. This command will cause haserl to exit with a return code other than zero. - INCLUDE
-
<?in pathspec ?>Include another file verbatim in this script. The file is included when the script is initially parsed, and any error messages referencing line numbers will include the number of lines in this file.
EXAMPLES
- WARNING
- The examples below are simplified to show how to use haserl. You should be familiar with basic web scripting security before using haserl (or any scripting language) in a production environment.
- Simple Command
-
#!/usr/local/bin/haserl content-type: text/plain <? env ?>
Prints the results of the env command as a mime-type "text/plain" document. This is the haserl version of the common printenv cgi.
- IF without ELSE clause
-
#!/usr/local/bin/haserl content-type: text/html <html> <body> <?if [ "$(hostname)" = "localhost" ] ?> My hostname is localhost <?fi?> </body> </html>
Sends a mime-type "text/html" document to the client. If the hostname command returns "localhost" then My hostname is localhost is printed. Otherwise the html document is empty.
- IF with ELSE clause
-
#!/usr/local/bin/haserl content-type: text/html <html> <body>f <?if [ "$(hostname)" = "localhost" ] ?> You really should change your hostname <?el?>
Congratulations! Your host is now named <? hostname ?> <?fi?> </body> </html>Sends a mime-type "text/html" document to the client. If the hostname command returns "localhost" then You really should change your hostname is printed. Otherwise, the document contains Congratulations! Your host is now named hostname Where hostname is the name of the host where the script is running.
- Self Referencing CGI with a form
-
#!/usr/local/bin/haserl content-type: text/html <html><body> <h1>Sample Form</h1> <form action="<? echo -n $SCRIPT_NAME ?>" method="GET"> <? # Do some basic validation of FORM_textfield
# To prevent common web attacks
FORM_textfield=$( echo "$FORM_textfield" | sed "s/[^A-Za-z0-9 ]//g"
?> <input type=text name=textfield Value="<? echo -n "$FORM_textfield" | tr a-z A-Z ?>" cols=20> <input type=submit value=GO> </form><html> <body>Prints a form. If the client enters text in the form, the CGI is reloaded (defined by $SCRIPT_NAME) and the textfield is sanitized to prevent web attacks, then the form is redisplayed with the text the user entered. The text is uppercased.
- Uploading a File
-
#!/usr/local/bin/haserl -u content-type: text/html <html><body> <form action="<? echo -n $SCRIPT_NAME ?>" method=POST enctype="multipart/form-data" > <input type=file name=uploadfile> <input type=submit value=GO> <br> <?if test -n "$FORM_uploadfile" ?>
<p>
You uploaded a file named <b><? echo -n $FORM_uploadfile_name ?></b>, and it was
temporarily stored on the server as <i><? echo $FORM_uploadfile ?></i>. The
file was <? cat $FORM_uploadfile | wc -c ?> bytes long.</p>
<? rm -f $FORM_uploadfile ?><p>Don't worry, the file has just been deleted
from the web server.</p> <?el?>
You haven't uploaded a file yet. <?fi?> </form> </body></html>Displays a form that allows for file uploading. This is accomplished by using the -u on the command line and by setting the form enctype to multipart/form-data. If the client sends a file, then some information regarding the file is printed, and then deleted. Otherwise, the form states that the client has not uploaded a file.
- ABORT command
-
#!/usr/local/bin/haserl --verbatim <?if [ "$(hostname)" = "localhost" ] ?><?ab?> <?el?>content-type: text/html <html> <body> ... </body> </html> <?fi?>
If the hostname command returns "localhost" then immediately abort the script, don't even send the HTTP headers. Otherwise, continue processing the script as normal.
- NOTE
- In this example the line spacing is important because the --verbatim flag was set. With --verbatim,haserl will send linefeeds where they appear in the script, so the <?if and <?ab tokens must appear on the same line for the HTTP headers to be sent correctly.
ENVIRONMENT
In addition to the environment variables inherited from the web server, the following environment variables are always defined at startup:
- HASERLVER
- haserl version - an informational tag.
- SESSIONID
- A hexadecimal tag that is unique for the life of the CGI (it is generated when the cgi starts; and does not change until another POST or GET query is generated.)
- HASERL_ACCEPT_ALL
- If the --accept-all flag was set, -1, otherwise 0.
- HASERL_CGIMODE
- If the initial script haserl started under (before any includes) begins with a #!.... -1, otherwise 0. when haserl is not in CGI mode, it refuses to read standard input or standard output for request data.
- HASERL_SHELL
- The name of the shell haserl started to run sub-shell commands in.
- HASERL_UPLOAD_DIR
- The directory haserl will use to store uploaded files.
- HASERL_UPLOAD_LIMIT
- The number of KB that are allowed to be sent from the client to the server.
- HASERL_VERBATIM
- If the --verbatim flag was set, -1, otherwise 0.
These variables can be modified or overwritten within the script, although the ones starting with "HASERL_" are informational only, and do not affect the running script.
SAFETY FEATURES
There is much literature regarding the dangers of using shell to program CGI scripts. haserl contains some protections to mitigate this risk.
- Environment Variables
- The code to populate the environment variables is outside the scope of the
sub-shell. It parses on the characters ? and &, so it is harder for a
client to do "injection" attacks. As an example,
foo.cgi?a=test;cat /etc/passwd could result in a variable being
assigned the value test and then the results of running cat
/etc/passwd being sent to the client. Haserl will assign the
variable the complete value: test;cat /etc/passwd
It is safe to use this "dangerous" variable in shell scripts by enclosing it in quotes; although validation should be done on all input fields.
- Privilege Dropping
- If installed as a suid script, haserl will set its uid/gid to that of the owner of the script. This can be used to have a set of CGI scripts that have various privilege. If the haserl binary is not installed suid, then the CGI scripts will run with the uid/gid of the web server.
NAME
The name "haserl" comes from the Bavarian word for "bunny." At first glance it may be small and cute, but haserl is more like the bunny from MontyPython&TheHolyGrail. In the words of Tim the Wizard, That's the most foul, cruel & bad-tempered rodent you ever set eyes on!
Haserl can be thought of the cgi equivalent to netcat. Both are small, powerful, and have very little in the way of extra features. Like netcat, haserl attempts to do its job with the least amount of extra "fluff".
BUGS
The parser is primitive. It can be confused regarding the line where an error occurs.
AUTHOR
Nathan Angelacos <nangel@users.sourceforge.net>
SEE ALSO
php(http://www.php.net) uncgi(http://www.midwinter.com/~koreth/uncgi.html) cgiwrapper(http://cgiwrapper.sourceforge.net)
| October 2005 |
