September 10th, 2016 Meeting

Controlling the World from a Web Browser

A presentation on how to use the Raspberry Pi (and a web browser) to control the world (or just about anything in it).

9:30 am
Victoria Computer Club
85A Burnside Rd West (at Wascana), Victoria, BC


Control from the web

Ahh. I see the light.I have shamelessly borrowed this from one of my Camosun labs.Dark in here isn't it.



Objectives

  • Control the Pi from the Web

Example

onoff.html

Concepts

  • Web Forms https://en.wikipedia.org/wiki/Form_%28HTML%29
    • Web forms allow us to enter values to be passed to a server
      and from there to a program that the web server
      executes.  See cgi-bin below. 
    • This ability to collect input is crucial to much that we do
      on the web.  In fact most of what we see today of the
      dynamic web, cloud services, on line collaboration etc. would,
      obviously, not work if we could not input data. 
      Unfortunately there is a down side.  As humans we make
      mistakes, and sometimes we “make mistakes” on purpose in order
      to break, compromise or crack something.  Like your web
      server.

    • Validation
      So, as soon as we start writing programs that accept input
      from the “Web” we need to ensure that the input we get is what
      we expect.  And, if not we need to not use the input and
      either complain or ignore it.

      Given the “Sand Box” environment we are working in, the danger
      is not too high that someone malicious will try to crack our
      cgi-bin scripts, but we need to get into the habit of always
      sanitizing our inputs.  Besides, we, as unlikely as this
      may seem, might make a misteak.

  • Web Servers https://en.wikipedia.org/wiki/Apache_HTTP_ServerWhich falls
        faster apache or IIS?
    • The web server is the server side partner to your web
      browser.  When a web page is requested the server finds
      the web page on its system, probably disk,  and sends it
      back to the requesting browser.
    • Directories of interest
      • /var/www – contains .html files to be served.
      • /usr/lib/cgi-bin  – contains programs to be executed.
  • Common Gateway Interface (CGI) https://en.wikipedia.org/wiki/Common_Gateway_Interface
    • The CGI interface is a way to tell the web server that we
      want it to do more than grab the file from disk and send it to
      us.  We want it to run the file as a program and send the
      results of that program running to us.  The program that
      runs can be written in any language we choose.  The only
      rules are:
      • That the program is in the “cgi-bin” directory.  On
        our Pi this is /usr/lib/cgi-bin/
      • That when the program runs it sends the appropriate header
        telling the web server and browser what the content type
        that it is creating is.  This is a line that looks
        like:

        Content-Type:

        text/html\n\n

      • Assuming your output is html text. 
      • The content type could be text/plain or image/png etc.
      •  The \n\n is two new line characters, thus creating a
        blank line.  The blank line is the indication to the
        Server/Browser that the HTTP is done and the (HTML) 
        content will now start.  You would create this header
        in Python with

        print
        “Content-Type: text/html\n\n”

  • Python – collecting form variables
    • Form variables can be GET or POST.  We will use
      POST.  Post gets to the server via standard in (stdin).
    • Stdin  https://en.wikipedia.org/wiki/Standard_streamsInput output and error is standard. The rest
          optionally available.
      • A running program in Linux has stdin, stdout and stderr.
      • In the browser/server system stdin is input from the
        network, stdout is output to the network and stderr is
        output to the network.
      • The following is Python code that will pick up the post
        variables that the web browser form submits and put them
        into the dictionary POST{}:

        # Build dictionary of post inputs.
        import sys

        POST = {}
        # Split each input parameter on ampersand.
        args = sys.stdin.read().split('&')

Or, your life will become much more complex. See
    Velociraptor.

        for arg in args:
            # split the name value pairs and add
        them to the POST dictionary
            inp = arg.split('=')
            POST[inp[0]] = inp[1]
    • Check validity  https://en.wikipedia.org/wiki/Data_validation    

      For the On/Off form:

      • Input should be on or off.  Anything else, including
        nothing, should be ignored or an error.
      • We can do this with a series of if/elif/else statements.
  • Executing as Root – Set UID  https://en.wikipedia.org/wiki/Setuid
    • We have two problems if we want to use the GPIO functions
      from a cgi-bin script:
      • GPIO functions must run as root.  We normally do that
        by running the programs like “sudo ./myprog.py” and this
        works.  But from the web – not so much.
        • We could give the Apache2 user (www-data) sudo
          privileges.  But, do we really want to allow the
          Apache user and thus anyone connecting via the web to our
          machine, complete access to run anything as root. 
          This means that if we open our Pi server to to outside
          world then that same outside world could run stuff as
          root.  Not a good thing.I tried that once.
              It worked once... and only once!
        • Normally when we run a program or a script, it runs with
          the privileges of the person who runs it, not the
          privileges of the owner.

          We can set our script to what is called suid.  And
          when the script is run it will run with the privileges of
          the owner of the file, not the “runner”.   An
          example is the Linux passwd command.  This allows a
          plain user to change a value in a root read only
          file.  That value is your password.

          -rwsr-xr-x. 1
          root root 30768 Feb 22  2012 /usr/bin/passwd

          And, the passwd program is very carefully written to
          ensure that you may only change your password.  Not
          someone else’s.

        • But there is another problem.  Scripts can have
          security issues in Linux in the way they are run, see next
          point.  So in general Linux will refuse to run any
          script in setuid mode.  In order to solve this
          problem we can use a compiled C wrapper.  The
          compiled wrapper in turn runs the script.
    • C Wrapper
        • Why – security issues.  From the Perl Security man
          page (man perlsec)

                      


          Beyond the obvious problems that stem from giving special
          privileges to
                 systems as
          flexible as scripts, on many versions of Unix, set-id
                 scripts are
          inherently insecure right from the start.  The
          problem is a
                 race condition in
          the kernel.  Between the time the kernel opens
          the
                 file to see which
          interpreter to run and when the (now-set-id)
                 interpreter turns
          around and reopens the file to interpret it, the file
                 in question may
          have changed, especially if you have symbolic links on
                 your system.

        • How – Build a C Wrapper
          • The C wrapper is compiled so it does not have the
            security issues that a plain script has.  Still it
            is running a plain script and we must ensure that the
            script is secure, inputs are checked and that it can
            only do what we intended it to do.  The C wrapper:

                  

            #define REAL_PATH “/path/to/script”
                   main(ac, av)
                      

            char **av;
                   {
                      

            execv(REAL_PATH, av);
                   }

          • ac is the number of arguments passed to the C program
            and av is a pointer to an array of arguments. The O/S
            handles setting this up so that you have the arguments
            available that were passed to the script.
          • The first line is not I repeat NOT a
            comment.  This is C, not Python. It is a
            declaration of a constant. You need to change this to
            the fully qualified path and name of the script you want
            to run, and this is the only thing you need to change in
            this program. E.g. /usr/lib/cgi-bin/myscript.py
          • This is C so you need to compile it. gcc

            mywrapper.c -o mywrapper will compile the C
            source file mywrapper.c into the executable file
            mywrapper.  Feel free to change the names to names
            that make sense in this context.   In fact –
            please do that.

            By convention C executables on Linux systems do not have
            an extension.

            Don’t mess the naming up. The compiler will happily
            compile your source file to an executable of the same
            name E.g. gcc -o foo.c foo.c will overwrite your source
            with an executable incorrectly named.

          • Both the compiled C program and the script need to be
            executable and the script needs a Shebang.
          • Change the owner of the compiled C program to root
            before you use chmod to set the setuid bit.
            • chown
              root:root mywrapper
            • chmod
              4755 mywrapper
    • Log Files of interest:
      • /var/log/apache2/error_log – Error logs.
      • /var/log/apache2/access_log – Logs of pages served.
    • .htaccess   If time permits – probably next time.
    • You may be tempted to open up your Pi to the World. 
      Don’t!  Not yet.
      • Security issues to opening Pi to the world.
          • ssh
          • password
          • sudo
          • iptables
          • vnc

Code

Web form – this sends the request, on or off

<html>
<head>
<title>Turn something on or off</title>
</head>
<body>
<h1>Turn Something On and Off</h1>
<form action=”/cgi-bin/onoff” method=”post”>
ON: &nbsp;<input name=”onoff” value=”On” type=”radio”
><br>
OFF: <input name=”onoff” value=”Off” type=”radio”
><br>
<input type=”submit”>
</form>
</body>
</html>

Wrapper – this allows the suid cgi script to be executed securely

#define REAL_PATH “/usr/lib/cgi-bin/onoff.py”
      main(ac, av)
          char **av;
      {
         
execv(REAL_PATH, av);
      }

python back end – this does the work

#! /usr/bin/python

# Import the Python gpio library and the system library
import RPi.GPIO as GPIO
import sys

print “Content-type: text/html\n\n”
print “<html><head><title>This is a
switch</title></head”

# Build dictionary of post inputs.
POST = {}
args = sys.stdin.read().split(‘&’)
for arg in args:
        t = arg.split(‘=’)
        POST[t[0]] = t[1]

# Get the input value from the POST[] array
inp = POST[‘onoff’]

# Determine on or off and which message to display.
if inp == “On”:
  print “<h1>Turned On</h1>”
  onOff = 1
elif inp == “Off”:
  print “<h1>Turned Off</h1>”
  onOff = 0
else:
  print “How do I do that?”

# Turn off warnings
GPIO.setwarnings(False)

# This is the gpio pin I have the led connected to
pin =  5

#  Set the pin number mode to Broadcom rather than connector
order
GPIO.setmode(GPIO.BCM)

# Set the choosen pin to be an output and set the ouput true, on.
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, onOff)

# Display the state of the pin.
print GPIO.input(pin);


References


Quiz

  • What is the purpose of web forms?
  • How important are web forms to our experience of the web?
  • Discuss why and how to validate input data.
  • What is a web server and which web server are we using on the
    Pi?
  • /var/www  and /usr/lib/cgi-bin  are
    directories  on the Pi.  What are each of the
    directories used for?
  • What is the purpose of the CGI?
  • What http line do we have to output before we output html?
  • Where do we get the submitted POST variables from?
  • What is set uid?  Why do we use it.
  • What do we have be extremely careful with when we create suid
    programs?
  • What is the purpose of the C wrapper?
  • Why do we have to compile the C wrapper program?
  • List two Apache log files, the directories they will be found
    in and what will be found in them.
  • Why do we care about the log files?
  • Opening up the Pi to the outside world.  Why should we
    not do this yet?
  • What is the purpose of .htaccess?
  • What files do we need to create to make .htaccess work and
    what file do we need to modify?

Leave a Reply