
###############################################################################
##                                                                           ##
##  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: 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 trace (and debug) messages
          * '**' prefix for INFO, WARNING and ERROR messages


TERMINOLGY

        For better readability of the descriptions herein, texts used/written
        by the tools literally are flagged with single quotes, like: 'N/A' or
        '+'. However, options and commands for the tools are written in plain
        text without quotes, as they are no natural lanuauage, usually.


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: 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 a tool is a wrapper and passes its arguments to other tools,  some
        arguments are valid for both, the wrapper tool and the called tool.
        For example   --help  for printing user's help. Examples:
          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 is most likely 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 '**INFO'.

        Except for hunting special problems with a target,  trace options are
        most likely for developers. These options are named  --trace* . Their
        messages are 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 tracing or debugging start with  '#'.
          * Errors, warnings and information 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: doc/help.txt  OUTPUT

        It is also often a matter of opinion and taste  how results should be
        presented. Most of our tools strictly follow the rule:
          * one check result in one line of output.
        To 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: 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 start with '**ERROR:' followed by a unique 3-digit number.

        Warnings start with '**WARNING:' followed by a unique 3-digit number.

        > See also: 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)
          * Programming (code) style
          * Handling erros and exceptions
          * Debugging code
          * Programming: arguments in general
          * Programming: +ciphers
          * Programming: +info +check
          * Testing
          * Make for development
          * Portability
          * Using env

    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.

        For details about  documentations and  how it is organised, following
        descriptions are recommended:

          doc/coding.txt
          perldoc o-saft.pl
          perldoc t/Makefile.pod

        All files used for documentation, wether source files, or contributed
        files, or generated files, are located in the  doc/  directory.

    Programming (code) style

        For programming style, and/or program code in general, please see:
          doc/coding.txt

    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  and it is not intended to 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 the commands and
        options (as decribed above).
        As a lot of spelling variants and aliases are supported, this results
        currently (2024 ..) 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) 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.

        Note that modern openssl is not useful to detect all ciphers!

# 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

        Documentation about testing can be found in the section  TESTING  in

        > See: doc/help.txt

# TODO: ... 
        This should be moved to this document and then referenced in there.

    Make for development

       Anything about building and maintaining the project, its code and its
       documentation is done in Makefiles.  The project's Makefile is in the
       project directory itself. It also serves common used/expected targets
       like 'install' or 'clean'.  All other targets for development, can be
       found in the  ./t  directory. There exist numerous Makfiles.

       > Start reading t/Makefile.pod , you may use: perldoc t/Makefile.pod

    Portability

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

        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).

        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
        It's not an option here, because GNU tools are not available always.

        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 12/2022):
            https://www.in-ulm.de/~mascheck/various/shebang/


STATUS CODE

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

        Short answer: 

        The option  --exitcode  does the trick. For details, please see:
          o-saft.pl --help

        Long answer: 

        For background please see section "Output and results" above, here in
        short some concepts again:
          * show important information
          * inform about the performed checks based on these informations
          * check results are basically 'yes' or 'no'
          * leaves the proper interpretation, if "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.

        On the other hand, using pipes or process chains  are of practical as
        they can simplify handling of results, sometimes.

        So, the requirement makes sense,  but throws a couple of questions to
        be answered. The requirement is to return a status code other than 0,
        if any check reports 'no'. Could be done (is done since 11-oct-2016),
        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 target 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 example 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 users are aware of such problems,  they can configure our
        tools to do only the relevant checks. Other tools cannot do this, and
        hence score everything, wether important, useful, practical, whatever
        ... 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.
        The  +cipher  command is a good example why this is necessary. 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?

        The author's opinions are 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, options are provided to control how  various kinds of checks
        should be subject for an exit code. 
        With this concepts, it's up to the user to configure when to return a
        proper exit code.  This should not be difficult for experianced users
        with special requirements ;-)

        The option are  --exitcode  and for fine tuning  --exitcode-*. Please
        see:
           o-saft.pl --help=OPTIONS

        This desciption for "--fail functionality" is subject for discussion.
        Please provide your opinions.


# TODO: move following to doc/help.txt

== Examples for  --exitcode ==

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

        Using something like:

          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. Most likely only 2 of these checks should
        count for the exit code: Lucky13 and Sweet32.

        Please correct me if wrong, participate on the discussion.

        Currently, having  docs/concepts.txt  in mind, the solution to filter
        the improper checks would be to use a prostprocessor, for example:

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

        Using configurations, there are at least three other 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 the traditional way:

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

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

          cat > .o-saft.pl <<EoConf && o-saft.pl
            --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
EoConf

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

        Hope this helps to understand how configuration can be used to handle
        very spezial purposes.


GLOSSAR

        This glossar explains the words, terms used herein in this document.

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


VERSION
        @(#) concepts.txt 1.6 24/05/26 13:26:42


AUTHOR
        12-feb-2021 Achim Hoffmann

