
###############################################################################
##                                                                           ##
##  This file/text is not yet complete (November 2022).                      ##
##  It describes the main concepts used for programming O-Saft.              ##
##                                                                           ##
###############################################################################


PROLOG

        Some texts herein are  literally or conceptual  identical to texts in
        other files. These texts are followed herein with for example:
          "> See also: OSaft/Doc/help.txt  CONCEPTS"

        It is recommended to cross-check changes of such texts and adapt them
        accordingly.


PRINCIPLES

        Quick overview about principles and concepts:

        Program execution
          * program terminates immediately on errors
          * shebang #! is followed by full path of program to be executed

        Program arguments
          * --help*  provides user help/usage
          * --test*  provides information about internal data
          * '+'  prefix for command arguments
          * '--' prefix for option arguments
          * the sequence of commands and options is not important

        Program output
          * each check produces one line of output
          * additional information (comments) are disabled by default
          * '='  prefix for documention/comments
          * '#'  prefix for verbose (and debug) messages


CONCEPTS

        The purpose of  O-Saft  is to do the work,  not to force the user  to
        learn a new tool or to install "newer" software first.
	However, the user "should do something" if necessary depending on the
        reported results.

        It follows the Perl principle:
          "Perl is a language for getting your job done."

        Before the concepts can be described,  we need to explained the words
        and terms. In particular:  developer vs. user,  commands vs. options,
        debug vs. trace vs. results/output,  code. For the deatils please see
        GLOSSAR  below.

        Following concepts are described first:
          * Developer vs. user
          * Commands vs. options
          * Verbose vs. trace (and debug)
          * Output and results
          * Errors, warnings, hints
          * Ancient systems

    Developer vs. user

        As said above, the main purpose of the tool(s) is/are to do the job.
        Therefore the user's view is the most important. This view covers the
        tools' usage with commands and options (arguments in general) and the
        results (output) shown by the tools.

        Developers also needs to understand the concept of the code.  In code
        the user's view is used. It is explicitly noted if a developer's view
        is meant.

    Commands vs. options

        In general "commands" are used for somthing to be done. "options" are
        used to configure or modify the behaviour of the commands.

        The tool(s) have many features that ease the user's tasks. It results
        in expense of  greater complexity  in parts of the code  (for example
        the parser for command line options and arguments).

        The design of the commands and options is very much influenced by
        linguistic principles. For examples, most options can be abrevated in
        various ways. Common things should be short and easy to remember, aka
        "concise and natural for humans".

        It's often a matter of opinion or taste how something should be done,
        for example command line arguments. Hence we try to allow all options
        and arguments a user might be used to (i.e. from other tools).

        Following general rules are used:
          * commands are prefixed with '+', for example  +cipher
          * options are prefixed with '--', for example  --cipher=AES
          * the sequence of commands and options is not important

        > See also: OSaft/Doc/help.txt  SYNOPSIS

        Rare exceptions are necessary to handle ambiguity. For example --help
        is used in general to print the user's help for a specific tool.  But
        if such a tool is a wrapper to passes  its arguments to another tool,
        user's help for this tool itself cannot be printed. Example:
          o-saft.pl --help  # print help for o-saft.pl
          o-saft    --help  # print help for o-saft.pl
          o-saft     -help  # print help for o-saft itself

        For some special tracing or debugging,  the sequence of arguments and
        options may be important. This most likely is necessary for  --v  and
        --trace*  options. Example:
          o-saft.pl +cn target             --exit=ARGS --traceargs
          o-saft.pl +cn target --traceargs --exit=ARGS
          o-saft.pl            --traceargs +cn target  --exit=ARGS

    Verbose vs. trace (and debug)

        Verbose output is intended for the user. It's enabled with the option
        --v . All verbose messages are prefixed with '#'.

        Except for hunting special problems with a target,  trace options are
        most likely for developers. These options are named  --trace* . Their
        messages are also prefixed with '#'.

        Debugging is intended for developers. No special options are provided
        for debugging beside the  --trace*  options. For more details see the
        section   "Debugging code"  below.

    Output and results

        All output is designed to make it  easily parsable by postprocessors.
        Following rules are used as default:
          * Lines for formatting or header lines start with  '='.
          * Lines for verbosity or tracing start with  '#'.
          * Errors and warnings start with  '**', hints start with '!!'.
          * Empty lines are comments ;-)
          * Label texts end with a separation character; default is  ':'.
          * Label and value for all checks are separated by at least one  TAB
            character.
          * Texts for additional information are enclosed in '<<'  and  '>>'.
          * 'N/A'  is used  when no proper information was found or provided.
            Replace  'N/A'  by whatever you think is adequate:  "No answer",
            "Not available",  "Not applicable",  ...

        Examples:
              === Title line ===
              = this is a comment
              Label for information or check:  TABresult
              !!Hint: above result depends on the target

        > See also: OSaft/Doc/help.txt  OUTPUT

        It is also often  a matter of opinion and taste how results should
        be presented. Most tools strictly follow the rule:
          one check result in one line of output
        For postprocess output (see above), think the UNIX pipe model.

        Results of checks are marked  'yes'  or 'no'.  This leaves the proper
        interpretation, if the result is "good" or "bad", to the user.

        > See also: OSaft/Doc/help.txt  CONCEPTS

        For status codes see  "STATUS CODE" below

    Errors, warnings, hints

        Errors, warnings and hints may be part of the output as needed. While
        errors and warnings are printed immediately as they occour during the
        program flow, hints are printed right after the corresponding result.

        Hints print an additional explanation of a specific result.

        Errors and warnings start with a unique 3-digit number.

        > See also: OSaft/Doc/help.txt  OUTPUT

        Note that an error terminates the tool immediately, unless the option
        --exitcode  was used.

    Ancient systems

        This tool should run on older systems too.
        Background:
        Most tools rely on the newest frameworks, libraries, modules or other
        gimmicks just to make work easy for programmers.  It seems that these
        programmers forget, that productive systems are still  most often one
        year or more behind the newest releases.  In practice, users of these
        tools are then forced to upgrade parts of the system or can't use the
        tool.

        Why?
        A program should do what the user wants and not the other way around,
        where the user does what the program (developer) wants.  Hence O-Saft
        tries hard to run on  ancient systems too.  It may miss some features
        then, but it should work. If "something" is missing or not working as
        expected, a  **WARNING  messages is printed. These warnings cannot be
        switched off the usual way with  --no-warning.


DEVELOPMENT

        This part describes concepts used for development.

        Following concepts are described first:
          * Documentation (user and development)
          * Progrmming (code) style
          * Handling erros and exceptions
          * Debugging code
          * Programming: arguments in general
          * Programming: +ciphers
          * Programming: +info +check
          * Testing
          * Make for development
          * Portability

    Documentation (user and development)

        Important information should come first in the documentation, details
        will follow. Cite:
          include Huffman coding (common constructions should be short), good
          end-weighting (the important information should come first), and a
          large collection of language primitives.

    Progrmming (code) style

        In general the code favours language constructs that are easy to read
        by humans, even they are unusual for other programming languages (for
        example postfix conditions).

        The code uses spaces for formating, mainly to make the code look like
        tabular data. It is the author's opinion  that tabular data is better
        readable (aka understanable) than continous (code) prose.  There's no
        strict rule about formatting of such "tables",  their style depend on
        the context they are used in.

        Because of our goal to support running on old systems too (see above)
        parts of the code make ugly checks for version numbers  and try to do
        checks in a cumbersome way. 
        Keep in mind: the code knows how to do the work, the user most likely
        does not know.

    Handling erros and exceptions

        As said before,  the purpose of the tools is to  simplify users' life 
        and not the developers' life.  We try hard to fullfil this goal.
        Programming is often done in a defensive way,  means that any kind of
        error (and exception if any) will be  catched and shown  to the user.
        Unfortnately not all errors can be foreseen.  In the GUI (o-saft.tcl)
        a "silent" catch is used sometimes, which disacrds any error message.

        There are often situations where the underlaying system misses tools,
        is improper configured or simply behaves strange.  If such conditions
        are identified, a proper  **WARNING  or  !!Hint  will be shown.  This
        may disturb or bore the user, but we don't hide what we detect, which
        might be suspiscious somehow.

        This tool checks security accurate, it dos not give a "good feeling".

    Debugging code

        Debugging is intended  for development only.  All additional code for
        debug, trace and verbose functionality is separated in its own file.
        As verbose functionality, and trace partly, makes sense for the user,
        debug does not.  However, using the  --trace*  options provide a huge
        amount of information too.
        Hence, no special commands and options for debugging are implemented.
        Either use perl's  -d  option, or change the code.  Further hints can
        be found in ... # TODO ...

    Programming: arguments in general

        A strong syntax for arguments is used to distinguish commands and
        options (as decribed above).
        As a lot of spelling variants and aliases are supported, this results
        currently (2018 ..) in an ugly parser.

        Known drawbacks, traps:

        O-Saft complains about unkwown commands (+cmd), but not about unknown
        options. This means that unknown options are silently ignored. 
        And more worse,  for unknown (or misspelled) options with parameters,
        like  --legacy=compact  the parameter (compact here)  then is treated
        as hostname.  Hopefully this may change in future.

    Programming: +ciphers

        There are three modes (technically spoken) are implemented  to detect
        the ciphers supported by the server:
          +cipher --ciphermode=intern
              - private implementation without using other perl modules
          +cipher --ciphermode=ssleay
              - using perl's IO::Socket:SSL module (mainly based on openssl)
          +cipher --ciphermode=openssl
              - using external openssl

        Since VERSION 22.02.22  --ciphermode=intern  is the default.

# TODO: ... 
To get the ciphers supported by the server, a basic connection is made
using IO::Socket:SSL->new(). This connection does not check the server's
certificate as it is not important for the cipher. It also uses SNI by
default (unless disabled with --no-sni), because most modern servers
expect TLS instead of SSL protocols and may fail with "handshake error"
(see man IO::Socket:SSL) if the server expects SNI but is not set.

Most likely IO::Socket:SSL->new() uses CTX_tlsv1_2_new() (from under-
laying libssl), which has proper fallbacks to older protocols (like
TLSv1 or even SSLv3).


   Programming: +info +check

# TODO: ... 
All other commands, beside +cipher use Net::SSLeay to connect to the
server. However, Net::SSLeay uses IO::Socket:SSL underneath.
In this case, the connection must succeed. We encounter the same
problems as described for +cipher, in particular the server expects
modern protocols (TLS instead of SSL) and SNI.
Hence we try to connect using the most modern protocol first (which is
CTX_tlsv1_2_new() and TLSv1_2_method() in 2017). If the connection fails,
the next method is used, until the connection is established or failed
at all. This logic is implemented in Net::SSLinfo's do_ssl_open().

    Testing

# TODO: ... 
	Documentation about testing can be found in the section  TESTING  in
          OSaft/Doc/help.txt
        This should be moved to this document and then referenced in there.

    Make for development

# TODO: ... explain make system and targets ...

    Portability

        As decribed above in "Ancient systems", the tools should work on many
        systems.  So we use traditional interpreters fpr the core tools only,
        these are: perl sh awk sed .

        Further development most likely continues on modern systems. On these
        modern and more sophisticated tools can be expected, like GNU Make or
        GNU awk (gawk).
        It's assumed that further development continues on modern systems and

        The scripts use hardcoded path in their shebang line, for a detailled
        discussion see "Using env" below.

    Using env

        From the perspective of an end-user, using  /usr/bin/env  seems to be
        more convenient than hardcoded paths.  The advantages are: it is most
        likely more portable and it respects the user's  environment variable
        $PATH . The disadvantages are:  /usr/bin/env  doesn't allow arguments
        passed to the specified program (exception see below) and it may have
        a security impact, because it is not obvious which program is finally
        executed.

        Also, some scripts use the program with arguments. When  /usr/bin/env
        is used instead, the user must call the scripts with these arguments,
        which violates our goal to provide a simple to use tool.
        Well, some env can be used to pass arguments, examples:
            /usr/bin/env sh -c '\prog -opt arg'
            /usr/bin/env sh -c 'exec "$@"' sh -opt arg
        This, a bit cumbersome approach, will not be implemented here.

        GNU's  /usr/bin/env  supports the  -S/--split-string  option  to pass
        such arguments to the program, example:
            /usr/bin/env -S prog -opt arg

        Using the old-school hardcoded path is simple and is known to work on
        all platforms, while detecting the version of /usr/bin/env and how it
        behaves, is hard work and not guaranteed to do the expected job.
        
        However, in some scripts /usr/bin/env is used, but in the most simple
        way only.

        That's why the  hardcoded path  for the program is used by default.
        The installation script  INSTALL.sh  will provide options  to replace
        it with  /usr/bin/env .

        For more details on shebang and env, please read (last seen 11/2022):
            https://www.in-ulm.de/~mascheck/various/shebang/


STATUS CODE

        Why does O-Saft not support a  --fail  option?

# TODO:
Long answer: 

Background: one of O-Saft's concepts is to "show important information" and to
inform about the performed checks based on these informations. The check results
are basically "yes" or "no". It leaves the interpretation, if the result is
"good" or "bad", to the user.  This means that all reported results are
unbiased.
Said this, it's obvious that o-saft.pl cannot return any other status beside the
printed results (which *are* the status).
The idea is, that postprocessors should be used to use the results for scoring
or alike. This is another concept, and o-saft.pl is prepared for the ease use of
prostprocessors.

As it is common sense that a status code other then 0 indicates that something
is wrong, such a status code would violate this concept as it is biased.
A status code other than 0 would also make using o-saft.pl in a pipe much more
difficult.

Now, I see the practical use in a pipe or process chain, which simplifies the
handling of the results, sometimes. 
So, the requirement makes sense, but throws a couple of questions to be
answered. The requirement, as described in this issue, is to return a status
code other than 0, if any check reports "no". Could be done (is done 11oct2016:),
but is not the complete truth.
What about the other checks
  * like the sizes?
  * the DNS checks?
  * are medium ciphers subject for fail?
  * What about incomplete checks because the server did not return proper data
    (i.e. DH parameters)?
  * What about failed checks, which could be considered unimportant in the
    tested environment (i.e. some STS or compliance checks)?

These are the same questions as for scoring the results (like other tools do).
It's a matter of personal opinion and/or the attributes of the tested
environment, if "something" should be considered "good" or "bad".
However, if a user is aware of such problems, she can configure O-Saft to do
only the relevant checks. Other tools cannot do this, and hence score
everything, wether important, useful, practical, ... or not.
Please see next comment for a practical example.

How to deal with these questions?
I.g. the "--fail" functionality needs to be configurable by the user.
Best example why this is necessary, is the +cipher command. Is any supported
cipher other than HIGH subject for fail? Should MEDIUM ciphers not fail? Or ask
your marketing people what they think about disabling DES-CBC3-SHA (which is
weak now), just to pass the test?

My opinion is, that a configurable --fail in conjunction with the existing
configuration of the checks to be performed, can do the trick.

Conclusion: there is no -and will not be- a general --fail option. Instead, we
provide some options if various kinds of checks should be subject for an exit
code. 
With this concepts, it's up to the user to configure O-Saft to return a proper
exit code. This should not be difficult for experianced users with special
requirements ;-)
For now, the option --exitcode does the trick, there are also some more
options  --exitcode-*  for fine tuning. Please see  `o-saft.pl --help`.

Here we go, this desciption is subject for discussion. Please provide you
opinions.


== Examples for  --exitcode ==

Here is a practical example why a general "--fail" would not return the
expected results.

Using  `o-saft.pl mysite.org --exitcode +vulns` would return results
like shown in the very first comment above.
Looking at these results, we see 6 "no" checks: BEAST, BREACH, DROWN,
Lucky13, Sweet32 and SSLv2. My opinion is -please correct me if wrong-
that only 2 checks should count for the exit code: Lucky13 and Sweet32.

Currently, having the concepts (see previous comment) in mind, the
solution to filter the improper checks would be to use prostprocessor:

    o-saft.pl mysite.org --exitcode +vulns \
	|grep -v '(<<'|egrep -q 'no( |$)' || echo fail

o-saft.pl could be configured to do the proper tests only. There are at least
3 ways to do it:

1. using proper commands only:

    o-saft.pl mysite.org --exitcode +crime +freak +heartbleed +logjam +lucky13 \
	+poodle +rc4 +sloth +sweet32 +time +hassslv3 +pfs_cipher +session_random

2. using .o-saft.pl (the traditional way):

cat > .o-saft.pl <<EoT
--host=mysite.org
--exitcode
+crime
+freak
+heartbleed
+logjam
+lucky13
+poodle
+rc4
+sloth
+sweet32
+time
+hassslv3
+pfs_cipher
+session_random
EoT

   o-saft.pl

3. using .o-saft.pl (advanced usage):

cat > .o-saft.pl <<EoT
--host=mysite.org
--exitcode
+heureca
### define new command +heureca , all following must be in one line
--cfg_cmd=heureca=crime freak heartbleed logjam lucky13 poodle rc4 sloth sweet32 time hassslv3 pfs_cipher session_random
EoT

   o-saft.pl

Note that creating .o-saft.pl must be done once in the directory where
o-saft.pl will be started.

Hope this helps to understand how O-Saft handles various situation and
how to configure it for very spezial purposes.


GLOSSAR

        This glossar explains the words and terms used herein this document.

# TODO: ... 
        developer
        user
        program and tool are used interchangeable
        debug
        trace
        verbose
        result
        code
        command
        option

