May 14, 2020

systemd-run + fish shell = poor man's jail for untrusted processes

One of the well-documented effects of Coronavirus is the move to video conferencing from home. This means more Zoom, Google Meet/Hangouts, Jitsi, etc. - programs I don't necessarily trust to have full access to my home directory. Using the fish shell and systemd-run, I made the following utility to ensure certain processes don't see my $HOME directory while still having the convenience of running them as myself without too much hassle. You can find the updated source here:

#! /usr/bin/env fish

################################################################################
# Poor-man's "jail" for untrusted processes, or at least untrusted to read/write
# to $HOME. Uses systemd-run to run an untrusted command such that $HOME is
# mapped to $HOME/.local/share/jail/<program name> instead, thus ensuring that
# program can't read/write your real $HOME directory.
#
# Usage: make sure this file is executable, then from somewhere in your $PATH
#        but before the actual path to the program, symlink names of the
#        programs you want to "jail" to this file.
#
# Example for Google Chrome, assuming $HOME/bin is in your PATH before /usr/bin:
#
#   $ cd $HOME/bin
#   $ ln -s <path to this file> google-chrome-stable
#   $ google-chrome-stable # Runs Google Chrome with $HOME really set to
#                            $HOME/.local/share/jail/google-chrome-stable
#
################################################################################

# Default arguments to systemd-run
set systemd_run_args --uid=(id -u) --gid=(id -g) --wait

# Connect stdin if available
isatty
  and set -a systemd_run_args --pty

# Loop through all exported variables for use with systemd-run -E
for var in (set -xn)
    test (count $$var) -gt 1
      # Array/PATH variable, flatten and join with :
      and set -a systemd_run_args -E $var=(string join : $$var)
      # Single value variable
      or set -a systemd_run_args -E $var=$$var
end

set program_to_run (basename (status filename))
set path_to_me (dirname (status filename))

# Find the real executable to run
for dir in $PATH
    test $dir = $path_to_me
      and continue

    test -x $dir/$program_to_run
      and set exe $dir/$program_to_run
      and break
end

set -q exe
  or begin
       echo Unable to find $program_to_run in $PATH
       exit 1
     end

# Create our jail if it doesn't exist
set jail $HOME/.local/share/jail/$program_to_run
test -d $jail
  or mkdir -p $jail

test -d $jail
  or begin
       echo Unable to create/use jail at $jail
       exit 1
     end

set -a systemd_run_args -p BindPaths=$jail:$HOME

# If we are below $HOME when invoked use $HOME as the working directory,
# otherwise use the directory where we currently are
if string match --regex "$HOME/?" (pwd -P) >/dev/null
  set -a systemd_run_args --working-directory=$HOME
else
  set -a systemd_run_args --same-dir
end

# Must be root to bind-mount, use sudo if necessary
if test (id -u) -ne 0
  exec sudo systemd-run $systemd_run_args $exe $argv
else
  exec systemd-run $systemd_run_args $exe $argv
end

February 13, 2011

ANTLR or Scala?

I like ANTLR and wanted to make a nice clean CSV grammar, as I didn't like the one on the ANTLR wiki (eats leading/trailing field white space, no good per RFC4180) and didn't see any others via Google. So I did, it's here.

As I was about to hack it up into something usable as a library I happened to be perusing some Scala docs and was reminded about Scala's built-in combinators. I gave it a shot even though there are others out there, though the existing docs/examples are pretty rough. I have to say after some effort it's pretty clean and far shorter than my ANTLR grammar at 23 lines (if you don't count the "parseCSV" convenience methods):
import scala.util.parsing.combinator.RegexParsers

trait CSVParser extends RegexParsers {

    import scala.util.parsing.input.CharSequenceReader
    import scala.util.parsing.input.StreamReader
    import java.io._

    override def skipWhitespace = false

    protected def records = repsep(record, """\r?\n|\r""".r)

    protected def record: Parser[List[String]] = repsep(field, ",".r)

    protected def field: Parser[String] = quoted_field | unquoted_field

    protected def quoted_field: Parser[String] = """"(""|[^"])*"""".r ^^ {
        s: String => s.substring(1, s.length()-1).replaceAll("\"\"", "\"")
    }

    protected def unquoted_field: Parser[String] = """[^,"\r\n]*""".r

    /**
     * Returns a list of CSV records, each a list of strings. If there were no records found or there was an error, None is returned
     */
    def parseCSV(reader: scala.util.parsing.input.Reader[Elem]): Option[List[List[String]]] = {
        parseAll(records, reader) match {
            case s: Success[List[List[String]]] => Some(s.result)
            case _ => None
        }
    }

    def parseCSV(input: CharSequence): Option[List[List[String]]] = parseCSV(new CharSequenceReader(input))

    def parseCSV(input: InputStream): Option[List[List[String]]] = parseCSV(StreamReader(new InputStreamReader(input)))

    def parseCSV(reader: Reader): Option[List[List[String]]] = parseCSV(StreamReader(reader))

    def parseCSV(file: File): Option[List[List[String]]] = {
        val fr = new java.io.FileReader(file)
        try {
            parseCSV(fr)
        } finally {
            fr.close
        }
    }

}
Even with convenience wrapper methods, coming in at 48 lines to get to something readily usable is pretty impressive. I really do like ANTLR, but at this point I can't justify the effort of hacking up my grammar to get to something usable (in java).

Scala 1, ANTLR 0.

April 16, 2009

Patch for Yaws 1.81 -- Hide your webserver identity

This is a patch for making attackers think you are running Windows and IIS/7.0, when in fact you are running *nix and Yaws. It also includes some mime types for XML/XSLT and .crt files.


diff -ur ./src/mime.types ../yaws-1.81/src/mime.types
--- ./src/mime.types 2009-04-16 12:02:43.000000000 -0700
+++ ../yaws-1.81/src/mime.types 2009-04-16 12:23:35.000000000 -0700
@@ -203,7 +203,7 @@
application/vnd.motorola.flexsuite.kmr
application/vnd.motorola.flexsuite.ttc
application/vnd.motorola.flexsuite.wem
-application/vnd.mozilla.xul+xml
+application/vnd.mozilla.xul+xml xul
application/vnd.ms-artgalry
application/vnd.ms-asf
application/vnd.ms-excel xls
@@ -331,9 +331,10 @@
application/x-ustar ustar
application/x-wais-source src
application/x400-bp
-application/xml
+application/xml xml xsl
application/xml-dtd
application/xml-external-parsed-entity
+application/xslt+xml xslt
application/zip zip
audio/32kadpcm
audio/basic au snd
@@ -465,7 +466,7 @@
text/vnd.wap.wml wml
text/vnd.wap.wmlscript wmls
text/x-setext etx
-text/xml xml xsl
+text/xml
text/xml-external-parsed-entity
video/mp4v-es
video/mpeg mpeg mpg mpe
@@ -490,3 +491,4 @@
application/xhtml+xml xhtml
image/svg+xml svg
application/ogg ogg
+application/x-x509-ca-cert crt pem
diff -ur ./src/yaws.erl ../yaws-1.81/src/yaws.erl
--- ./src/yaws.erl 2009-04-16 12:02:43.000000000 -0700
+++ ../yaws-1.81/src/yaws.erl 2009-04-16 12:47:42.000000000 -0700
@@ -577,13 +577,7 @@
end).


-address() ->
- ?F("
~s Server at ~s
",
- [
- (get(gc))#gconf.yaws,
- (get(sc))#sconf.servername]).
-
-
+address() -> "".

is_space($\s) ->
true;
@@ -699,7 +693,7 @@


printversion() ->
- io:format("Yaws ~s~n", [yaws_generated:version()]),
+ io:format("Microsoft-IIS/7.0~n"),
init:stop().

%% our default arg rewriteer does's of cource nothing
@@ -1284,13 +1278,7 @@
"\r\n"]
end.
make_server_header() ->
- HasDav = ?sc_has_dav(get(sc)),
- ["Server: Yaws/", yaws_generated:version(), " Yet Another Web Server\r\n" |
- if HasDav == true ->
- ["DAV: 1\r\n"];
- true ->
- []
- end].
+ ["Server: Microsoft-IIS/", "7.0", "\r\n"].

make_last_modified_header(FI) ->
N = element(2, now()),

March 28, 2009

plist to JSON (including your iTunes library...)

This is a quick-and-dirty sed script to convert a plist XML file to a no-unnecessary-whitespace JSON format. It has a few caveats, however:

  • It uses extended regular expressions

  • It slurps the entire XML text into memory before spitting it back out; it does not perform the traditional line-by-line editing/printing as most sed scripts do

  • <data> elements are given empty string values


Because of the above conditions, you have to use the "-En" options when running sed otherwise you will get garbage output (on Mac OS X, at least). Here is the code, I saved it in a file called "plist-to-json.sed" and invoke it as "sed -Enf plist-to-json.sed ...":

# Kill newlines
/^[[:space:]]*$/ d

# Kill leading whitespace
s|^[[:space:]]*||g

# Kill trailing whitespace
s|[[:space:]]*$||g

# Kill any base64 lines, <data> elements will not be converted
/^[[:alnum:]]+=*$/ d

# Kill the <?xml...?> line
s|<\?[^>]*>||g

# Kill any XML processing instructions
s|<![^>]*>||g

# String escape any values for JSON
s|"|\\"|g

# Convert the top level <plist> element to an object with a "plist" field
s|<plist[^>]*>|{"plist":|
s|</plist>|}|

# Keys, strings and dates get surrounded with quotes, numbers and booleans left alone
s|<key>|"|g
s|</key>|":|g
s|<string>|"|g
s|</string>|",|g
s|<real>||g
s|</real>|,|g
s|<integer>||g
s|</integer>|,|g
s|<true[[:space:]]*/>|true,|g
s|<false[[:space:]]*/>|false,|g
s|<date>|"|g
s|</date>|",|g

# Arrays and dictionaries convert nicely
s|<array>|[|g
s|</array>|],|g
s|<dict>|{|g
s|</dict>|},|g

# Give <data> elements an empty value
s|<data>|""|g
s|</data>|,|g

# Append the pattern space into the hold space
H

# Everything here happens only on the last line of input, after the above stuff has run
${
# Bring the hold space into the pattern space (the entire document)
g

# Remove tailing commas from the last field definitions in a JSON object
s|,[[:space:]]*}|}|g
s|,[[:space:]]*]|]|g

# Kill remaining unnecessary whitespace
s|:[[:space:]]*|:|g
s|{[[:space:]]*|{|g
s|\[[[:space:]]*|[|g
s|\n||g

# Print out the resulting JSON
p
}


Using this I was able to take my "iTunes Music Library.xml" file (2,365,981 bytes) and convert it nicely to JSON (1,087,270 bytes), which I intend to use in a Flash/Flex application I want to build to listen to my iTunes music over the internet. Unfortunately, Mediamaster (a company I used to work for) is not going to exist anymore and I miss the service... this will be my pathetic attempt at a replacement as I have absolutely no UI skills at all.

September 25, 2008

Simple Revision Control

Many people (including some of my family members) do not use revision control or even know what it is. They have lots of files with funny extensions on their computers (doc1.doc, doc2.doc, doc3.doc) that are different versions of the same document. Sometimes there will be many folders or a combination of files and folders; all in the name of being able to go back in time to see what something used to look like, so that information is permanently lost when you overwrite it.

I think that's terrible and I'm trying to convince my family members to use revision control since it solves this problem nicely, thus the reason for this post. It it very simplified, but should be more than adequate for most computer users to help keep track of changes to their files.



Download and install this: http://tortoisesvn.net/downloads. It is a Windows "extension" -- meaning when you right-click on files and folders you will have a new menu to choose from after you reboot after installation. The program is called "TortoiseSVN" -- it's free and (I think) very usable and convenient.

Here are the basics of revision control:
  1. Everything you want to keep track of is centrally stored in a repository
  2. When you want to make changes to something in a repository, you do a check-out into a folder on your computer
  3. The folder that you checked out in step 2 is called a working copy
  4. When you make changes and want to save them, you commit your changes to the repository, when you do so you provide a comment stating what your change was. This comment is recorded in the log for that file or directory.
  5. You can browse a repository, and for any file or folder inside it you can see its full history of commits (changes), as well as each log message.
  6. In addition to seeing a full history of changes, you can also see the contents of a file/directory at any point in time, so if you decide you don't like a change or need to find something you wrote a long time ago, you can find it.
This tool "TortoiseSVN" enhances the regular Windows Explorer to give you the functionality, using the "Subversion" software ("SVN" for short). It's what many companies and independent projects use to track changes to their work. And while organizations and groups have advanced usage of it, what I described here can be used by anyone with a computer who makes changes to their files over time; spreadsheets, documents, pictures, financial files... anything you might change over time.

To set it up:
  1. Create a repository. Make a new folder somewhere (perhaps C:\repo or something obvious), right-click on it and from the TortoiseSVN menu choose "Create repository here"
  2. Create a working copy. Go make a new directory under "My Documents" (or someplace else you will remember) -- this is a directory where you will be loading/saving your documents from. Right-click on it and choose "SVN Checkout," then for the "URL of repository" put in "file:///C:/repo", then choose "Ok". If you chose something other than "C:\repo", just put "file:///C:/" to start with, then browse to it using the box to the right.
  3. Collect your files you want to keep track of. If you have them, copy your existing documents into your working copy folder. You can put anything you want in there including files, folders (and sub-folders), whatever.
  4. Add your existing files to SVN. SVN wants to be told explicitly what to worry about and what not to, so if you see icons with a question mark by them (as you now should), you can right click on them and choose "Add" from the menu. The icons should change to plus signs at that point -- they are poised to be controled by SVN now.
  5. Commit your new files/folders. Once you have placed your existing documents in your working copy, right-click on the working copy folder and choose "SVN Commit..." from the menu. Enter a comment and hit "Ok" -- you just made your first revision!
To use it day to day:
  1. Stay up to date. At any time you can right-click on your working copy and choose "SVN Update" from the menu to make sure everything is in order.
  2. Edit your files. Make whatever changes you want to your files, add new files or folders, etc. Make sure when you are done that you explicitly add new files -- they will have a question mark icon next to them so they should be easy to identify.
  3. Commit your changes. When you reach what you think is a good point in time to save your work, commit your changes like you did above. Perhaps you added an important idea (even if it isn't finished yet), updated your address book with a new contact, updated your resume, whatever makes sense for you.
  4. Go to step 1.
That's it! At any point you can right-click on your working copy, choose "Show log" from the TortoiseSVN menu, and instantly see a complete trail of all your commits including what files were changed/modified/removed, when you did it, and what comment you wrote when you committed each change.

Hopefully that is enough to get everyone going, and hopefully it is intuitive enough from this point how to use TortoiseSVN to do what you want. If you need more information please leave a comment here, or you can see the TortoiseSVN website at http://tortoisesvn.net/ or the Subversion book online at http://svnbook.red-bean.com/en/1.5/index.html.

September 16, 2008

Venn diagrams in PIC



For the true nerds: some simple PIC macros for creating Venn diagrams (of the 1, 2 or 3 set variety):
define vennDiagram1 {
    box height $1 width $2
    $3 at last box.nw - (0.1, 0.0)
    "U" at last box.ne - (0.1, 0.1)
    circle radius last box.height/4 at last box.c
    $4 at last circle.nw + (-0.1, 0.1)
    move to last box.e
}

define vennDiagram2 {
    box height $1 width $2
    $3 at last box.nw - (0.1, 0.0)
    "U" at last box.ne - (0.1, 0.1)
    circle radius last box.height/4 at last box.c - (last box.height/8, 0)
    $4 at last circle.nw + (-0.1, 0.1)
    circle radius last box.height/4 at last box.c + (last box.height/8, 0)
    $5 at last circle.ne + (0.1, 0.1)
    move to last box.e
}

define vennDiagram3 {
    box height $1 width $2
    $3 at last box.nw - (0.1, 0.0)
    "U" at last box.ne - (0.1, 0.1)
    circle radius last box.height/4 at last box.c - (last box.height/7, -last box.height/10)
    $4 at last circle.nw + (-0.1, 0.1)
    circle radius last box.height/4 at last box.c + (last box.height/7, last box.height/10)
    $5 at last circle.ne + (0.1, 0.1)
    circle radius last box.height/4 at last box.c - (0, last box.height/7)
    $6 at last circle.sw - (0.1, 0.1)
    move to last box.e
} 

April 11, 2008

SASL2

I grew annoyed at not knowing whether or not my SASL2 plugins were as "installed" as I thought they were and had permissions correct enough to work, so I created this lousy program to print them out. As it turns out you can build the NTLM mechanism from the cyrus sources, plop it into /usr/lib/sasl2 on a Mac and it will in fact work.


#include <stdio.h>
#include <stdlib.h>
#include <sasl/sasl.h>

int main() {
if (SASL_OK == sasl_client_init(NULL)) {
const char ** list = sasl_global_listmech();
if (list != NULL) {
fprintf(stdout, "Available SASL mechs:\n");
int pos;
for (pos = 0; list[pos] != NULL; pos += 1) {
fprintf(stdout, "\t%s\n", list[pos]);
}
return EXIT_SUCCESS;
} else {
fprintf(stderr, "No mechanisms available!\n");
return EXIT_FAILURE;
}
} else {
fprintf(stderr, "SASL init failed\n");
return EXIT_FAILURE;
}
}