commit b52b934adf0622975d5a61dcf938e4f80ce1e9d2
Author: parazyd <parazyd@dyne.org>
Date: Wed, 15 Jun 2016 13:14:34 +0200
initial commit
Diffstat:
56 files changed, 6645 insertions(+), 0 deletions(-)
diff --git a/aliens.sh b/aliens.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# some disco
+
+x="if(t%2)else"
+python3 -c"[print(t>>15&(t>>(2$x 4))%(3+(t>>(8$x 11))%4)+(t>>10)|42&t>>7&t<<9,end='')for t in range(2**20)]" | aplay -c2 -r4"
diff --git a/autoup b/autoup
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+sshuser=parazyd
+sshkey="$HOME/.ssh/id_ed25519"
+server=pub.parazyd.cf
+webroot=/home/parazyd/public_html/pub
+webdir=tmp
+
+test -z "$1" && exit 1 || filename=$(basename $1)
+scp -i "$sshkey" "$1" "${sshuser}@${server}:${webroot}/${webdir}/${filename}"
+echo "https://$server/$webdir/$filename" | xsel -i
+rm $1
diff --git a/backup.sh b/backup.sh
@@ -0,0 +1 @@
+rsync -aAXv --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/home/parazyd/mnt"} / /home/parazyd/mnt/backup/
diff --git a/cdmenu b/cdmenu
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+normfg="#939393"
+normbg="#000000"
+selbg="#800000"
+selfg="#ffffff"
+font="Terminus:size=9"
+
+list="$(ls -d */ \
+ | dmenu -p 'cd' \
+ -l 20 \
+ -fn $font \
+ -nb $normbg \
+ -nf $normfg \
+ -sb $selbg \
+ -sf $selfg \
+ $@)"
+
+if [ ! -z "$list" ]; then
+ echo $list
+fi
diff --git a/cdump b/cdump
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+xrdb -query | grep -P '^\*color[0-9]*:' | tr -d '\t' | cut -d: -f2 #| hex2col
diff --git a/chrono b/chrono
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# fonts: xsansb future smblock univers
+
+FONT=$1
+: ${FONT:="future"}
+
+for m in $(seq 0 59); do
+ for s in $(seq 0 59); do
+ clear
+ echo
+ printf ' %02d : %02d' $m $s | toilet -f $FONT
+ sleep 1
+ done
+done
diff --git a/ddg b/ddg
@@ -0,0 +1,2 @@
+#!/bin/sh
+palemoon -new-tab "https://duckduckgo.com/html/?q=$*"
diff --git a/disco b/disco
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# party mode
+
+timeout=0.2
+while true; do
+ echo 255 > /sys/devices/platform/thinkpad_acpi/leds/tpacpi::thinklight/brightness
+ sleep $timeout
+ echo 0 > /sys/devices/platform/thinkpad_acpi/leds/tpacpi::thinklight/brightness
+ sleep $timeout
+done
diff --git a/dmenuwindows b/dmenuwindows
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# parayzd - (c) wtfpl 2016
+# list open windows in a dmenu
+
+normfg="#939393"
+normbg="#000000"
+selbg="#800000"
+selfg="#ffffff"
+font="Terminus:size=9"
+
+height=$(wmctrl -l | wc -l)
+if [[ $height -gt 30 ]]; then
+ heightfit=30
+else
+ heightfit=$height
+fi
+
+num=$(wmctrl -l \
+ | sed 's/ hansolo/ /' \
+ | cut -d " " -f 4- \
+ | nl -w 3 -n rn \
+ | sed -r 's/^([ 0-9]+)[ \t]*(.*)$/\1 - \2/' \
+ | dmenu -i \
+ -fn $font \
+ -nb $normbg \
+ -nf $normfg \
+ -sb $selbg \
+ -sf $selfg \
+ -l $heightfit \
+ | cut -d '-' -f -1 \
+)
+
+[[ -z "$num" ]] && exit
+wmctrl -l | sed -n "$num p" | cut -c -10 | xargs wmctrl -i -a
diff --git a/dn b/dn
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# manage resolv.conf
+
+chattr() {
+ case $1 in
+ on) /usr/bin/chattr -V +i /etc/resolv.conf;;
+ off) /usr/bin/chattr -V -i /etc/resolv.conf;;
+ esac
+}
+
+stopd() {
+ /etc/init.d/unbound stop
+ /etc/init.d/dnscrypt-proxy stop
+}
+
+startd() {
+ /etc/init.d/unbound start
+ /etc/init.d/dnscrypt-proxy start
+}
+
+case $1 in
+ out|o)
+ startd
+ chattr off && echo "nameserver 127.0.0.1 # $1" > /etc/resolv.conf && chattr on
+ exit 0;;
+ home|h)
+ stopd
+ chattr off && echo "nameserver 10.0.1.1 # $1" > /etc/resolv.conf && chattr on
+ exit 0;;
+ bridge)
+ stopd
+ chattr off && echo "nameserver 172.16.17.1 # $1" > /etc/resolv.conf && chattr on
+ exit 0;;
+ *)
+ echo "current set as: `awk '{print $4}' /etc/resolv.conf`"
+ echo "usage: `basename $0` {home|out}";;
+esac
+
+exit 1
diff --git a/extmon b/extmon
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# skreenz
+
+IN="LVDS1"
+EXT="VGA1"
+
+case "$1" in
+ left|l)
+ xrandr --output $IN --auto --primary --output $EXT --auto --left-of $IN
+ nitrogen --restore
+ ;;
+ right|r)
+ xrandr --output $IN --auto --primary --output $EXT --auto --right-of $IN
+ nitrogen --restore
+ ;;
+ above|a)
+ xrandr --output $IN --auto --primary --output $EXT --auto --above $IN
+ nitrogen --restore
+ ;;
+ dup|d)
+ xrandr --output $EXT --same-as $IN --output $IN --primary
+ ;;
+ off|o)
+ xrandr --output $IN --auto --output $EXT --off
+ nitrogen --restore
+ ;;
+ list)
+ xrandr | grep \<connected\> | cut -d\ -f1
+ ;;
+ *)
+ echo `basename $0` '{left|right|above|dup|off|list}'
+ ;;
+esac
diff --git a/find-https-debian-archives.py b/find-https-debian-archives.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python2
+#
+# tries to find debian mirrors with https enabled
+
+import urllib2
+import re
+import ssl
+import sys
+
+# # find generic mirrors
+mirrors = urllib2.urlopen('http://www.debian.org/mirror/list')
+https = []
+for line in mirrors.readlines():
+ m = re.match('.*<td valign="top"><a rel="nofollow" href="http(.*)">.*', line)
+ if m:
+ url = 'https' + m.group(1)
+ print 'trying: ',
+ print url,
+ print '...',
+ sys.stdout.flush()
+ try:
+ response=urllib2.urlopen(url, timeout=1)
+ https.append(url)
+ print 'success!'
+ except urllib2.URLError as err:
+ print 'fail!'
+ except ssl.SSLError as err:
+ print 'bad SSL!'
+
+# print 'HTTPS apt repos:'
+#for url in https:
+# print url
+
+
+# # find security mirrors
+mirrors = urllib2.urlopen('http://www.debian.org/mirror/list-full')
+securitys = []
+for line in mirrors.readlines():
+ m = re.match('.*</tt><br>Security updates over HTTP: <tt><a rel="nofollow" href="http(.*)">.*/debian-security/</a>.*', line)
+ if m:
+ url = 'https' + m.group(1)
+ print 'trying: ',
+ print url,
+ print '...',
+ sys.stdout.flush()
+ try:
+ response=urllib2.urlopen(url, timeout=1)
+ securitys.append(url)
+ print 'success!'
+ except urllib2.URLError as err:
+ print 'fail!'
+ except ssl.SSLError as err:
+ print 'bad SSL!'
+
+# print 'HTTPS security repos:'
+# for url in securitys:
+# print url
+
+
+# now find the backports mirrors
+mirrors = urllib2.urlopen('http://backports-master.debian.org/Mirrors/')
+backports = []
+for line in mirrors.readlines():
+#<td><a href="http://be.mirror.eurid.eu/debian-backports/">/debian-backports/</a>
+ m = re.match('.*<td><a href="http(.*)">.*/debian-backports/</a>.*', line)
+ if m:
+ url = 'https' + m.group(1)
+ print 'trying: ',
+ print url,
+ print '...',
+ sys.stdout.flush()
+ try:
+ response=urllib2.urlopen(url, timeout=1)
+ backports.append(url)
+ print 'success!'
+ except urllib2.URLError as err:
+ print 'fail!'
+ except ssl.SSLError as err:
+ print 'bad SSL!'
+
+#print 'HTTPS backports repos:'
+#for url in backports:
+# print url
+
+
+# now find the CD image mirrors
+mirrors = urllib2.urlopen('http://www.debian.org/CD/http-ftp/')
+cds = []
+for line in mirrors.readlines():
+# <a rel="nofollow" href="http://mirror.easyspeedy.com/debian-cd/">HTTP</a></li>
+ m = re.match('.*<a rel="nofollow" href="http(:.*)">HTTP</a></li>.*', line)
+ if m:
+ url = 'https' + m.group(1)
+ print 'trying: ',
+ print url,
+ print '...',
+ sys.stdout.flush()
+ try:
+ response=urllib2.urlopen(url, timeout=1)
+ cds.append(url)
+ print 'success!'
+ except urllib2.URLError as err:
+ print 'fail!'
+ except ssl.SSLError as err:
+ print 'bad SSL!'
+
+print 'HTTPS CD image repos:'
+for url in cds:
+ print url
+
+
+# now write everything to a file
+f = open('/tmp/https-debian-archives.txt', 'w')
+
+f.write('HTTPS apt repos\n')
+f.write('---------------\n')
+for url in https:
+ f.write(url + '\n')
+
+f.write('\n\nHTTPS security repos\n')
+f.write('---------------\n')
+for url in securitys:
+ f.write(url + '\n')
+
+f.write('\n\nHTTPS backports repos\n')
+f.write('--------------------\n')
+for url in backports:
+ f.write(url + '\n')
+
+f.write('\n\nHTTPS CD image repos\n')
+f.write('--------------------\n')
+for url in cds:
+ f.write(url + '\n')
+
+
+f.close()
diff --git a/genhtpasswd b/genhtpasswd
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+user="$1"
+realm="$2"
+password="$3"
+
+if ! [[ -n $user ]] || ! [[ -n $realm ]] || ! [[ -n $password ]]; then
+ echo "usage: `basename $0` [user] [realm] [password]"
+else
+ echo -n "${user}:${realm}:" \
+ && echo -n "${user}:${realm}:${password}" \
+ | md5sum | awk '{print $1}'
+fi
diff --git a/img b/img
@@ -0,0 +1,2 @@
+#!/bin/sh
+curl -s "$1" | 2ff | lel -a -w 640 -h 480
diff --git a/ix b/ix
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+torsocks curl -sF "f:1=<-" ix.io
diff --git a/lock b/lock
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# parazyd - (c) wtfpl 2016
+# make screenshot, pixelize and use as i3lock image
+
+scrot -z /tmp/screen.png
+convert /tmp/screen.png -scale 10% -scale 1000% /tmp/screen.png
+
+if [ -f $HOME/.icons/lock.png ]; then
+ # placement x/y
+ PX=0
+ PY=0
+ # lockscreen image info
+ R=$(file ~/.icons/lock.png | grep -o '[0-9]* x [0-9]*')
+ RX=$(echo $R | cut -d' ' -f 1)
+ RY=$(echo $R | cut -d' ' -f 3)
+
+ SR=$(xrandr --query | grep ' connected' | cut -f4 -d' ')
+ for RES in $SR; do
+ # monitor position/offset
+ SRX=$(echo $RES | cut -d'x' -f 1) # x pos
+ SRY=$(echo $RES | cut -d'x' -f 2 | cut -d'+' -f 1) # y pos
+ SROX=$(echo $RES | cut -d'x' -f 2 | cut -d'+' -f 2) # x offset
+ SROY=$(echo $RES | cut -d'x' -f 2 | cut -d'+' -f 3) # y offset
+ PX=$(($SROX + $SRX/2 - $RX/2))
+ PY=$(($SROY + $SRY/2 - $RY/2))
+
+ convert /tmp/screen.png $HOME/.icons/lock.png -geometry +$PX+$PY -composite -matte /tmp/screen.png
+ done
+fi
+i3lock -e -i /tmp/screen.png
diff --git a/macspoof.sh b/macspoof.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# spoof or restore mac
+
+case "$1" in
+ random|rand|r)
+ echo "(*) randomizing $2's MAC"
+ ip link set dev $2 down
+ macchanger -r $2
+ ip link set dev $2 up
+ echo "(*) done!"
+ ;;
+ permanent|orig)
+ echo "(*) restoring $2's MAC"
+ ip link set dev $2 down
+ macchanger -p $2
+ ip link set dev $2 up
+ echo "(*) done!"
+ ;;
+ *)
+ echo `basename $0` '{random|orig} {device}'
+ ;;
+esac
diff --git a/nx-termcast b/nx-termcast
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+#
+# start recording a script and upload it in base64
+# usage: nx-termcast [url]
+
+nx_upload() {
+ curl -sLT${1:--} https://p.iotek.org
+}
+
+nx_record() {
+ script --timing=timing typescript
+ # make a motherfucking tarbomb
+ tar cz . | base64 | nx_upload
+}
+
+nx_replay() {
+ curl -s "$*" | base64 -d | tar xz
+ scriptreplay timing typescript
+}
+
+# that's to prevent the motherfucking tarbombs
+cd $(mktemp -d)
+
+# give the impression of a clean session
+
+# either record or play, your choice
+test -n "$1" && nx_replay "$*" || nx_record
diff --git a/obsolete/dmenu.sh b/obsolete/dmenu.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+dmenu_run
diff --git a/obsolete/scream.sh b/obsolete/scream.sh
@@ -0,0 +1,12 @@
+#!/bin/zsh
+
+POSIT=/sys/devices/platform/hdaps/position
+
+while true; do
+ val1=`cat $POSIT | awk -F, '{print $1}' | sed 's/(//'`
+ val2=`cat $POSIT | awk -F, '{print $2}' | sed 's/)//'`
+
+ while [[ $val1 -lt '-370' && $val1 -gt '-375' ]]; do
+ echo "AHAHHAHAA!!"
+ done
+done
diff --git a/obsolete/startcups.sh b/obsolete/startcups.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+sudo /etc/init.d/avahi-dnsconfd start
+sudo /etc/init.d/cupsd start
diff --git a/obsolete/startvbox.sh b/obsolete/startvbox.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+sudo modprobe vboxdrv vboxnetadp vboxnetflt vboxpci; virtualbox
diff --git a/obsolete/torrentnotif.sh b/obsolete/torrentnotif.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+notify-send 'Transmission' 'Torrent Complete!\n$1 $2 $3'
diff --git a/obsolete/updatepw.sh b/obsolete/updatepw.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env zsh
+
+for i in `find -name "*.xz"`; do
+ rm ${i}.sha
+ xz -d $i
+done
+
+for i in `find -name "*.img"`; do
+ loopdev=`sudo losetup -f --show ${i}`
+ mappdev=`sudo kpartx -va $loopdev | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`
+
+ mappdev="/dev/mapper/${mappdev}"
+ rootpart="${mappdev}p2"
+
+ [[ -d "mountdir" ]] || mkdir mountdir
+
+ mount ${rootpart} mountdir
+
+ cat << EOF | sudo tee mountdir/changepw
+#!/bin/sh
+echo "root:devuan" | chpasswd
+EOF
+
+ sudo cp /usr/bin/qemu-arm-static mountdir/usr/bin/qemu-arm-static
+
+ sudo chroot mountdir /changepw
+ sudo rm mountdir/changepw
+ sudo rm mountdir/usr/bin/qemu-arm-static
+
+ sudo umount ${rootpart}
+
+ sudo kpartx -dv ${loopdev}
+ sudo losetup -d ${loopdev}
+
+ xz -zv ${i}
+ sha256sum ${i}.xz > ${i}.xz.sha
+done
diff --git a/open b/open
@@ -0,0 +1,295 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use constant
+{
+ PROG_REVEAL => 'rox',
+ PROG_EDIT => 'vim',
+};
+
+sub open_smart;
+sub edit;
+sub stdin_to_editor;
+sub reveal;
+sub header_edit;
+sub wait_or_not;
+
+sub usage
+{
+ print STDERR <<"!";
+Usage: $0 -[efWwRh]
+-e: edit
+-f: stdin-edit
+-W: wait for exit (true by default for editing)
+-w: don't wait for exit
+-R: reveal
+-h: header search
+!
+ exit 1;
+}
+
+my $cmd = \&open_smart;
+my(@files, @args);
+
+my %opts = (
+ 'e' => 0,
+ 'f' => 0,
+ 'W' => 0,
+ 'R' => 0,
+ 'h' => 0,
+);
+
+my $wait_set = 0;
+
+usage() unless @ARGV;
+
+for(my $i = 0; $i < @ARGV; ++$i){
+ $_ = $ARGV[$i];
+
+ if($_ eq '--'){
+ push @files, @ARGV[$i + 1 .. $#ARGV];
+ last;
+ }
+
+ if(/^-([a-z])$/i){
+ my $k = $1;
+
+ if(exists $opts{$k}){
+ $opts{$k} = 1;
+ $wait_set = 1 if $k eq 'W';
+ }elsif($k eq 'w'){
+ $opts{W} = 0;
+ $wait_set = 1;
+ }else{
+ usage();
+ }
+
+ }elsif($_ eq '--args'){
+ push @args, @ARGV[$i + 1 .. $#ARGV];
+ last;
+
+ }elsif(/^-/){
+ usage();
+
+ }else{
+ push @files, $_;
+
+ }
+}
+
+if($opts{e} + $opts{f} + $opts{R} + $opts{h} > 1){
+ print STDERR "Can't combine -e, -f, -R and -h\n";
+ usage();
+}
+
+my $should_wait = 1;
+if($opts{e}){
+ $cmd = \&edit;
+
+}elsif($opts{f}){
+ # <STDIN> | $EDITOR -
+ $cmd = \&stdin_to_editor;
+
+}elsif($opts{R}){
+ # open with rox
+ $cmd = \&reveal;
+ $should_wait = 0;
+
+}elsif($opts{h}){
+ # search /usr/include/$_ for @files
+ $cmd = \&header_edit;
+
+}
+
+$opts{W} = 1 if $should_wait and not $wait_set;
+
+exit(&{$cmd}((
+ wait => !!$opts{W},
+ args => [@args],
+ files => [@files])));
+
+# end ---
+
+sub open_smart
+{
+ sub read_maps
+ {
+ my $rc = "$ENV{HOME}/.openrc";
+ open F, '<', $rc or die "open $rc: $!\n";
+
+ my %maps;
+
+ my $suffix = 0;
+ while(<F>){
+ chomp;
+ s/#.*//;
+
+ if(/^\[(.*)\]$/){
+ if($1 eq 'full'){
+ $suffix = 0;
+ }elsif($1 eq 'suffix'){
+ $suffix = 1;
+ }else{
+ die "invalid section \"$1\" in $rc\n";
+ }
+
+ }elsif(my($prog, $matches) = /^([^:]+): *(.*)/){
+ sub getenv
+ {
+ my $k = shift;
+ return $ENV{$k} if $ENV{$k};
+ my %backup = (
+ "TERM" => "urxvt",
+ "VISUAL" => "vim",
+ );
+ return $backup{$k} if $backup{$k};
+ return "\$$k";
+ }
+
+ my @matches = split / *, */, $matches;
+
+ $prog =~ s/\$([A-Z_]+)/getenv($1)/e;
+
+ push @{$maps{$prog}}, [ $_, $suffix ] for @matches;
+
+ }elsif(length){
+ die "invalid confiuration line: \"$1\" in $rc\n";
+ }
+ }
+
+ close F;
+
+ return %maps;
+ }
+
+ my %maps = read_maps();
+ my $ec = 0;
+ my %h = @_;
+
+ my @to_open;
+
+file:
+ for my $fnam (@{$h{files}}){
+ #print "maps:\n";
+
+ if(-d $fnam){
+ push @to_open, [($h{wait}, PROG_REVEAL, @{$h{args}}, $fnam)];
+ next file;
+ }
+
+ for my $prog (keys %maps){
+ #print " $_:\n";
+ for(@{$maps{$prog}}){
+ my($reg, $suffix) = ($_->[0], $_->[1]);
+ if($suffix){
+ $reg = "\\.$reg\$";
+ }
+ #print " $reg\n"
+
+ if($fnam =~ /$reg/){
+ push @to_open, [($h{wait}, $prog, @{$h{args}}, $fnam)];
+ next file;
+ }
+ }
+ }
+
+ die "no program found for $fnam\n";
+ }
+
+ wait_or_not(@{$_}) for @to_open;
+
+ return 0;
+}
+
+sub wait_or_not
+{
+ my($wait, @rest) = @_;
+ my $pid = fork();
+
+ die "fork(): $!\n" unless defined $pid;
+
+ if($pid == 0){
+ if($rest[0] =~ / /){
+ my $a = shift @rest;
+ unshift @rest, split / +/, $a;
+ }
+
+ exec @rest;
+ die;
+ }else{
+ # parent
+ if($wait){
+ my $reaped = wait();
+ my $ret = $?;
+
+ die "wait(): $!\n" if $reaped == -1;
+ warn "unexpected dead child $reaped (expected $pid)\n" if $reaped != $pid;
+
+ return $ret;
+ }
+ }
+}
+
+sub edit
+{
+ my %h = @_;
+ my $e = $ENV{VISUAL} || $ENV{EDITOR} || PROG_EDIT;
+ return wait_or_not($h{wait}, $e, @{$h{args}}, @{$h{files}});
+}
+
+sub stdin_to_editor
+{
+ my $tmp = "/tmp/stdin_$$";
+
+ open F, '>', $tmp or die "open $tmp: $!\n";
+ print F $_ while <STDIN>;
+ close F;
+
+ my %h = @_;
+ push @{$h{files}}, $tmp;
+ my $r = edit(%h);
+ unlink $tmp;
+ return $r;
+}
+
+sub reveal
+{
+ my %h = @_;
+ return wait_or_not($h{wait}, PROG_REVEAL, @{$h{args}}, @{$h{files}});
+}
+
+sub header_edit
+{
+ my %h = @_;
+ my @files = @{$h{files}};
+ @{$h{files}} = ();
+
+ for my $name (@files){
+ sub find_header
+ {
+ my @inc = ("", "arpa", "net", "sys");
+ my $r = shift;
+ my @matches;
+
+ for(my @tmp = @inc){
+ push @inc, "x86_64-linux-gnu/$_";
+ }
+
+ for my $inc (@inc){
+ $inc = "/usr/include/$inc";
+
+ opendir D, $inc or next;
+ push @matches, map { "$inc/$_" } grep /$r/, readdir D;
+ closedir D;
+ }
+
+ return @matches;
+ }
+
+ my @paths = find_header($name);
+ push @{$h{files}}, @paths if @paths;
+ }
+
+ return edit(%h);
+}
diff --git a/p b/p
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+palemoon=/usr/bin/palemoon
+profile="$1"
+
+ps ax | grep '[p]alemoon' > /dev/null
+[[ $? = 0 ]] && {
+ # one is already running, run next with -no-remote
+ opts=" -no-remote "
+}
+case $profile in
+ obi) $palemoon ${opts} -P def;;
+ soc) $palemoon ${opts} -P soc;;
+ profile*) $palemoon -ProfileManager;;
+ *) $palemoon $*;;
+esac
diff --git a/pacmans.sh b/pacmans.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+initializeANSI()
+{
+ esc="$(echo -en '\e')"
+
+ blackf="${esc}[30m"; redf="${esc}[31m"; greenf="${esc}[32m"
+ yellowf="${esc}[33m" bluef="${esc}[34m"; purplef="${esc}[35m"
+ cyanf="${esc}[36m"; whitef="${esc}[37m"
+
+ blackb="${esc}[40m"; redb="${esc}[41m"; greenb="${esc}[42m"
+ yellowb="${esc}[43m" blueb="${esc}[44m"; purpleb="${esc}[45m"
+ cyanb="${esc}[46m"; whiteb="${esc}[47m"
+
+ boldon="${esc}[1m"; boldoff="${esc}[22m"
+ italicson="${esc}[3m"; italicsoff="${esc}[23m"
+ ulon="${esc}[4m"; uloff="${esc}[24m"
+ invon="${esc}[7m"; invoff="${esc}[27m"
+
+ reset="${esc}[0m"
+}
+
+# note in this first use that switching colors doesn't require a reset
+# first - the new color overrides the old one.
+
+#clear
+
+initializeANSI
+
+cat << EOF
+
+ ${yellowf} ▄███████▄${reset} ${redf} ▄██████▄${reset} ${greenf} ▄██████▄${reset} ${bluef} ▄██████▄${reset} ${purplef} ▄██████▄${reset} ${cyanf} ▄██████▄${reset}
+ ${yellowf}▄█████████▀▀${reset} ${redf}▄${whitef}█▀█${redf}██${whitef}█▀█${redf}██▄${reset} ${greenf}▄${whitef}█▀█${greenf}██${whitef}█▀█${greenf}██▄${reset} ${bluef}▄${whitef}█▀█${bluef}██${whitef}█▀█${bluef}██▄${reset} ${purplef}▄${whitef}█▀█${purplef}██${whitef}█▀█${purplef}██▄${reset} ${cyanf}▄${whitef}█▀█${cyanf}██${whitef}█▀█${cyanf}██▄${reset}
+ ${yellowf}███████▀${reset} ${redf}█${whitef}▄▄█${redf}██${whitef}▄▄█${redf}███${reset} ${greenf}█${whitef}▄▄█${greenf}██${whitef}▄▄█${greenf}███${reset} ${bluef}█${whitef}▄▄█${bluef}██${whitef}▄▄█${bluef}███${reset} ${purplef}█${whitef}▄▄█${purplef}██${whitef}▄▄█${purplef}███${reset} ${cyanf}█${whitef}▄▄█${cyanf}██${whitef}▄▄█${cyanf}███${reset}
+ ${yellowf}███████▄${reset} ${redf}████████████${reset} ${greenf}████████████${reset} ${bluef}████████████${reset} ${purplef}████████████${reset} ${cyanf}████████████${reset}
+ ${yellowf}▀█████████▄▄${reset} ${redf}██▀██▀▀██▀██${reset} ${greenf}██▀██▀▀██▀██${reset} ${bluef}██▀██▀▀██▀██${reset} ${purplef}██▀██▀▀██▀██${reset} ${cyanf}██▀██▀▀██▀██${reset}
+ ${yellowf} ▀███████▀${reset} ${redf}▀ ▀ ▀ ▀${reset} ${greenf}▀ ▀ ▀ ▀${reset} ${bluef}▀ ▀ ▀ ▀${reset} ${purplef}▀ ▀ ▀ ▀${reset} ${cyanf}▀ ▀ ▀ ▀${reset}
+
+ ${boldon}${yellowf} ▄███████▄ ${redf} ▄██████▄ ${greenf} ▄██████▄ ${bluef} ▄██████▄ ${purplef} ▄██████▄ ${cyanf} ▄██████▄${reset}
+ ${boldon}${yellowf}▄█████████▀▀ ${redf}▄${whitef}█▀█${redf}██${whitef}█▀█${redf}██▄ ${greenf}▄${whitef}█▀█${greenf}██${whitef}█▀█${greenf}██▄ ${bluef}▄${whitef}█▀█${bluef}██${whitef}█▀█${bluef}██▄ ${purplef}▄${whitef}█▀█${purplef}██${whitef}█▀█${purplef}██▄ ${cyanf}▄${whitef}█▀█${cyanf}██${whitef}█▀█${cyanf}██▄${reset}
+ ${boldon}${yellowf}███████▀ ${redf}█${whitef}▄▄█${redf}██${whitef}▄▄█${redf}███ ${greenf}█${whitef}▄▄█${greenf}██${whitef}▄▄█${greenf}███ ${bluef}█${whitef}▄▄█${bluef}██${whitef}▄▄█${bluef}███ ${purplef}█${whitef}▄▄█${purplef}██${whitef}▄▄█${purplef}███ ${cyanf}█${whitef}▄▄█${cyanf}██${whitef}▄▄█${cyanf}███${reset}
+ ${boldon}${yellowf}███████▄ ${redf}████████████ ${greenf}████████████ ${bluef}████████████ ${purplef}████████████ ${cyanf}████████████${reset}
+ ${boldon}${yellowf}▀█████████▄▄ ${redf}██▀██▀▀██▀██ ${greenf}██▀██▀▀██▀██ ${bluef}██▀██▀▀██▀██ ${purplef}██▀██▀▀██▀██ ${cyanf}██▀██▀▀██▀██${reset}
+ ${boldon}${yellowf} ▀███████▀ ${redf}▀ ▀ ▀ ▀ ${greenf}▀ ▀ ▀ ▀ ${bluef}▀ ▀ ▀ ▀ ${purplef}▀ ▀ ▀ ▀ ${cyanf}▀ ▀ ▀ ▀${reset}
+
+EOF
diff --git a/pio b/pio
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+curl -sLT${1:--} https://p.iotek.org
diff --git a/pipesx b/pipesx
@@ -0,0 +1,193 @@
+#!/bin/bash
+# Animated pipes.sh terminal screensaver at an angle.
+# Copyright (C) 2013, 2014 by Yu-Jie Lin
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# Website: https://github.com/livibetter/pipesX.sh
+
+VERSION=0.1.0
+
+W=$(tput cols) H=$(tput lines)
+# maximal random value + 1
+M=32768
+
+SETS=('╱╲' '/\')
+COLORS=(31 32 33 34 35 36 37)
+
+# default values
+N=1
+T=()
+I=0.05
+P=25
+R=$((W * H / 4))
+
+HELP="Usage: $(basename $0) [OPTIONS]
+Animated pipes.sh terminal screensaver at an angle.
+
+Options:
+
+ -n [1-] number of pipes. (Default: $N)
+ -t [0-$((${#SETS[@]} - 1))] types of pipes, can be used more than once. (Default: $T)
+ -i [float] piping interval or maze generation interval. (Default: $I)
+ -P [0-100] probability of a turning pipe or of \\ in maze generation. (Default: $P)
+ -r [LIMIT] reset after x characters, 0 if no limit. (Default: $R)
+ -R random starting point.
+ -C no color.
+ -X maze generation.
+ -h this help message.
+ -v print version number.
+"
+
+while getopts "n:t:i:P:r:RCXhv" arg; do
+ case $arg in
+ n)
+ ((N = OPTARG > 0 ? OPTARG : N))
+ ;;
+ t)
+ T+=($(((OPTARG >= 0 && OPTARG < ${#SETS[@]}) ? OPTARG : T)))
+ ;;
+ i)
+ I=$OPTARG
+ ;;
+ P)
+ ((P = (OPTARG >= 0 && OPTARG <= 100) ? OPTARG : P))
+ ;;
+ r)
+ ((R = OPTARG >= 0 ? OPTARG : R))
+ ;;
+ R)
+ RNDSTART=1
+ ;;
+ C)
+ NOCOLOR=1
+ ;;
+ X)
+ MAZE=1
+ ;;
+ h)
+ echo -e "$HELP"
+ exit 0
+ ;;
+ v)
+ echo "$(basename -- "$0") $VERSION"
+ exit 0
+ esac
+done
+
+# set to default values if not by options
+((${#T[@]})) || T=(0)
+
+do_exit() {
+ # Show cursor and echo stdin
+ echo -ne "\e[?25h"
+ stty echo
+ clear
+ exit 0
+ }
+trap do_exit INT TERM
+
+# No echo stdin and hide the cursor
+stty -echo
+echo -ne "\e[?25l"
+
+# maze geneartion
+while [[ $MAZE ]] && clear; do
+ [[ $NOCOLOR ]] || echo -ne "\e[1;${COLORS[${#COLORS[@]} * RANDOM / M]}m"
+ for ((i = 0; i < W * H; i++ )); do
+ echo -ne ${SETS[T]:100 * RANDOM / M < P:1}
+ done
+ read -t $I -n 1 && [[ $REPLY =~ q|Q ]] && do_exit
+done
+
+# initialze values
+for ((n = 0; n < N; n++)); do
+ ((X[n] = RNDSTART ? (W + 2) * RANDOM / M : W / 2))
+ ((Y[n] = RNDSTART ? (H + 2) * RANDOM / M : H / 2))
+ D[n]=$((4 * RANDOM / M))
+ C[n]=${COLORS[${#COLORS[@]} * RANDOM / M]}
+ t[n]=${T[${#T[@]} * RANDOM / M]}
+done
+
+clear
+while :; do
+ for ((n = 0; n < N; n++, CC = 0)); do
+ x=${X[n]} y=${Y[n]}
+ d=${D[n]} c=${C[n]}
+
+ # calculate new direction `d`
+ # 1 0
+ # \/ 4 directions 0 to 3
+ # /\
+ # 2 3
+ # valid directions: d: dd', d' is the new direction
+ # d
+ # 0: / 00 \ 01 03
+ # / / /\
+ # 1: / 10 \ 11 12
+ # \ \ /\
+ # 2: \/ 21 / 22 / 23
+ # / \
+ # 3: \/ 30 \ 32 \ 33
+ # / \
+ ((d = (100 * RANDOM / M) < P ? ((d + 1) + 2 * (RANDOM % 2)) % 4 : d))
+ ((e = (d + 1) % 4))
+
+ # calculate new position
+ # d' x' y'
+ # 0: x+1 y-1
+ # 1: x-1 y-1
+ # 2: x-1 y+1
+ # 3: x+1 y+1
+ ((xn = e < 2 ? x + 1 : x - 1))
+ ((yn = d < 2 ? y - 1 : y + 1))
+
+ # adjust position and change color?
+ ((d < 2 && y == 0)) && ((yn--, CC=1))
+ ((e > 1 && x == 0)) && ((xn--, CC=1))
+ ((d > 1 && y == H)) && ((yn++, CC=1))
+ ((e < 2 && x == W)) && ((xn++, CC=1))
+ ((CC)) && c=${COLORS[${#COLORS[@]} * RANDOM / M]}
+ ((CC)) && t[n]=${T[${#T[@]} * RANDOM / M]}
+
+ # warp pipe
+ ((xn = (xn + W + 1) % (W + 1)))
+ ((yn = (yn + H + 1) % (H + 1)))
+
+ # calculate position in terminal
+ # d' xt yt
+ # 0: x' y'+1
+ # 1: x'+1 y'+1
+ # 2: x'+1 y'
+ # 3: x' y'
+ ((xt = e < 2 ? xn : xn + 1))
+ ((yt = d < 2 ? yn + 1 : yn))
+
+ echo -ne "\e[${yt};${xt}H"
+ [[ $NOCOLOR ]] || echo -ne "\e[1;${c}m"
+ echo -n "${SETS[t[n]]:d%2:1}"
+
+ X[n]=$xn Y[n]=$yn
+ D[n]=$d C[n]=$c
+ done
+ read -t $I -n 1 && [[ $REPLY =~ q|Q ]] && do_exit
+ ((R)) && ((r += N, r >= R)) && r=0 && clear
+done
+
+do_exit+
\ No newline at end of file
diff --git a/popup b/popup
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+duration=3
+
+(echo " $@"; sleep ${duration}) | lemonbar -g '120x20+10+10' -B '#ff333333' -n 'popuplemonbar'
diff --git a/powertoptune.sh b/powertoptune.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+echo '1500' > '/proc/sys/vm/dirty_writeback_centisecs';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-0/device/power/control';
+echo 'auto' > '/sys/bus/usb/devices/1-1.3/power/control';
+echo 'auto' > '/sys/bus/usb/devices/2-1.3/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-3/device/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-5/device/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-1/device/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-2/device/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-4/device/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-6/device/power/control';
+echo 'auto' > '/sys/bus/i2c/devices/i2c-7/device/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1d.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1c.3/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1c.4/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1c.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1b.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1a.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:19.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:16.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1f.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:02.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:ff:00.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1f.2/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:00.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:ff:02.1/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:ff:02.3/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:ff:02.2/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:ff:02.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:ff:00.1/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:02:00.0/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1f.3/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1f.6/power/control';
+echo 'auto' > '/sys/bus/pci/devices/0000:00:1e.0/power/control';
diff --git a/rain b/rain
@@ -0,0 +1,100 @@
+#!/bin/bash
+RAINS=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿")
+COLORS=("\e[37m" "\e[37;1m")
+# More from 256 color mode
+for i in {244..255}; do
+ COLORS=("${COLORS[@]}" "\e[38;5;${i}m")
+done
+NRAINS=${#RAINS[@]}
+NCOLORS=${#COLORS[@]}
+NUM_RAIN_METADATA=5
+
+
+sigwinch() {
+ TERM_WIDTH=$(tput cols)
+ TERM_HEIGHT=$(tput lines)
+ STEP_DURATION=0.025
+ ((MAX_RAINS = TERM_WIDTH * TERM_HEIGHT / 4))
+ ((MAX_RAIN_LENGTH = TERM_HEIGHT < 10 ? 1 : TERM_HEIGHT / 10))
+ # In percentage
+ ((NEW_RAIN_ODD = TERM_HEIGHT > 50 ? 100 : TERM_HEIGHT * 2))
+ ((NEW_RAIN_ODD = NEW_RAIN_ODD * 75 / 100))
+ ((FALLING_ODD = TERM_HEIGHT > 25 ? 100 : TERM_HEIGHT * 4))
+ ((FALLING_ODD = FALLING_ODD * 90 / 100))
+ }
+
+do_exit() {
+ echo -ne "\e[${TERM_HEIGHT};1H\e[0K"
+
+ # Show cursor and echo stdin
+ echo -ne "\e[?25h"
+ stty echo
+ exit 0
+ }
+
+do_render() {
+ # Clean screen first
+ for ((idx = 0; idx < num_rains * NUM_RAIN_METADATA; idx += NUM_RAIN_METADATA)); do
+ X=${rains[idx]}
+ Y=${rains[idx + 1]}
+ LENGTH=${rains[idx + 4]}
+ for ((y = Y; y < Y + LENGTH; y++)); do
+ (( y < 1 || y > TERM_HEIGHT )) && continue
+ echo -ne "\e[${y};${X}H "
+ done
+ done
+
+ for ((idx = 0; idx < num_rains * NUM_RAIN_METADATA; idx += NUM_RAIN_METADATA)); do
+ if ((100 * RANDOM / 32768 < FALLING_ODD)); then
+ # Falling
+ if ((++rains[idx + 1] > TERM_HEIGHT)); then
+ # Out of screen, bye sweet <3
+ rains=("${rains[@]:0:idx}"
+ "${rains[@]:idx+NUM_RAIN_METADATA:num_rains*NUM_RAIN_METADATA}")
+ ((num_rains--))
+ continue
+ fi
+ fi
+ X=${rains[idx]}
+ Y=${rains[idx + 1]}
+ RAIN=${rains[idx + 2]}
+ COLOR=${rains[idx + 3]}
+ LENGTH=${rains[idx + 4]}
+ for ((y = Y; y < Y + LENGTH; y++)); do
+ (( y < 1 || y > TERM_HEIGHT )) && continue
+ echo -ne "\e[${y};${X}H${COLOR}${RAIN}"
+ done
+ done
+ }
+
+trap do_exit TERM INT
+trap sigwinch WINCH
+# no echo stdin and hide the cursor
+stty -echo
+echo -ne "\e[?25l"
+
+echo -ne "\e[2J"
+rains=()
+sigwinch
+while :; do
+ read -n 1 -t $STEP_DURATION ch
+ case "$ch" in
+ q|Q)
+ do_exit
+ ;;
+ esac
+
+ if ((num_rains < MAX_RAINS)) && ((100 * RANDOM / 32768 < NEW_RAIN_ODD)); then
+ # Need new |, 1-based
+ RAIN="${RAINS[NRAINS * RANDOM / 32768]}"
+ COLOR="${COLORS[NCOLORS * RANDOM / 32768]}"
+ LENGTH=$((MAX_RAIN_LENGTH * RANDOM / 32768 + 1))
+ X=$((TERM_WIDTH * RANDOM / 32768 + 1))
+ Y=$((1 - LENGTH))
+ rains=("${rains[@]}" "$X" "$Y" "$RAIN" "$COLOR" "$LENGTH")
+ ((num_rains++))
+ fi
+
+ # let rain fall!
+ do_render
+done
diff --git a/rainbow b/rainbow
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# z3bra - 2015 (c) wtfpl
+# make the current window "rainbowish"... Awesome idea from xero@nixers.net !
+
+FREQ=${FREQ:-0.1}
+COLORS="888888 8064cc 6480cc 64cccc 80cc64 cccc64 cc6464"
+CUR=$(pfw)
+
+while :; do
+ for c in $COLORS; do
+ chwb -c $c $(pfw)
+ sleep $FREQ
+ done
+done
diff --git a/randcolor b/randcolor
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# print a random terminal colorscheme
+
+for i in `seq 0 15`; do
+ c=$(tr -cd "0-9a-f" < /dev/urandom | fold -bw 6 | sed 1q)
+ printf "*color%d: #%s\n" "$i" "$c"
+done
diff --git a/record b/record
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# screencast time
+
+PIDNAME=recorder
+FRAMERATE=25
+RES=$(wattr wh `lsw -r` | tr \ x)
+
+usage() {
+ echo "usage: $(basename $0) [-fk] <filename>" >&2
+ exit 1
+}
+
+case $1 in
+ -f) FRAMERATE=50; shift 1 ;;
+ -k) kill $(pidof -s $PIDNAME); exit 0 ;;
+esac
+
+test -n "$1" && WEBM=$1 || usage
+
+echo Framerate: $FRAMERATE
+exec -a $PIDNAME ffmpeg -f x11grab -s $RES -an -r $FRAMERATE -i :0.0 -c:v libvpx -b:v 5M -crf 10 -quality realtime -y -loglevel quiet $1
diff --git a/refan b/refan
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/thinkfan restart
diff --git a/retor b/retor
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/tor restart
diff --git a/rot13 b/rot13
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+tr 'A-Za-z' 'N-ZA-Mn-za-m'
diff --git a/signrelease b/signrelease
@@ -0,0 +1,55 @@
+#!/bin/zsh
+#
+# shasum and sign release
+
+KEY=B876CB44FA1B0274
+SUM=sha256sum
+
+{ test -r "$1" } || { print "usage: signrelease [directory|tarball]"; return 0 }
+
+target="$1"
+
+{ test -d "$target" } && {
+
+ pushd "$target"
+ for i in README.md INSTALL.md ChangeLog.md AUTHORS.md KNOWN_BUGS.md TODO.md; do
+ { test -r $i } && {
+ print "converting $i to .txt"
+ mv $i ${i%%.md}.txt }
+ # cat $i | markdown > ${i%%.md}.html
+ done
+ for i in COPYING LICENSE TODO README INSTALL ChangeLog AUTHORS KNOWN_BUGS; do
+ [[ -r $i ]] && {
+ print "converting $i to .txt"
+ mv $i ${i}.txt
+ }
+ done
+
+ # remove git dirs
+ { test -r .gitignore } && { rm -f .gitignore }
+ { test -d .git } && { rm -rf .git }
+
+ # generate configure
+ [[ -r configure.in ]] && autoreconf -i
+ [[ -r configure.ac ]] && autoreconf -i
+
+ # remove configure artifacts
+ [[ -r autom4te.cache ]] && rm -rf autom4te.cache
+
+ popd
+
+ print "packing tarball from dir $target"
+ tar cfz ${target}.tar.gz ${target}
+ target="$1.tar.gz"
+}
+
+{ test -r "$target" } && {
+ print "making checksum of $target"
+ ${=SUM} "$target" > "$target.sha"
+ print "signing tarball with GnuPG key id $KEY"
+ gpg -b -a -u $KEY "$target"
+}
+
+print "Release done:"
+ls -lh ${target}
+cat ${target}.sha
diff --git a/skrot b/skrot
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ $1 == "-s" ]; then
+ scrot -s '%Y-%m-%d_%H-%M-%S_$wx$h.png' -e 'mv $f ~/screenshots/'
+else
+ scrot '%Y-%m-%d_%H-%M-%S_$wx$h.png' -e 'mv $f ~/screenshots/'
+fi
diff --git a/square b/square
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+CHAR=${1:-██}
+
+echo
+for i in `seq 0 7`; do printf " [0;3${i}m${CHAR}[0m"; done; echo
+for i in `seq 0 7`; do printf " [1;3${i}m${CHAR}[0m"; done; echo
+echo
diff --git a/sslget b/sslget
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# retrieve SSL certificate of a website
+# it assumes 443 to be the default SSL port
+
+if [[ -n $2 ]]; then
+ portnum=$2
+else
+ portnum=443
+fi
+
+echo | openssl s_client -connect $1:$portnum 2>&1| sed '/BEGIN CERT/,/END CERT/p;d'
diff --git a/startdwm b/startdwm
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+while true; do
+ # Log stderror to a file
+ dwm 2> ~/.dwm.log
+ # No error logging
+ #dwm >/dev/null 2>&1
+done
diff --git a/supadd b/supadd
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# edit sup's bins
+
+supsrc="/home/parazyd/src/INSTALLED/sup"
+
+printsettings() {
+ cat <<EOF
+/* sup's configuration file */
+
+/* (un)comment flags below to remove functionalities */
+#define HASH 1
+#define DAEMON 0
+/* #define DEBUG 0 */
+
+#ifndef FLAGSONLY
+
+#define USER 1000
+#define GROUP -1
+
+#define SETUID 0
+#define SETGID 0
+
+#define CHROOT ""
+#define CHRDIR ""
+
+static struct rule_t rules[] = {
+ /* allow user to run these programs when found in path location */
+EOF
+}
+
+printentry() {
+ cat <<EOF
+ { USER, GROUP, "$bin", "$binpath", "$shasum" },
+EOF
+}
+
+printrest() {
+ cat <<EOF
+
+ { 0 },
+};
+
+#endif
+EOF
+}
+
+getsha() {
+ shasum=`sha256sum $binpath | awk '{print $1}'`
+ [[ -n $shasum ]] || exit 1
+}
+case $1 in
+ -l) grep -v -i -e '//' -e '^#' $supsrc/allowed.txt ;;
+ *)
+ binpath="$1"
+ bin=`echo $binpath | awk -F"/" '{print $NF}'`
+ if ! [[ $binpath == /* ]]; then
+ binpath=`pwd`/$bin
+ fi
+ getsha || exit 1
+
+ if [[ `grep $binpath $supsrc/allowed.txt` ]]; then
+ grep -v $binpath $supsrc/allowed.txt > /tmp/allowed.txt
+ mv /tmp/allowed.txt $supsrc/allowed.txt
+ fi
+ printentry >> $supsrc/allowed.txt
+ printsettings | cat - $supsrc/allowed.txt > $supsrc/config.h
+ printrest >> $supsrc/config.h
+
+ `basename $0` -l
+ ;;
+esac
+
+cd $supsrc
diff --git a/susp b/susp
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo mem > /sys/power/state
diff --git a/takeoffdir b/takeoffdir
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+TAKEOFF_DIR=/tmp/takeoff
+
+mkdir -p $TAKEOFF_DIR
+
+# file either created or modified
+wendy -m 136 -f $TAKEOFF_DIR -v | while read ev file; do
+ $HOME/bin/autoup "$file"
+done
diff --git a/testssl.sh b/testssl.sh
@@ -0,0 +1,4892 @@
+#!/usr/bin/env bash
+#
+# vim:ts=5:sw=5
+# use vim and you will see everything beautifully indented with a 5 char tab
+
+[ -z "$BASH_VERSINFO" ] && printf "\n\033[1;35m Please make sure you're using \"bash\"! Bye...\033[m\n\n" >&2 && exit 245
+[ $(kill -l | grep -c SIG) -eq 0 ] && printf "\n\033[1;35m Please make sure you're calling me without leading \"sh\"! Bye...\033[m\n\n" >&2 && exit 245
+
+# testssl.sh is a program for spotting weak SSL encryption, ciphers, version and some
+# vulnerabilities or features
+#
+# Devel version is available from https://github.com/drwetter/testssl.sh
+# Stable version from https://testssl.sh
+# Please file bugs at github! https://github.com/drwetter/testssl.sh/issues
+
+# Main author: Dirk Wetter, copyleft: 2007-today, contributions so far see CREDIT.md
+#
+# License: GPLv2, see http://www.fsf.org/licensing/licenses/info/GPLv2.html
+# and accompanying license "LICENSE.txt". Redistribution + modification under this
+# license permitted.
+# If you enclose this script or parts of it in your software, it has to
+# be accompanied by the same license (see link) and the place where to get
+# the recent version of this program. Do not violate the license!
+#
+# USAGE WITHOUT ANY WARRANTY, THE SOFTWARE IS PROVIDED "AS IS". USE IT AT
+# your OWN RISK!
+
+# HISTORY:
+# Back in 2006 it all started with a few openssl commands...
+# That's because openssl is a such a good swiss army knife (see e.g.
+# wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist
+# wrapping some shell commands around it, which I used for my pen tests. This is how
+# everything started.
+# Now it has grown up, it has bash socket support for some features which basically replacing
+# more and more functions of OpenSSL and will serve as some kind of library in the future.
+# The socket checks in bash may sound cool and unique -- they are -- but probably you
+# can achieve e.g. the same result with my favorite interactive shell: zsh (zmodload zsh/net/socket
+# -- checkout zsh/net/tcp) too!
+# /bin/bash though is way more often used within Linux and it's perfect
+# for cross platform support, see MacOS X and also under Windows the MSYS2 extension or Cygwin.
+# Cross-platform is one of the three main goals of this script. Second: Ease of installation.
+# No compiling, install gems, go to CPAN, use pip etc. Third: Easy to use and to interpret
+# the results.
+
+# Did I mention it's open source?
+
+# Q: So what's the difference to www.ssllabs.com/ssltest/ or sslcheck.globalsign.com/ ?
+# A: As of now ssllabs only check 1) webservers 2) on standard ports, 3) reachable from the
+# internet. And those examples above 4) are 3rd parties. If these restrictions are all fine
+# with you and you need a management compatible rating -- go ahead and use those.
+
+# But also if your fine with those restrictions: testssl.sh is meant as a tool in your hand
+# and it's way more flexible.
+#
+# Oh, and did I mention testssl.sh is open source?
+
+# Note that up to today there were a lot changes for "standard" openssl
+# binaries: a lot of features (ciphers, protocols, vulnerabilities)
+# are disabled as they'll impact security otherwise. For security
+# testing though we need all broken features. testssl.sh will
+# over time replace those checks with bash sockets -- however it's
+# still recommended to use the supplied binaries or cook your own, see
+# https://github.com/drwetter/testssl.sh/blob/master/bin/Readme.md .
+# Don't worry if feature X is not available you'll get a warning about
+# this missing feature! The idea is if this script can't tell something
+# for sure it speaks up so that you have clear picture.
+
+
+# debugging help:
+readonly PS4='${LINENO}> ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
+
+# make sure that temporary files are cleaned up after use in ANY case
+trap "cleanup" QUIT EXIT
+
+readonly VERSION="2.6"
+readonly SWCONTACT="dirk aet testssl dot sh"
+egrep -q "dev|rc" <<< "$VERSION" && \
+ SWURL="https://testssl.sh/dev/" ||
+ SWURL="https://testssl.sh/ "
+
+readonly PROG_NAME=$(basename "$0")
+readonly RUN_DIR=$(dirname "$0")
+INSTALL_DIR=""
+MAPPING_FILE_RFC=""
+
+which git &>/dev/null && readonly GIT_REL=$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $1" "$2" "$3 }')
+readonly CVS_REL=$(tail -5 "$0" | awk '/dirkw Exp/ { print $4" "$5" "$6}')
+readonly CVS_REL_SHORT=$(tail -5 "$0" | awk '/dirkw Exp/ { print $4 }')
+readonly SYSTEM=$(uname -s)
+date --help >/dev/null 2>&1 && \
+ readonly HAS_GNUDATE=true || \
+ readonly HAS_GNUDATE=false
+echo A | sed -E 's/A//' >/dev/null 2>&1 && \
+ readonly HAS_SED_E=true || \
+ readonly HAS_SED_E=false
+TERM_DWITH=${COLUMNS:-$(tput cols)} # for future custom line wrapping
+TERM_CURRPOS=0 # ^^^ we also need to find out the length or current pos in the line
+
+
+# following variables make use of $ENV, e.g. OPENSSL=<myprivate_path_to_openssl> ./testssl.sh <host>
+# 0 means (normally) true here. Some of the variables are also accessible with a command line switch
+
+declare -x OPENSSL
+COLOR=${COLOR:-2} # 2: Full color, 1: b/w+positioning, 0: no ESC at all
+SHOW_EACH_C=${SHOW_EACH_C:-0} # where individual ciphers are tested show just the positively ones tested #FIXME: upside down value
+SNEAKY=${SNEAKY:-false} # is the referer and useragent we leave behind just usual?
+QUIET=${QUIET:-false} # don't output the banner. By doing this yiu acknowledge usage term appearing in the banner
+SSL_NATIVE=${SSL_NATIVE:-false} # we do per default bash sockets where possible "true": switch back to "openssl native"
+ASSUMING_HTTP=${ASSUMING_HTTP:-false} # in seldom cases (WAF, old servers, grumpy SSL) service detection fails. "True" enforces HTTP checks
+DEBUG=${DEBUG:-0} # 1.: the temp files won't be erased.
+ # 2: list more what's going on (formerly: eq VERBOSE=1, VERBERR=true), lists some errors of connections
+ # 3: slight hexdumps + other info,
+ # 4: display bytes sent via sockets, 5: display bytes received via sockets, 6: whole 9 yards
+WIDE=${WIDE:-false} # whether to display for some options the cipher or the table with hexcode/KX,Enc,strength etc.
+HEADER_MAXSLEEP=${HEADER_MAXSLEEP:-5} # we wait this long before killing the process to retrieve a service banner / http header
+readonly MAX_WAITSOCK=10 # waiting at max 10 seconds for socket reply
+readonly CCS_MAX_WAITSOCK=5 # for the two CCS payload (each)
+readonly HEARTBLEED_MAX_WAITSOCK=8 # for the heartbleed payload
+STARTTLS_SLEEP=${STARTTLS_SLEEP:-1} # max time to wait on a socket replay for STARTTLS
+FAST_STARTTLS=${FAST_STARTTLS:-true} #at the cost of reliabilty decrease the handshakes for STARTTLS
+USLEEP_SND=${USLEEP_SND:-0.1} # sleep time for general socket send
+USLEEP_REC=${USLEEP_REC:-0.2} # sleep time for general socket receive
+
+CAPATH="${CAPATH:-/etc/ssl/certs/}" # Does nothing yet (FC has only a CA bundle per default, ==> openssl version -d)
+FNAME=${FNAME:-""} # file name to read commands from
+IKNOW_FNAME=false
+HSTS_MIN=${HSTS_MIN:-179} # >179 days is ok for HSTS
+HPKP_MIN=${HPKP_MIN:-30} # >=30 days should be ok for HPKP_MIN, practical hints?
+readonly CLIENT_MIN_PFS=5 # number of ciphers needed to run a test for PFS
+DAYS2WARN1=${DAYS2WARN1:-60} # days to warn before cert expires, threshold 1
+DAYS2WARN2=${DAYS2WARN2:-30} # days to warn before cert expires, threshold 2
+
+# further vars needed to follow
+readonly NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1"
+TEMPDIR=""
+TMPFILE=""
+ERRFILE=""
+HOSTCERT=""
+HEADERFILE=""
+HEADERFILE_BREACH=""
+LOGFILE=""
+PROTOS_OFFERED=""
+DETECTED_TLS_VERSION=""
+SOCKREPLY=""
+SOCK_REPLY_FILE=""
+HEXC=""
+NW_STR=""
+LEN_STR=""
+SNI=""
+OSSL_VER="" # openssl version, will be auto-determined
+OSSL_VER_MAJOR=0
+OSSL_VER_MINOR=0
+OSSL_VER_APPENDIX="none"
+HAS_DH_BITS=true
+HAS_SSL2=true #TODO: in the future we'll do the fastest possible test (openssl s_client -ssl2 is currently faster than sockets)
+HAS_SSL3=true
+PORT=443 # unless otherwise auto-determined, see below
+NODE=""
+NODEIP=""
+IPADDRs=""
+IP46ADDRs=""
+LOCAL_A=false # does the $NODEIP ceom from /etc/hosts?
+LOCAL_AAAA=false # does the IPv6 IP come from /etc/hosts?
+XMPP_HOST=""
+PROXY=""
+PROXYIP=""
+PROXYPORT=""
+VULN_COUNT=0
+readonly VULN_THRESHLD=1 # if bigger than this no we show a separate header in blue
+IPS=""
+SERVICE="" # is the server running an HTTP server, SMTP, POP or IMAP?
+URI=""
+STARTTLS_PROTOCOL=""
+OPTIMAL_PROTO="" # we need this for IIS6 (sigh) and OpenSSL 1.02, otherwise some handshakes
+ # will fail, see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
+STARTTLS_OPTIMAL_PROTO="" # same for STARTTLS, see https://github.com/drwetter/testssl.sh/issues/188
+TLS_TIME=""
+TLS_NOW=""
+HTTP_TIME=""
+GET_REQ11=""
+HEAD_REQ10=""
+readonly UA_SNEAKY="Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
+readonly UA_STD="Mozilla/5.0 (X11; Linux x86_64; rv:42.0) Gecko/19700101 Firefox/42.0"
+
+# Devel stuff, see -q below
+TLS_LOW_BYTE=""
+HEX_CIPHER=""
+
+ # The various hexdump commands we need to replace xxd (BSD compatibility))
+HEXDUMPVIEW=(hexdump -C) # This is used in verbose mode to see what's going on
+HEXDUMP=(hexdump -ve '16/1 "%02x " " \n"') # This is used to analyze the reply
+HEXDUMPPLAIN=(hexdump -ve '1/1 "%.2x"') # Replaces both xxd -p and tr -cd '[:print:]'
+
+
+
+###### some hexbytes for bash network sockets follow ######
+
+# 133 standard cipher + 4x GOST for TLS 1.2 and SPDY/NPN
+readonly TLS12_CIPHER="
+cc,14, cc,13, cc,15, c0,30, c0,2c, c0,28, c0,24, c0,14,
+c0,0a, c0,22, c0,21, c0,20, 00,a5, 00,a3, 00,a1, 00,9f,
+00,6b, 00,6a, 00,69, 00,68, 00,39, 00,38, 00,37, 00,36, 00,80, 00,81, 00,82, 00,83,
+c0,77, c0,73, 00,c4, 00,c3, 00,c2, 00,c1, 00,88, 00,87,
+00,86, 00,85, c0,32, c0,2e, c0,2a, c0,26, c0,0f, c0,05,
+c0,79, c0,75, 00,9d, 00,3d, 00,35, 00,c0, 00,84, c0,2f,
+c0,2b, c0,27, c0,23, c0,13, c0,09, c0,1f, c0,1e, c0,1d,
+00,a4, 00,a2, 00,a0, 00,9e, 00,67, 00,40, 00,3f, 00,3e,
+00,33, 00,32, 00,31, 00,30, c0,76, c0,72, 00,be, 00,bd,
+00,bc, 00,bb, 00,9a, 00,99, 00,98, 00,97, 00,45, 00,44,
+00,43, 00,42, c0,31, c0,2d, c0,29, c0,25, c0,0e, c0,04,
+c0,78, c0,74, 00,9c, 00,3c, 00,2f, 00,ba, 00,96, 00,41,
+00,07, c0,11, c0,07, 00,66, c0,0c, c0,02, 00,05, 00,04,
+c0,12, c0,08, c0,1c, c0,1b, c0,1a, 00,16, 00,13, 00,10,
+00,0d, c0,0d, c0,03, 00,0a, 00,63, 00,15, 00,12, 00,0f,
+00,0c, 00,62, 00,09, 00,65, 00,64, 00,14, 00,11, 00,0e,
+00,0b, 00,08, 00,06, 00,03, 00,ff"
+
+# 76 standard cipher +4x GOST for SSLv3, TLS 1, TLS 1.1
+readonly TLS_CIPHER="
+c0,14, c0,0a, c0,22, c0,21, c0,20, 00,39, 00,38, 00,37,
+00,36, 00,88, 00,87, 00,86, 00,85, c0,0f, c0,05, 00,35,
+00,84, c0,13, c0,09, c0,1f, c0,1e, c0,1d, 00,33, 00,32, 00,80, 00,81, 00,82, 00,83,
+00,31, 00,30, 00,9a, 00,99, 00,98, 00,97, 00,45, 00,44,
+00,43, 00,42, c0,0e, c0,04, 00,2f, 00,96, 00,41, 00,07,
+c0,11, c0,07, 00,66, c0,0c, c0,02, 00,05, 00,04, c0,12,
+c0,08, c0,1c, c0,1b, c0,1a, 00,16, 00,13, 00,10, 00,0d,
+c0,0d, c0,03, 00,0a, 00,63, 00,15, 00,12, 00,0f, 00,0c,
+00,62, 00,09, 00,65, 00,64, 00,14, 00,11, 00,0e, 00,0b,
+00,08, 00,06, 00,03, 00,ff"
+
+readonly SSLv2_CLIENT_HELLO="
+,80,34 # length (here: 52)
+,01 # Client Hello
+,00,02 # SSLv2
+,00,1b # cipher spec length (here: 27 )
+,00,00 # session ID length
+,00,10 # challenge length
+,05,00,80 # 1st cipher 9 cipher specs, only classical V2 ciphers are used here, see FIXME below
+,03,00,80 # 2nd there are v3 in v2!!! : https://tools.ietf.org/html/rfc6101#appendix-E
+,01,00,80 # 3rd Cipher specifications introduced in version 3.0 can be included in version 2.0 client hello messages using
+,07,00,c0 # 4th the syntax below. [..] # V2CipherSpec (see Version 3.0 name) = { 0x00, CipherSuite }; !!!!
+,08,00,80 # 5th
+,06,00,40 # 6th
+,04,00,80 # 7th
+,02,00,80 # 8th
+,00,00,00 # 9th
+,29,22,be,b3,5a,01,8b,04,fe,5f,80,03,a0,13,eb,c4" # Challenge
+# https://idea.popcount.org/2012-06-16-dissecting-ssl-handshake/ (client)
+# FIXME: http://max.euston.net/d/tip_sslciphers.html
+
+
+###### output functions ######
+# a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%' and '--' does the rest.
+out() { /usr/bin/printf -- "${1//%/%%}"; }
+outln() { out "$1\n"; }
+#TODO: Still no shell injection safe but if just run it from the cmd line: that's fine
+
+# color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
+pr_liteblue() { [[ "$COLOR" -eq 2 ]] && out "\033[0;34m$1" || out "$1"; pr_off; } # not yet used
+pr_liteblueln() { pr_liteblue "$1"; outln; }
+pr_blue() { [[ "$COLOR" -eq 2 ]] && out "\033[1;34m$1" || out "$1"; pr_off; } # used for head lines of single tests
+pr_blueln() { pr_blue "$1"; outln; }
+
+pr_litered() { [[ "$COLOR" -eq 2 ]] && out "\033[0;31m$1" || pr_bold "$1"; pr_off; } # this is bad
+pr_literedln() { pr_litered "$1"; outln; }
+pr_red() { [[ "$COLOR" -eq 2 ]] && out "\033[1;31m$1" || pr_bold "$1"; pr_off; } # oh, this is really bad
+pr_redln() { pr_red "$1"; outln; }
+
+pr_litemagenta() { [[ "$COLOR" -eq 2 ]] && out "\033[0;35m$1" || pr_underline "$1"; pr_off; } # local problem: one test cannot be done
+pr_litemagentaln() { pr_litemagenta "$1"; outln; }
+pr_magenta() { [[ "$COLOR" -eq 2 ]] && out "\033[1;35m$1" || pr_underline "$1"; pr_off; } # Fatal error: quitting because of this!
+pr_magentaln() { pr_magenta "$1"; outln; }
+
+pr_litecyan() { [[ "$COLOR" -eq 2 ]] && out "\033[0;36m$1" || out "$1"; pr_off; } # not yet used
+pr_litecyanln() { pr_litecyan "$1"; outln; }
+pr_cyan() { [[ "$COLOR" -eq 2 ]] && out "\033[1;36m$1" || out "$1"; pr_off; } # additional hint
+pr_cyanln() { pr_cyan "$1"; outln; }
+
+pr_litegreyln() { pr_litegrey "$1"; outln; }
+pr_litegrey() { [[ "$COLOR" -eq 2 ]] && out "\033[0;37m$1" || out "$1"; pr_off; }
+pr_grey() { [[ "$COLOR" -eq 2 ]] && out "\033[1;30m$1" || out "$1"; pr_off; }
+pr_greyln() { pr_grey "$1"; outln; }
+
+pr_litegreen() { [[ "$COLOR" -eq 2 ]] && out "\033[0;32m$1" || out "$1"; pr_off; } # This is good
+pr_litegreenln() { pr_litegreen "$1"; outln; }
+pr_green() { [[ "$COLOR" -eq 2 ]] && out "\033[1;32m$1" || out "$1"; pr_off; } # This is the best
+pr_greenln() { pr_green "$1"; outln; }
+
+pr_yellow() { [[ "$COLOR" -eq 2 ]] && out "\033[1;33m$1" || out "$1"; pr_off; } # academic or minor problem
+pr_yellowln() { pr_yellow "$1"; outln; }
+pr_brown() { [[ "$COLOR" -eq 2 ]] && out "\033[0;33m$1" || out "$1"; pr_off; } # it is not a bad problem but you shouldn't do this
+pr_brownln() { pr_brown "$1"; outln; }
+
+
+# color=1 functions
+pr_off() { [[ "$COLOR" -ne 0 ]] && out "\033[m\c"; }
+pr_bold() { [[ "$COLOR" -ne 0 ]] && out "\033[1m$1" || out "$1"; pr_off; }
+pr_boldln() { pr_bold "$1" ; outln; }
+pr_underline() { [[ "$COLOR" -ne 0 ]] && out "\033[4m$1" || out "$1"; pr_off; }
+pr_reverse() { [[ "$COLOR" -ne 0 ]] && out "\033[7m$1" || out "$1"; pr_off; }
+
+
+### colorswitcher (see e.g. https://linuxtidbits.wordpress.com/2008/08/11/output-color-on-bash-scripts/
+### http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html
+set_color_functions() {
+ local linux_tput=true
+
+ # empty vars if we have no color:
+ red=""
+ green=""
+ brown=""
+ blue=""
+ magenta=""
+ cyan=""
+ grey=""
+ yellow=""
+ off=""
+ bold=""
+ underline=""
+
+ tput sgr0 &>/dev/null || linux_tput=false
+ if [[ "$COLOR" -eq 2 ]]; then
+ if $linux_tput; then
+ red=$(tput setaf 1)
+ green=$(tput setaf 2)
+ brown=$(tput setaf 3)
+ blue=$(tput setaf 4)
+ magenta=$(tput setaf 5)
+ cyan=$(tput setaf 6)
+ grey=$(tput setaf 7)
+ yellow=$(tput setaf 3; tput bold)
+ else # this is a try for old BSD, see terminfo(5)
+ red=$(tput AF 1)
+ green=$(tput AF 2)
+ brown=$(tput AF 3)
+ blue=$(tput AF 4)
+ magenta=$(tput AF 5)
+ cyan=$(tput AF 6)
+ grey=$(tput AF 7)
+ yellow=$(tput AF 3; tput md)
+ fi
+ fi
+
+ if [[ "$COLOR" -ge 1 ]]; then
+ if $linux_tput; then
+ bold=$(tput bold)
+ underline=$(tput sgr 0 1)
+ off=$(tput sgr0)
+ else # this is a try for old BSD, see terminfo(5)
+ bold=$(tput md)
+ underline=$(tput us)
+ reverse=$(tput mr)
+ off=$(tput me)
+ fi
+ fi
+}
+
+
+###### helper function definitions ######
+
+debugme() {
+ [[ $DEBUG -ge 2 ]] && "$@"
+}
+
+hex2dec() {
+ #/usr/bin/printf -- "%d" 0x"$1"
+ echo $((16#$1))
+}
+
+dec2hex() {
+ /usr/bin/printf -- "%x" "$1"
+ #echo $((0x$1))
+}
+
+# trim spaces for BSD and old sed
+count_lines() {
+ echo "$1" | wc -l | sed 's/ //g'
+}
+count_words() {
+ echo "$1" | wc -w | sed 's/ //g'
+}
+
+count_ciphers() {
+ echo -n "$1" | sed 's/:/ /g' | wc -w | sed 's/ //g'
+}
+
+actually_supported_ciphers() {
+ $OPENSSL ciphers "$1" 2>/dev/null || echo ""
+}
+
+newline_to_spaces() {
+ echo "$1" | tr '\n' ' ' | sed 's/ $//'
+}
+
+strip_lf() {
+ echo "$1" | tr -d '\n' | tr -d '\r'
+}
+
+toupper() {
+ echo -n "$1" | tr 'a-z' 'A-Z'
+}
+
+# prints out multiple lines in $1, left aligned by spaces in $2
+out_row_aligned() {
+ local first=true
+
+ echo "$1" | while read line; do
+ if $first; then
+ first=false
+ else
+ out "$2"
+ fi
+ outln "$line"
+ done
+}
+
+
+tmpfile_handle() {
+ if [[ "$DEBUG" -eq 0 ]]; then
+ rm $TMPFILE 2>/dev/null
+ [[ $ERRFILE =~ dev.null ]] || rm $ERRFILE
+ else
+ mv $TMPFILE "$TEMPDIR/$NODEIP.$1" 2>/dev/null
+ mv $ERRFILE "$TEMPDIR/$NODEIP.$(sed 's/\.txt//g' <<<"$1").errorlog" 2>/dev/null
+ fi
+}
+
+# arg1: line with comment sign, tabs and so on
+filter_input() {
+ echo "$1" | sed -e 's/#.*$//' -e '/^$/d' | tr -d '\n' | tr -d '\t'
+}
+
+
+wait_kill(){
+ local pid=$1 # pid we wait for or kill
+ local maxsleep=$2 # how long we wait before killing
+
+ while true; do
+ [[ "$DEBUG" -ge 6 ]] && ps $pid
+ if ! ps $pid >/dev/null ; then
+ return 0 # process terminated before didn't reach $maxsleep
+ fi
+ sleep 1
+ maxsleep=$((maxsleep - 1))
+ test $maxsleep -le 0 && break
+ done # needs to be killed:
+ kill $pid >&2 2>/dev/null
+ wait $pid 2>/dev/null # make sure pid terminated, see wait(1p)
+ return 3 # means killed
+}
+
+
+###### check code starts here ######
+
+# determines whether the port has an HTTP service running or not (plain TLS, no STARTTLS)
+# arg1 could be the protocol determined as "working". IIS6 needs that
+runs_HTTP() {
+ # SNI is nonsense for !HTTPS but fortunately other protocols don't seem to care
+ printf "$GET_REQ11" | $OPENSSL s_client $1 -quiet -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>$ERRFILE &
+ wait_kill $! $HEADER_MAXSLEEP
+ head $TMPFILE | grep -aq ^HTTP && SERVICE=HTTP
+ head $TMPFILE | grep -aq SMTP && SERVICE=SMTP
+ head $TMPFILE | grep -aq POP && SERVICE=POP
+ head $TMPFILE | grep -aq IMAP && SERVICE=IMAP
+ head $TMPFILE | egrep -aqw "Jive News|InterNetNews|NNRP|INN" && SERVICE=NNTP
+ debugme head -50 $TMPFILE
+# $TMPFILE contains also a banner which we could use if there's a need for it
+
+ out " Service detected: "
+ case $SERVICE in
+ HTTP)
+ out " $SERVICE"
+ ret=0 ;;
+ IMAP|POP|SMTP|NNTP)
+ out " $SERVICE, thus skipping HTTP specific checks"
+ ret=0 ;;
+ *) out " Couldn't determine what's running on port $PORT"
+ if $ASSUMING_HTTP; then
+ SERVICE=HTTP
+ out " -- ASSUMING_HTTP set though"
+ ret=0
+ else
+ out ", assuming no HTTP service => skipping HTTP checks"
+ ret=1
+ fi
+ ;;
+ esac
+
+ outln
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+#problems not handled: chunked
+run_http_header() {
+ local header
+ local -i ret
+ local referer useragent
+ local url
+
+ outln; pr_blue "--> Testing HTTP header response"; outln " @ \"$URL_PATH\"\n"
+
+ [[ -z "$1" ]] && url="/" || url="$1"
+ if $SNEAKY; then
+ referer="http://google.com/"
+ useragent="$UA_SNEAKY"
+ else
+ referer="TLS/SSL-Tester from $SWURL"
+ useragent="$UA_STD"
+ fi
+ (
+ $OPENSSL s_client $OPTIMAL_PROTO -quiet -connect $NODEIP:$PORT $PROXY $SNI << EOF
+GET $url HTTP/1.1
+Host: $NODE
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Language: en-us,en;q=0.7,de-de;q=0.3
+User-Agent: $useragent
+Referer: $referer
+Connection: close
+
+EOF
+) >$HEADERFILE 2>$ERRFILE &
+ if wait_kill $! $HEADER_MAXSLEEP; then
+ if ! egrep -iaq "XML|HTML|DOCTYPE|HTTP|Connection" $HEADERFILE; then
+ pr_litemagenta " likely HTTP header requests failed (#lines: $(wc -l < $HEADERFILE | sed 's/ //g'))."
+ outln "Rerun with DEBUG=1 and inspect \"run_http_header.txt\"\n"
+ debugme cat $HEADERFILE
+ ret=7
+ fi
+ sed -e '/^<HTML/,$d' -e '/^<html/,$d' -e '/^<XML /,$d' -e '/<?XML /,$d' \
+ -e '/^<xml /,$d' -e '/<?xml /,$d' -e '/^<\!DOCTYPE/,$d' -e '/^<\!doctype/,$d' $HEADERFILE >$HEADERFILE.2
+#### ^^^ Attention: the filtering for the html body only as of now, doesn't work for other content yet
+ mv $HEADERFILE.2 $HEADERFILE # sed'ing in place doesn't work with BSD and Linux simultaneously
+ ret=0
+ else
+ #TODO: attention: if this runs into a timeout, we're dead. Try again differently:
+ printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI 1>$HEADERFILE 2>$ERRFILE
+ if [[ $? -ne 0 ]]; then
+ pr_litemagentaln " failed (1st request stalled, 2nd erroneous)"
+ return 3
+ #ret=3
+ fi
+ fi
+ status_code=$(awk '/^HTTP\// { print $2 }' $HEADERFILE 2>>$ERRFILE)
+ msg_thereafter=$(awk -F"$status_code" '/^HTTP\// { print $2 }' $HEADERFILE 2>>$ERRFILE) # dirty trick to use the status code as a
+ msg_thereafter=$(strip_lf "$msg_thereafter") # field separator, otherwise we need a loop with awk
+ debugme echo "Status/MSG: $status_code $msg_thereafter"
+
+ pr_bold " HTTP Status Code "
+ [[ -z "$status_code" ]] && pr_litemagentaln "No status code" && return 3
+
+ out " $status_code$msg_thereafter"
+ case $status_code in
+ 301|302|307|308) out ", redirecting to \"$(grep -a '^Location' $HEADERFILE | sed 's/Location: //' | tr -d '\r\n')\"" ;;
+ 200) ;;
+ 206) out " -- WTF?" ;;
+ 400) pr_litemagenta " (Hint: better try another URL)" ;;
+ 401) grep -aq "^WWW-Authenticate" $HEADERFILE && out " "; strip_lf "$(grep -a "^WWW-Authenticate" $HEADERFILE)"
+ ;;
+ 403) ;;
+ 404) out " (Hint: supply a path which doesn't give a \"$status_code$msg_thereafter\")" ;;
+ 405) ;;
+ *) pr_litemagenta ". Oh, didn't expect a $status_code$msg_thereafter";;
+ esac
+ outln
+
+ # we don't call "tmpfile_handle $FUNCNAME.txt" as we need the header file in other functions!
+ return $ret
+}
+
+# Borrowed from Glenn Jackman, see https://unix.stackexchange.com/users/4667/glenn-jackman
+detect_ipv4() {
+ local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
+ local ipv4address="$octet\\.$octet\\.$octet\\.$octet"
+ local your_ip_msg="(check if it's your IP address or e.g. a cluster IP)"
+ local result
+ local first=true
+ local spaces=" "
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+
+ # remove pagespeed header as it is mistakenly identified as ipv4 address https://github.com/drwetter/testssl.sh/issues/158
+ # also facebook has a CSP rule for 127.0.0.1
+ if egrep -vi "pagespeed|page-speed|Content-Security-Policy" $HEADERFILE | grep -iqE "$ipv4address"; then
+ pr_bold " IPv4 address in header "
+ while read line; do
+ result="$(grep -E "$ipv4address" <<< "$line")"
+ result=$(strip_lf "$result")
+ if [[ -n "$result" ]]; then
+ if ! $first; then
+ out "$spaces"
+ your_ip_msg=""
+ else
+ first=false
+ fi
+ pr_litered "$result"
+ outln "\n$spaces$your_ip_msg"
+ fi
+ done < $HEADERFILE
+ fi
+}
+
+
+run_http_date() {
+ local now difftime
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3 # this is just for the line "Testing HTTP header response"
+ fi
+ pr_bold " HTTP clock skew "
+ if [[ $SERVICE != "HTTP" ]]; then
+ out "not tested as we're not targeting HTTP"
+ else
+ printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO -ign_eof -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE
+ now=$(date "+%s") # we need an ACCURATE date here and cannot rely on the headerfile!
+ HTTP_TIME=$(awk -F': ' '/^date:/ { print $2 } /^Date:/ { print $2 }' $TMPFILE)
+ if [[ -n "$HTTP_TIME" ]]; then
+ if $HAS_GNUDATE ; then
+ HTTP_TIME=$(date --date="$HTTP_TIME" "+%s")
+ else
+ HTTP_TIME=$(date -j -f "%a, %d %b %Y %T %Z" "$HTTP_TIME" "+%s" 2>>$ERRFILE) # the trailing \r confuses BSD flavors otherwise
+ fi
+
+ difftime=$((HTTP_TIME - $now))
+ [[ $difftime != "-"* ]] && [[ $difftime != "0" ]] && difftime="+$difftime"
+ out "$difftime sec from localtime";
+ else
+ out "Got no HTTP time, maybe try different URL?";
+ fi
+ debugme out " epoch: $HTTP_TIME"
+ fi
+ outln
+ detect_ipv4
+}
+
+includeSubDomains() {
+ if grep -aiqw includeSubDomains "$1"; then
+ pr_litegreen ", includeSubDomains"
+ else
+ pr_litecyan ", just this domain"
+ fi
+}
+
+preload() {
+ grep -aiqw preload "$1" && pr_litegreen ", preload"
+}
+
+
+run_hsts() {
+ local hsts_age_sec
+ local hsts_age_days
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ #pr_bold " HSTS "
+ pr_bold " Strict Transport Security "
+ grep -iaw '^Strict-Transport-Security' $HEADERFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ grep -aciw '^Strict-Transport-Security' $HEADERFILE | egrep -waq "1" || out "(two HSTS header, using 1st one) "
+ hsts_age_sec=$(sed -e 's/[^0-9]*//g' $TMPFILE | head -1)
+#FIXME: test for number!
+ hsts_age_days=$(( hsts_age_sec / 86400))
+ if [[ $hsts_age_days -gt $HSTS_MIN ]]; then
+ pr_litegreen "$hsts_age_days days" ; out "=$hsts_age_sec s"
+ else
+ out "$hsts_age_sec s = "
+ pr_brown "$hsts_age_days days, <$HSTS_MIN days is too short"
+ fi
+ includeSubDomains "$TMPFILE"
+ preload "$TMPFILE"
+ #FIXME: To be checked against e.g. https://dxr.mozilla.org/mozilla-central/source/security/manager/boot/src/nsSTSPreloadList.inc
+ # and https://chromium.googlesource.com/chromium/src/+/master/net/http/transport_security_state_static.json
+ else
+ out "--"
+ fi
+ outln
+
+ tmpfile_handle $FUNCNAME.txt
+ return $?
+}
+
+
+run_hpkp() {
+ local -i hpkp_age_sec
+ local -i hpkp_age_days
+ local -i hpkp_nr_keys
+ local hpkp_key hpkp_key_hostcert
+ local spaces=" "
+ local key_found=false
+ local i
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ #pr_bold " HPKP "
+ pr_bold " Public Key Pinning "
+ egrep -aiw '^Public-Key-Pins|Public-Key-Pins-Report-Only' $HEADERFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ if egrep -aciw '^Public-Key-Pins|Public-Key-Pins-Report-Only' $HEADERFILE | egrep -waq "1" ; then
+ :
+ else
+ pr_brown "two HPKP headers: "
+ for i in $(newline_to_spaces "$(egrep -ai '^Public-Key-Pins' $HEADERFILE | awk -F':' '/Public-Key-Pins/ { print $1 }')"); do
+ pr_underline $i
+ out " "
+ done
+ out "\n$spaces using first "
+ pr_underline "$(awk -F':' '/Public-Key-Pins/ { print $1 }' $HEADERFILE | head -1), "
+ fi
+
+ # remove leading Public-Key-Pins*, any colons, double quotes and trailing spaces and taking the first -- whatever that is
+ sed -e 's/Public-Key-Pins://g' -e s'/Public-Key-Pins-Report-Only://' $TMPFILE | \
+ sed -e 's/;//g' -e 's/\"//g' -e 's/^ //' | head -1 > $TMPFILE.2
+ # BSD lacks -i, otherwise we would have done it inline
+ # now separate key value and other stuff per line:
+ tr ' ' '\n' < $TMPFILE.2 >$TMPFILE
+
+ hpkp_nr_keys=$(grep -ac pin-sha $TMPFILE)
+ out "# of keys: "
+ if [[ $hpkp_nr_keys -eq 1 ]]; then
+ pr_litered "1 (NOT ok), "
+ else
+ out "$hpkp_nr_keys, "
+ fi
+
+ # print key=value pair with awk, then strip non-numbers, to be improved with proper parsing of key-value with awk
+ hpkp_age_sec=$(awk -F= '/max-age/{max_age=$2; print max_age}' $TMPFILE | sed -E 's/[^[:digit:]]//g')
+ hpkp_age_days=$((hpkp_age_sec / 86400))
+ if [[ $hpkp_age_days -ge $HPKP_MIN ]]; then
+ pr_litegreen "$hpkp_age_days days" ; out "=$hpkp_age_sec s"
+ else
+ out "$hpkp_age_sec s = "
+ pr_brown "$hpkp_age_days days (<$HPKP_MIN days is not good enough)"
+ fi
+
+ includeSubDomains "$TMPFILE"
+ preload "$TMPFILE"
+
+ [[ -s "$HOSTCERT" ]] || get_host_cert
+ # get the key fingerprints
+ hpkp_key_hostcert="$($OPENSSL x509 -in $HOSTCERT -pubkey -noout | grep -v PUBLIC | \
+ $OPENSSL base64 -d | $OPENSSL dgst -sha256 -binary | $OPENSSL base64)"
+ while read hpkp_key; do
+ if [[ "$hpkp_key_hostcert" == "$hpkp_key" ]] || [[ "$hpkp_key_hostcert" == "$hpkp_key=" ]]; then
+ out "\n$spaces matching host key: "
+ pr_litegreen "$hpkp_key"
+ key_found=true
+ fi
+ debugme out "\n $hpkp_key | $hpkp_key_hostcert"
+ done < <(tr ';' '\n' < $TMPFILE | tr -d ' ' | tr -d '\"' | awk -F'=' '/pin.*=/ { print $2 }')
+ if ! $key_found ; then
+ out "\n$spaces"
+ pr_litered " No matching key for pins found "
+ out "(CAs pinned? -- not yet checked)"
+ fi
+ else
+ out "--"
+ fi
+ outln
+
+ tmpfile_handle $FUNCNAME.txt
+ return $?
+}
+
+emphasize_stuff_in_headers(){
+# see http://www.grymoire.com/Unix/Sed.html#uh-3
+# outln "$1" | sed "s/[0-9]*/$brown&$off/g"
+ outln "$1" | sed -e "s/\([0-9]\)/$brown\1$off/g" \
+ -e "s/Debian/"$yellow"\Debian$off/g" \
+ -e "s/Win32/"$yellow"\Win32$off/g" \
+ -e "s/Win64/"$yellow"\Win64$off/g" \
+ -e "s/Ubuntu/"$yellow"Ubuntu$off/g" \
+ -e "s/ubuntu/"$yellow"ubuntu$off/g" \
+ -e "s/jessie/"$yellow"jessie$off/g" \
+ -e "s/squeeze/"$yellow"squeeze$off/g" \
+ -e "s/wheezy/"$yellow"wheezy$off/g" \
+ -e "s/lenny/"$yellow"lenny$off/g" \
+ -e "s/SUSE/"$yellow"SUSE$off/g" \
+ -e "s/Red Hat Enterprise Linux/"$yellow"Red Hat Enterprise Linux$off/g" \
+ -e "s/Red Hat/"$yellow"Red Hat$off/g" \
+ -e "s/CentOS/"$yellow"CentOS$off/g" \
+ -e "s/Via/"$yellow"Via$off/g" \
+ -e "s/X-Forwarded/"$yellow"X-Forwarded$off/g" \
+ -e "s/Liferay-Portal/"$yellow"Liferay-Portal$off/g" \
+ -e "s/X-Cache-Lookup/"$yellow"X-Cache-Lookup$off/g" \
+ -e "s/X-Cache/"$yellow"X-Cache$off/g" \
+ -e "s/X-Squid/"$yellow"X-Squid$off/g" \
+ -e "s/X-Server/"$yellow"X-Server$off/g" \
+ -e "s/X-Varnish/"$yellow"X-Varnish$off/g" \
+ -e "s/X-OWA-Version/"$yellow"X-OWA-Version$off/g" \
+ -e "s/X-Version/"$yellow"X-Version$off/g" \
+ -e "s/X-Powered-By/"$yellow"X-Powered-By$off/g" \
+ -e "s/X-UA-Compatible/"$yellow"X-UA-Compatible$off/g" \
+ -e "s/X-AspNet-Version/"$yellow"X-AspNet-Version$off/g"
+}
+
+run_server_banner() {
+ local serverbanner
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ pr_bold " Server banner "
+ grep -ai '^Server' $HEADERFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ serverbanner=$(sed -e 's/^Server: //' -e 's/^server: //' $TMPFILE)
+ if [[ x"$serverbanner" == "x\n" ]] || [[ x"$serverbanner" == "x\n\r" ]] || [[ x"$serverbanner" == "x" ]]; then
+ outln "banner exists but empty string"
+ else
+ emphasize_stuff_in_headers "$serverbanner"
+ [[ "$serverbanner" = *Microsoft-IIS/6.* ]] && [[ $OSSL_VER == 1.0.2* ]] && \
+ pr_litemagentaln " It's recommended to run another test w/ OpenSSL 1.01 !"
+ # see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
+ fi
+ # mozilla.github.io/server-side-tls/ssl-config-generator/
+ # https://support.microsoft.com/en-us/kb/245030
+ else
+ outln "(no \"Server\" line in header, interesting!)"
+ fi
+
+ tmpfile_handle $FUNCNAME.txt
+ return 0
+}
+
+run_rp_banner() {
+ local line
+ local first=true
+ local spaces=" "
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ pr_bold " Reverse Proxy banner "
+ egrep -ai '^Via:|^X-Cache|^X-Squid|^X-Varnish:|^X-Server-Name:|^X-Server-Port:|^x-forwarded' $HEADERFILE >$TMPFILE
+ if [[ $? -ne 0 ]]; then
+ outln "--"
+ else
+ while read line; do
+ line=$(strip_lf "$line")
+ if ! $first; then
+ out "$spaces"
+ else
+ first=false
+ fi
+ emphasize_stuff_in_headers "$line"
+ done < $TMPFILE
+ fi
+ outln
+
+ tmpfile_handle $FUNCNAME.txt
+ return 0
+# emphasize_stuff_in_headers "$(sed 's/^/ /g' $TMPFILE | tr '\n\r' ' ')" || \
+}
+
+run_application_banner() {
+ local line
+ local first=true
+ local spaces=" "
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ pr_bold " Application banner "
+ egrep -ai '^X-Powered-By|^X-AspNet-Version|^X-Version|^Liferay-Portal|^X-OWA-Version' $HEADERFILE >$TMPFILE
+ if [[ $? -ne 0 ]]; then
+ outln "--"
+ else
+ cat $TMPFILE | while read line; do
+ line=$(strip_lf "$line")
+ if ! $first; then
+ out "$spaces"
+ else
+ first=false
+ fi
+ emphasize_stuff_in_headers "$line"
+ done
+ fi
+ tmpfile_handle $FUNCNAME.txt
+ return 0
+}
+
+run_cookie_flags() { # ARG1: Path, ARG2: path
+ local -i nr_cookies
+ local nr_httponly nr_secure
+ local negative_word
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ pr_bold " Cookie(s) "
+ grep -ai '^Set-Cookie' $HEADERFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ nr_cookies=$(wc -l < $TMPFILE | sed 's/ //g')
+ out "$nr_cookies issued: "
+ if [[ $nr_cookies -gt 1 ]]; then
+ negative_word="NONE"
+ else
+ negative_word="NOT"
+ fi
+ nr_secure=$(grep -iac secure $TMPFILE)
+ case $nr_secure in
+ 0) pr_brown "$negative_word" ;;
+ [123456789]) pr_litegreen "$nr_secure/$nr_cookies";;
+ esac
+ out " secure, "
+ nr_httponly=$(grep -cai httponly $TMPFILE)
+ case $nr_httponly in
+ 0) pr_brown "$negative_word" ;;
+ [123456789]) pr_litegreen "$nr_httponly/$nr_cookies";;
+ esac
+ out " HttpOnly"
+ else
+ out "(none issued at \"$1\")"
+ fi
+ outln
+
+ tmpfile_handle $FUNCNAME.txt
+ return 0
+}
+
+
+run_more_flags() {
+ local good_flags2test="X-Frame-Options X-XSS-Protection X-Content-Type-Options Content-Security-Policy X-Content-Security-Policy X-WebKit-CSP Content-Security-Policy-Report-Only"
+ local other_flags2test="Access-Control-Allow-Origin Upgrade X-Served-By X-UA-Compatible"
+ local egrep_pattern=""
+ local f2t result_str
+ local first=true
+ local spaces=" "
+
+ if [[ ! -s $HEADERFILE ]]; then
+ run_http_header "$1" || return 3
+ fi
+ pr_bold " Security headers "
+ # convert spaces to | (for egrep)
+ egrep_pattern=$(echo "$good_flags2test $other_flags2test"| sed -e 's/ /|\^/g' -e 's/^/\^/g')
+ egrep -ai "$egrep_pattern" $HEADERFILE >$TMPFILE
+ if [[ $? -ne 0 ]]; then
+ outln "--"
+ ret=1
+ else
+ #set -x
+ ret=0
+ for f2t in $good_flags2test; do
+ debugme echo "---> $f2t"
+ result_str=$(grep -wi "^$f2t" $TMPFILE | grep -vi "$f2t"-)
+ result_str=$(strip_lf "$result_str")
+ [[ -z "$result_str" ]] && continue
+ if ! $first; then
+ out "$spaces" # output leading spaces if the first header
+ else
+ first=false
+ fi
+ # extract and print key(=flag) in green:
+ pr_litegreen "${result_str%%:*}:"
+ #pr_litegreen "$(sed 's/:.*$/:/' <<< "$result_str")"
+ # print value in plain text:
+ outln "${result_str#*:}"
+
+ done
+ # now the same with other flags
+ for f2t in $other_flags2test; do
+ result_str=$(grep -i "^$f2t" $TMPFILE)
+ [[ -z "$result_str" ]] && continue
+ if ! $first; then
+ out "$spaces" # output leading spaces if the first header
+ else
+ first=false
+ fi
+ # extract and print key(=flag) underlined
+ pr_underline "${result_str%%:*}:"
+ # print value in plain text:
+ outln "${result_str#*:}"
+ done
+ fi
+#TODO: I am not testing for the correctness or anything stupid yet, e.g. "X-Frame-Options: allowall"
+
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+
+# #1: string with 2 opensssl codes, HEXC= same in NSS/ssllabs terminology
+normalize_ciphercode() {
+ part1=$(echo "$1" | awk -F',' '{ print $1 }')
+ part2=$(echo "$1" | awk -F',' '{ print $2 }')
+ part3=$(echo "$1" | awk -F',' '{ print $3 }')
+ if [[ "$part1" == "0x00" ]]; then # leading 0x00
+ HEXC=$part2
+ else
+ #part2=$(echo $part2 | sed 's/0x//g')
+ part2=${part2//0x/}
+ if [[ -n "$part3" ]]; then # a SSLv2 cipher has three parts
+ #part3=$(echo $part3 | sed 's/0x//g')
+ part3=${part3//0x/}
+ fi
+ HEXC="$part1$part2$part3"
+ fi
+#TODO: we should just echo this and avoid the global var HEXC
+ HEXC=$(echo $HEXC | tr 'A-Z' 'a-z' | sed 's/0x/x/') #tolower + strip leading 0
+ return 0
+}
+
+prettyprint_local() {
+ local arg
+ local hexcode dash ciph sslvers kx auth enc mac export
+ local re='^[0-9A-Fa-f]+$'
+
+ pr_blue "--> Displaying all local ciphers ";
+ if [[ -n "$1" ]]; then
+ [[ $1 =~ $re ]] && \
+ pr_blue "matching number pattern \"$1\" " || \
+ pr_blue "matching word pattern "\"$1\"" (ignore case)"
+ fi
+ outln "\n"
+ neat_header
+
+ if [[ -z "$1" ]]; then
+ $OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE | while read hexcode dash ciph sslvers kx auth enc mac export ; do # -V doesn't work with openssl < 1.0
+ normalize_ciphercode $hexcode
+ neat_list $HEXC $ciph $kx $enc
+ outln
+ done
+ else
+ #for arg in $(echo $@ | sed 's/,/ /g'); do
+ for arg in ${*//,/ /}; do
+ $OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE | while read hexcode dash ciph sslvers kx auth enc mac export ; do # -V doesn't work with openssl < 1.0
+ normalize_ciphercode $hexcode
+ # for numbers we don't do word matching:
+ [[ $arg =~ $re ]] && \
+ neat_list $HEXC $ciph $kx $enc | grep -ai "$arg" || \
+ neat_list $HEXC $ciph $kx $enc | grep -wai "$arg"
+ done
+ done
+ fi
+ outln
+ return 0
+}
+
+
+# list ciphers (and makes sure you have them locally configured)
+# arg[1]: cipher list (or anything else)
+listciphers() {
+ local -i ret
+ local debugname="$(sed -e s'/\!/not/g' -e 's/\:/_/g' <<< "$1")"
+
+ $OPENSSL ciphers "$1" &>$TMPFILE
+ ret=$?
+ debugme cat $TMPFILE
+
+ tmpfile_handle $FUNCNAME.$debugname.txt
+ return $ret
+}
+
+
+# argv[1]: cipher list to test
+# argv[2]: string on console
+# argv[3]: ok to offer? 0: yes, 1: no
+std_cipherlists() {
+ local -i ret
+ local singlespaces
+ local debugname="$(sed -e s'/\!/not/g' -e 's/\:/_/g' <<< "$1")"
+
+ pr_bold "$2 " # indent in order to be in the same row as server preferences
+ if listciphers "$1"; then # is that locally available??
+ $OPENSSL s_client -cipher "$1" $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI 2>$ERRFILE >$TMPFILE </dev/null
+ ret=$?
+ debugme cat $ERRFILE
+ case $3 in
+ 0) # ok to offer
+ [[ $ret -eq 0 ]] && \
+ pr_greenln "offered (OK)" || \
+ pr_brownln "not offered (NOT ok)" ;;
+ 1) # the ugly ones
+ [[ $ret -eq 0 ]] && \
+ pr_redln "offered (NOT ok)" || \
+ pr_greenln "not offered (OK)" ;;
+ 2) # bad but not worst
+ [[ $ret -eq 0 ]] && \
+ pr_literedln "offered (NOT ok)" || \
+ pr_litegreenln "not offered (OK)" ;;
+ 3) # not totally bad
+ [[ $ret -eq 0 ]] && \
+ pr_brownln "offered (NOT ok)" || \
+ outln "not offered (OK)" ;;
+ *) # we shouldn't reach this
+ pr_litemagenta "? (please report this)" ;;
+ esac
+ tmpfile_handle $FUNCNAME.$debugname.txt
+ else
+ singlespaces=$(echo "$2" | sed -e 's/ \+/ /g' -e 's/^ //' -e 's/ $//g' -e 's/ //g')
+ local_problem "No $singlespaces configured in $OPENSSL"
+ fi
+ # we need lf in those cases:
+ [[ $DEBUG -ge 2 ]] && echo
+}
+
+
+# sockets inspired by http://blog.chris007.de/?p=238
+# ARG1: hexbyte with a leading comma (!!), separated by commas
+# ARG2: sleep
+socksend() {
+ # the following works under BSD and Linux, which is quite tricky. So don't mess with it unless you're really sure what you do
+ if $HAS_SED_E; then
+ data=$(echo "$1" | sed -e 's/# .*$//g' -e 's/ //g' | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//; /^$/d' | sed 's/,/\\/g' | tr -d '\n')
+ else
+ data=$(echo "$1" | sed -e 's/# .*$//g' -e 's/ //g' | sed -r 's/^[[:space:]]+//; s/[[:space:]]+$//; /^$/d' | sed 's/,/\\/g' | tr -d '\n')
+ fi
+ [[ $DEBUG -ge 4 ]] && echo "\"$data\""
+ printf -- "$data" >&5 2>/dev/null &
+ sleep $2
+}
+
+
+#FIXME: This is only for HB and CCS, others use still sockread_serverhello()
+sockread() {
+ local -i ret=0
+ local ddreply
+
+ [[ "x$2" == "x" ]] && maxsleep=$MAX_WAITSOCK || maxsleep=$2
+
+ ddreply=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
+ dd bs=$1 of=$ddreply count=1 <&5 2>/dev/null &
+ wait_kill $! $maxsleep
+ ret=$?
+ SOCKREPLY=$(cat $ddreply)
+ rm $ddreply
+ return $ret
+}
+
+
+show_rfc_style(){
+ local rfcname
+
+ [[ -z "$MAPPING_FILE_RFC" ]] && return 1
+ rfcname=$(grep -iw "$1" "$MAPPING_FILE_RFC" | sed -e 's/^.*TLS/TLS/' -e 's/^.*SSL/SSL/')
+ [[ -n "$rfcname" ]] && out "$rfcname"
+ return 0
+}
+
+neat_header(){
+ printf -- "Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits${MAPPING_FILE_RFC:+ Cipher Suite Name (RFC)}\n"
+ printf -- "%s-------------------------------------------------------------------------${MAPPING_FILE_RFC:+----------------------------------------------}\n"
+}
+
+
+# arg1: hexcode
+# arg2: cipher in openssl notation
+# arg3: keyexchange
+# arg4: encryption (maybe included "export")
+neat_list(){
+ local hexcode="$1"
+ local ossl_cipher="$2"
+ local kx enc strength
+
+ kx=$(sed 's/Kx=//g' <<< "$3")
+ enc=$(sed 's/Enc=//g' <<< "$4")
+ strength=$(sed -e 's/.*(//' -e 's/)//' <<< "$enc") # strength = encryption bits
+ strength=$(sed -e 's/ChaCha20-Poly1305/ly1305/g' <<< "$strength") # workaround for empty bits ChaCha20-Poly1305
+ enc=$(sed -e 's/(.*)//g' -e 's/ChaCha20-Poly1305/ChaCha20-Po/g' <<< "$enc") # workaround for empty bits ChaCha20-Poly1305
+ echo "$export" | grep -iq export && strength="$strength,export"
+ # workaround for color escape codes:
+ if printf -- "$kx" | "${HEXDUMPVIEW[@]}" | grep -q 33 ; then # here's a color code
+ kx="$kx " # one for color code if ECDH and three digits
+ [[ "${#kx}" -eq 18 ]] && kx="$kx " # 18 means DH, colored < 1000. Add another space
+ [[ "${#kx}" -eq 19 ]] && kx="$kx " # 19 means DH, colored >=1000. Add another space
+ #echo ${#kx} # should be always 20
+ fi
+ #if [[ -r "$MAPPING_FILE_RFC" ]]; then
+ printf -- " %-7s %-30s %-10s %-11s%-11s${MAPPING_FILE_RFC:+ %-48s}${SHOW_EACH_C:+ }" "$hexcode" "$ossl_cipher" "$kx" "$enc" "$strength" "$(show_rfc_style $HEXC)"
+ #else
+ # printf -- " %-7s %-30s %-10s %-11s%-11s${SHOW_EACH_C:+ }" "$1" "$2" "$kx" "$enc" "$strength"
+ #fi
+}
+
+test_just_one(){
+ local hexcode n ciph sslvers kx auth enc mac export
+ local dhlen
+ local ret
+ local re='^[0-9A-Fa-f]+$'
+
+ pr_blue "--> Testing single cipher with "
+ [[ $1 =~ $re ]] && \
+ pr_blue "matching number pattern \"$1\" " || \
+ pr_blue "word pattern "\"$1\"" (ignore case)"
+ outln
+ ! $HAS_DH_BITS && pr_litemagentaln " (Your $OPENSSL cannot show DH/ECDH bits)"
+ outln
+ neat_header
+ #for arg in $(echo $@ | sed 's/,/ /g'); do
+ for arg in ${*//, /}; do
+ # 1st check whether openssl has cipher or not
+ $OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE | while read hexcode dash ciph sslvers kx auth enc mac export ; do
+ # FIXME: e.g. OpenSSL < 1.0 doesn't understand "-V" --> we can't do anything about it!
+ normalize_ciphercode $hexcode
+ # is argument a number?
+ if [[ $arg =~ $re ]]; then
+ neat_list $HEXC $ciph $kx $enc | grep -qai "$arg"
+ else
+ neat_list $HEXC $ciph $kx $enc | grep -qwai "$arg"
+ fi
+ if [[ $? -eq 0 ]]; then # string matches, so we can ssl to it:
+ $OPENSSL s_client -cipher $ciph $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ if [[ $kx == "Kx=ECDH" ]] || [[ $kx == "Kx=DH" ]] || [[ $kx == "Kx=EDH" ]]; then
+ if [[ $ret -eq 0 ]]; then
+ dhlen=$(read_dhbits_from_file $TMPFILE quiet)
+ kx="$kx $dhlen"
+ else
+ kx="$kx$grey TBD $off "
+ fi
+ fi
+ neat_list $HEXC $ciph "$kx" $enc
+ if [[ $ret -eq 0 ]]; then
+ pr_cyan " available"
+ else
+ out " not a/v"
+ fi
+ outln
+ fi
+ done
+ done
+ outln
+
+ tmpfile_handle $FUNCNAME.txt
+ return 0 # this is a single test for a cipher
+}
+
+
+# test for all ciphers locally configured (w/o distinguishing whether they are good or bad
+run_allciphers(){
+ local tmpfile
+ local nr_ciphers
+ local -i ret=0
+ local hexcode n ciph sslvers kx auth enc mac export
+ local dhlen
+
+ nr_ciphers=$(count_ciphers "$($OPENSSL ciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE)")
+ outln
+ pr_blue "--> Testing all locally available $nr_ciphers ciphers against the server"; outln ", ordered by encryption strength"
+ ! $HAS_DH_BITS && pr_litemagentaln " (Your $OPENSSL cannot show DH/ECDH bits)"
+ outln
+ neat_header
+
+ $OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>>$ERRFILE | while read hexcode n ciph sslvers kx auth enc mac export; do
+ # FIXME: e.g. OpenSSL < 1.0 doesn't understand "-V" --> we can't do anything about it!
+ $OPENSSL s_client -cipher $ciph $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ if [[ $ret -ne 0 ]] && [[ "$SHOW_EACH_C" -eq 0 ]]; then
+ continue # no successful connect AND not verbose displaying each cipher
+ fi
+ normalize_ciphercode $hexcode
+ if [[ $kx == "Kx=ECDH" ]] || [[ $kx == "Kx=DH" ]] || [[ $kx == "Kx=EDH" ]]; then
+ dhlen=$(read_dhbits_from_file $TMPFILE quiet)
+ kx="$kx $dhlen"
+ fi
+ neat_list $HEXC $ciph "$kx" $enc
+ if [[ "$SHOW_EACH_C" -ne 0 ]]; then
+ if [[ $ret -eq 0 ]]; then
+ pr_cyan " available"
+ else
+ out " not a/v"
+ fi
+ fi
+ outln
+ tmpfile_handle $FUNCNAME.txt
+ done
+ outln
+ return 0
+}
+
+# test for all ciphers per protocol locally configured (w/o distinguishing whether they are good or bad
+run_cipher_per_proto(){
+ local proto proto_text
+ local hexcode n ciph sslvers kx auth enc mac export
+ local -i ret=0
+ local dhlen
+
+ pr_blue "--> Testing all locally available ciphers per protocol against the server"; outln ", ordered by encryption strength"
+ ! $HAS_DH_BITS && pr_litemagentaln " (Your $OPENSSL cannot show DH/ECDH bits)"
+ outln
+ neat_header
+ outln " -ssl2 SSLv2\n -ssl3 SSLv3\n -tls1 TLS 1\n -tls1_1 TLS 1.1\n -tls1_2 TLS 1.2"| while read proto proto_text; do
+ locally_supported "$proto" "$proto_text" || continue
+ outln
+ $OPENSSL ciphers $proto -V 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE | while read hexcode n ciph sslvers kx auth enc mac export; do # -V doesn't work with openssl < 1.0
+ $OPENSSL s_client -cipher $ciph $proto $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ if [[ $ret -ne 0 ]] && [[ "$SHOW_EACH_C" -eq 0 ]]; then
+ continue # no successful connect AND not verbose displaying each cipher
+ fi
+ normalize_ciphercode $hexcode
+ if [[ $kx == "Kx=ECDH" ]] || [[ $kx == "Kx=DH" ]] || [[ $kx == "Kx=EDH" ]]; then
+ dhlen=$(read_dhbits_from_file $TMPFILE quiet)
+ kx="$kx $dhlen"
+ fi
+ neat_list $HEXC $ciph "$kx" $enc
+ if [[ "$SHOW_EACH_C" -ne 0 ]]; then
+ if [[ $ret -eq 0 ]]; then
+ pr_cyan " available"
+ else
+ out " not a/v"
+ fi
+ fi
+ outln
+ tmpfile_handle $FUNCNAME.txt
+ done
+ done
+ return 0
+}
+
+locally_supported() {
+ local -i ret=0
+
+ [[ -n "$2" ]] && out "$2 "
+ $OPENSSL s_client "$1" 2>&1 | grep -aq "unknown option"
+ if [[ $? -eq 0 ]]; then
+ local_problem "$OPENSSL doesn't support \"s_client $1\""
+ ret=7
+ fi
+ return $ret
+}
+
+
+run_prototest_openssl() {
+ local sni="$SNI"
+ local -i ret=0
+
+ $OPENSSL s_client -state $1 $STARTTLS -connect $NODEIP:$PORT $PROXY $sni &>$TMPFILE </dev/null
+ ret=$?
+# FIXME: here FreeBSD9/openssl 0.9.8 returns always 0 --> need to read the error but for now we DO NOT SUPPORT this platform.
+# that's where the binaries are for!
+ [[ $DEBUG -eq 2 ]] && egrep "error|failure" $TMPFILE | egrep -av "unable to get local|verify error"
+
+ if ! locally_supported "$1" "$2" ; then
+ ret=7
+ else # we remove SNI for SSLv2 and v3:
+ [[ "$1" =~ ssl ]] && sni="" # newer openssl throw an error if SNI is supplied with SSLv2,
+ # SSLv3 doesn't have SNI (openssl doesn't complain though -- yet)
+ $OPENSSL s_client -state $1 $STARTTLS -connect $NODEIP:$PORT $sni &>$TMPFILE </dev/null
+ ret=$? #TODO (maybe): here FreeBSD9 returns always 0 --> need to read the error
+ [[ $DEBUG -eq 2 ]] && egrep "error|failure" $TMPFILE | egrep -av "unable to get local|verify error"
+ grep -aq "no cipher list" $TMPFILE && ret=5
+ fi
+
+ tmpfile_handle $FUNCNAME$1.txt
+ return $ret
+
+ # 0: offered
+ # 1: not offered
+ # 5: protocol ok, but no cipher
+ # 7: no local support
+}
+
+
+run_protocols() {
+ local using_sockets=true
+ local supported_no_ciph1="supported but couldn't detect a cipher (may need debugging)"
+ local supported_no_ciph2="supported but couldn't detect a cipher"
+
+ outln; pr_blue "--> Testing protocols ";
+
+ #FIXME: use PROTOS_OFFERED here
+
+ if $SSL_NATIVE; then
+ using_sockets=false
+ outln "(via native openssl)\n"
+ else
+ if [[ -n "$STARTTLS" ]]; then
+ outln "(via openssl, SSLv2 via sockets)\n"
+ using_sockets=false
+ else
+ using_sockets=true
+ outln "(via sockets except TLS 1.2 and SPDY/NPN)\n"
+ fi
+ fi
+
+ pr_bold " SSLv2 ";
+ if ! $SSL_NATIVE; then
+ sslv2_sockets #FIXME: messages need to be moved to this higher level
+ else
+ run_prototest_openssl "-ssl2"
+ case $? in
+ 0) pr_redln "offered (NOT ok)" ;;
+ 1) pr_greenln "not offered (OK)" ;;
+ 5) pr_litered "$supported_no_ciph2";
+ outln " (may need further attention)" ;; # protocol ok, but no cipher
+ 7) ;; # no local support
+ esac
+ fi
+
+ pr_bold " SSLv3 ";
+ if $using_sockets; then
+ tls_sockets "00" "$TLS_CIPHER"
+ else
+ run_prototest_openssl "-ssl3"
+ fi
+ case $? in
+ 0) pr_literedln "offered (NOT ok)" ;;
+ 1) pr_greenln "not offered (OK)" ;;
+ 2) pr_litemagentaln "#FIXME: downgraded. still missing a test case here" ;;
+ 5) pr_litered "$supported_no_ciph2";
+ outln "(may need debugging)" ;; # protocol ok, but no cipher
+ 7) ;; # no local support
+ esac
+
+ pr_bold " TLS 1 ";
+ if $using_sockets; then
+ tls_sockets "01" "$TLS_CIPHER"
+ else
+ run_prototest_openssl "-tls1"
+ fi
+ case $? in
+ 0) outln "offered" ;; # nothing wrong with it -- per se
+ 1) outln "not offered" ;; # neither good or bad
+ 2) pr_brown "not offered (NOT ok)"
+ [[ $DEBUG -eq 1 ]] && out " -- downgraded"
+ outln ;;
+ 5) outln "$supported_no_ciph1" ;; # protocol ok, but no cipher
+ 7) ;; # no local support
+ esac
+
+ pr_bold " TLS 1.1 ";
+ if $using_sockets; then
+ tls_sockets "02" "$TLS_CIPHER"
+ else
+ run_prototest_openssl "-tls1_1"
+ fi
+ case $? in
+ 0) outln "offered" ;; # nothing wrong with it
+ 1) outln "not offered" ;; # neither good or bad
+ 2) out "not offered"
+ [[ $DEBUG -eq 1 ]] && out " -- downgraded"
+ outln ;;
+ 5) outln "$supported_no_ciph1" ;; # protocol ok, but no cipher
+ 7) ;; # no local support
+ esac
+
+ pr_bold " TLS 1.2 ";
+ if $using_sockets && [[ $EXPERIMENTAL == "yes" ]]; then #TODO: IIS servers do have a problem here with our handshake
+ tls_sockets "03" "$TLS12_CIPHER"
+ else
+ run_prototest_openssl "-tls1_2"
+ fi
+ case $? in
+ 0) pr_greenln "offered (OK)" ;; # GCM cipher in TLS 1.2: very good!
+ 1) pr_brownln "not offered (NOT ok)" ;; # no GCM, penalty
+ 2) pr_brown "not offered (NOT ok)"
+ [[ $DEBUG -eq 1 ]] && out " -- downgraded"
+ outln ;;
+ 5) outln "$supported_no_ciph1" ;; # protocol ok, but no cipher
+ 7) ;; # no local support
+ esac
+
+ return 0
+}
+
+#TODO: work with a fixed list here
+run_std_cipherlists() {
+ outln
+ pr_blue "--> Testing ~standard cipher lists"; outln "\n"
+# see ciphers(1ssl)
+ std_cipherlists 'NULL:eNULL' " Null Ciphers " 1
+ std_cipherlists 'aNULL' " Anonymous NULL Ciphers " 1
+ std_cipherlists 'ADH' " Anonymous DH Ciphers " 1
+ std_cipherlists 'EXPORT40' " 40 Bit encryption " 1
+ std_cipherlists 'EXPORT56' " 56 Bit encryption " 1
+ std_cipherlists 'EXPORT' " Export Ciphers (general) " 1
+ std_cipherlists 'LOW:!ADH' " Low (<=64 Bit) " 1
+ std_cipherlists 'DES:!ADH:!EXPORT:!aNULL' " DES Ciphers " 1
+ std_cipherlists 'MEDIUM:!NULL:!aNULL:!SSLv2' " Medium grade encryption " 2
+ std_cipherlists '3DES:!ADH:!aNULL' " Triple DES Ciphers " 3
+ std_cipherlists 'HIGH:!NULL:!aNULL:!DES:!3DES' " High grade encryption " 0
+ return 0
+}
+
+
+# arg1: file with input for grepping the bit length for ECDH/DHE
+# arg2: whether to print sparse or not (empty: no)
+read_dhbits_from_file() {
+ local bits what_dh
+ local add=""
+ local old_fart=" (openssl cannot show DH bits)"
+
+ bits=$(awk -F': ' '/^Server Temp Key/ { print $2 }' "$1") # extract line
+ bits=$(echo "$bits" | sed -e 's/, P-...//' -e 's/,//g' -e 's/bits//' -e 's/ //g') # now: ??DH [number] K??
+ what_dh=$(echo "$bits" | tr -d '0-9')
+ bits=$(echo $bits | tr -d 'DHEC')
+
+ debugme echo ">$what_dh|$bits<"
+
+ if ! $HAS_DH_BITS && [[ -z "what_dh" ]]; then
+ if [[ -z "$2" ]]; then
+ pr_litemagenta "$old_fart"
+ fi
+ return 0
+ fi
+
+ [[ -n "$bits" ]] && [[ -z "$2" ]] && out ", "
+ if [[ $what_dh == "DH" ]] || [[ $what_dh == "EDH" ]]; then
+ [[ -z "$2" ]] && add="bit DH"
+ if [[ "$bits" -le 600 ]]; then
+ pr_red "$bits $add"
+ elif [[ "$bits" -le 800 ]]; then
+ pr_litered "$bits $add"
+ elif [[ "$bits" -le 1280 ]]; then
+ pr_brown "$bits $add"
+ elif [[ "$bits" -ge 2048 ]]; then
+ pr_litegreen "$bits $add"
+ else
+ out "$bits $add"
+ fi
+ # https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography, http://www.keylength.com/en/compare/
+ elif [[ $what_dh == "ECDH" ]]; then
+ [[ -z "$2" ]] && add="bit ECDH"
+ if [[ "$bits" -le 128 ]]; then # has that ever existed?
+ pr_red "$bits $add"
+ elif [[ "$bits" -le 163 ]]; then
+ pr_litered "$bits $add"
+ elif [[ "$bits" -ge 224 ]]; then
+ pr_litegreen "$bits $add"
+ else
+ out "$bits $add"
+ fi
+ fi
+
+ return 0
+}
+
+
+run_server_preference() {
+ local cipher1 cipher2
+ local default_cipher default_proto
+ local remark4default_cipher
+ local -a cipher proto
+ local p i
+ local -i ret=0
+ local list_fwd="DES-CBC3-SHA:RC4-MD5:DES-CBC-SHA:RC4-SHA:AES128-SHA:AES128-SHA256:AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-AES256-SHA:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:AES256-SHA256"
+ # now reversed offline via tac, see https://github.com/thomassa/testssl.sh/commit/7a4106e839b8c3033259d66697893765fc468393 :
+ local list_reverse="AES256-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384DHE-DSS-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA:ECDH-RSA-AES128-SHA:ECDH-RSA-DES-CBC3-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:AES256-SHA:AES128-SHA256:AES128-SHA:RC4-SHA:DES-CBC-SHA:RC4-MD5:DES-CBC3-SHA"
+ local has_cipher_order=true
+
+ outln;
+ pr_blue "--> Testing server preferences"; outln "\n"
+
+ pr_bold " Has server cipher order? "
+ $OPENSSL s_client $STARTTLS -cipher $list_fwd -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>$ERRFILE >$TMPFILE
+ if [[ $? -ne 0 ]] && [[ -z "$STARTTLS_PROTOCOL" ]]; then
+ pr_litemagenta "no matching cipher in this list found (pls report this): "
+ outln "$list_fwd . "
+ has_cipher_order=false
+ ret=6
+ elif [[ -n "$STARTTLS_PROTOCOL" ]]; then
+ # now it still could be that we hit this bug: https://github.com/drwetter/testssl.sh/issues/188
+ # workaround is to connect with a protocol
+ debugme out "(workaround #188) "
+ determine_optimal_proto $STARTTLS_PROTOCOL
+ $OPENSSL s_client $STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_fwd -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>$ERRFILE >$TMPFILE
+ if [[ $? -ne 0 ]]; then
+ pr_litemagenta "no matching cipher in this list found (pls report this): "
+ outln "$list_fwd . "
+ has_cipher_order=false
+ ret=6
+ fi
+ fi
+
+ if $has_cipher_order; then
+ cipher1=$(grep -wa Cipher $TMPFILE | egrep -avw "New|is" | sed -e 's/^ \+Cipher \+://' -e 's/ //g')
+ $OPENSSL s_client $STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_reverse -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
+ # that worked above so no error handling here
+ cipher2=$(grep -wa Cipher $TMPFILE | egrep -avw "New|is" | sed -e 's/^ \+Cipher \+://' -e 's/ //g')
+
+ if [[ "$cipher1" != "$cipher2" ]]; then
+ pr_litered "nope (NOT ok)"
+ remark4default_cipher=" (limited sense as client will pick)"
+ else
+ pr_green "yes (OK)"
+ remark4default_cipher=""
+ fi
+ [[ $DEBUG -ge 2 ]] && out " $cipher1 | $cipher2"
+ outln
+
+ pr_bold " Negotiated protocol "
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
+ if [[ $? -ne 0 ]]; then
+ # 2 second try with $OPTIMAL_PROTO especially for intolerant IIS6 servers:
+ $OPENSSL s_client $STARTTLS $OPTIMAL_PROTO -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
+ [[ $? -ne 0 ]] && pr_litemagenta "Handshake error!"
+ fi
+ default_proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g')
+ case "$default_proto" in
+ *TLSv1.2) pr_greenln $default_proto ;;
+ *TLSv1.1) pr_litegreenln $default_proto ;;
+ *TLSv1) outln $default_proto ;;
+ *SSLv2) pr_redln $default_proto ;;
+ *SSLv3) pr_redln $default_proto ;;
+ "") pr_litemagenta "default proto empty"; [[ $OSSL_VER == 1.0.2* ]] && outln " (Hint: if IIS6 give OpenSSL 1.01 a try)" ;;
+ *) pr_litemagenta "FIXME line $LINENO: $default_proto" ;;
+ esac
+
+ pr_bold " Negotiated cipher "
+ default_cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ case "$default_cipher" in
+ *NULL*|*EXP*) pr_red "$default_cipher" ;;
+ *RC4*) pr_litered "$default_cipher" ;;
+ *CBC*) pr_brown "$default_cipher" ;; # FIXME BEAST: We miss some CBC ciphers here, need to work w/ a list
+ *GCM*) pr_green "$default_cipher" ;; # best ones
+ *CHACHA20*) pr_green "$default_cipher" ;; # best ones
+ ECDHE*AES*) pr_yellow "$default_cipher" ;; # it's CBC. --> lucky13
+ "") pr_litemagenta "default cipher empty" ; [[ $OSSL_VER == 1.0.2* ]] && out " (Hint: if IIS6 give OpenSSL 1.01 a try)" ;;
+ *) out "$default_cipher" ;;
+ esac
+ read_dhbits_from_file "$TMPFILE"
+ outln "$remark4default_cipher"
+
+ if [[ ! -z "$remark4default_cipher" ]]; then
+ pr_bold " Negotiated cipher per proto"; outln " $remark4default_cipher"
+ i=1
+ for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do
+ #locally_supported -"$p" " " || continue
+ locally_supported -"$p" || continue
+ $OPENSSL s_client $STARTTLS -"$p" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ proto[i]=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g')
+ cipher[i]=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ [[ ${cipher[i]} == "0000" ]] && cipher[i]="" # Hack!
+ [[ $DEBUG -ge 2 ]] && outln "Default cipher for ${proto[i]}: ${cipher[i]}"
+ else
+ proto[i]=""
+ cipher[i]=""
+ fi
+ i=$(($i + 1))
+ done
+
+ [[ -n "$PROXY" ]] && arg=" SPDY/NPN is"
+ [[ -n "$STARTTLS" ]] && arg=" "
+ if spdy_pre " $arg"; then # is NPN/SPDY supported and is this no STARTTLS? / no PROXY
+ $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg "$NPN_PROTOs" </dev/null 2>>$ERRFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ proto[i]=$(grep -aw "Next protocol" $TMPFILE | sed -e 's/^Next protocol://' -e 's/(.)//' -e 's/ //g')
+ if [[ -z "${proto[i]}" ]]; then
+ cipher[i]=""
+ else
+ cipher[i]=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ [[ $DEBUG -ge 2 ]] && outln "Default cipher for ${proto[i]}: ${cipher[i]}"
+ fi
+ fi
+ else
+ outln # we miss for STARTTLS 1x LF otherwise
+ fi
+
+ for i in 1 2 3 4 5 6; do
+ if [[ -n "${cipher[i]}" ]]; then # cipher not empty
+ if [[ -z "${cipher[i-1]}" ]]; then # previous one empty
+ #outln
+ printf -- " %-30s %s" "${cipher[i]}:" "${proto[i]}" # print out both
+ else # previous NOT empty
+ if [[ "${cipher[i-1]}" == "${cipher[i]}" ]]; then # and previous protocol same cipher
+ out ", ${proto[i]}" # same cipher --> only print out protocol behind it
+ else
+ outln
+ printf -- " %-30s %s" "${cipher[i]}:" "${proto[i]}" # print out both
+ fi
+ fi
+ fi
+ done
+ fi
+ fi
+
+ tmpfile_handle $FUNCNAME.txt
+ if [[ -z "$remark4default_cipher" ]]; then
+ cipher_pref_check
+ else
+ outln "\n No further cipher order check has been done as order is determined by the client"
+ fi
+ return 0
+}
+
+cipher_pref_check() {
+ local p proto protos
+ local tested_cipher cipher
+
+ pr_bold " Cipher order"
+
+ for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do
+ $OPENSSL s_client $STARTTLS -"$p" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>$ERRFILE >$TMPFILE
+ if [[ $? -eq 0 ]]; then
+ tested_cipher=""
+ proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g')
+ cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ [[ -z "$proto" ]] && continue # for early openssl versions sometimes needed
+ outln
+ printf " %-10s %s " "$proto:" "$cipher"
+ tested_cipher="-"$cipher
+ while true; do
+ $OPENSSL s_client $STARTTLS -"$p" -cipher "ALL:$tested_cipher" -connect $NODEIP:$PORT $PROXY $SNI </dev/null 2>>$ERRFILE >$TMPFILE
+ [[ $? -ne 0 ]] && break
+ cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ out "$cipher "
+ tested_cipher="$tested_cipher:-$cipher"
+ done
+ fi
+ done
+ outln
+
+ if ! spdy_pre " SPDY/NPN: " ; then # is NPN/SPDY supported and is this no STARTTLS?
+ outln
+ else
+ protos=$($OPENSSL s_client -host $NODE -port $PORT -nextprotoneg \"\" </dev/null 2>>$ERRFILE | grep -a "^Protocols " | sed -e 's/^Protocols.*server: //' -e 's/,//g')
+ for p in $protos; do
+ $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg "$p" $PROXY </dev/null 2>>$ERRFILE >$TMPFILE
+ cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ printf " %-10s %s " "$p:" "$cipher"
+ tested_cipher="-"$cipher
+ while true; do
+ $OPENSSL s_client -cipher "ALL:$tested_cipher" -host $NODE -port $PORT -nextprotoneg "$p" $PROXY </dev/null 2>>$ERRFILE >$TMPFILE
+ [[ $? -ne 0 ]] && break
+ cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')
+ out "$cipher "
+ tested_cipher="$tested_cipher:-$cipher"
+ done
+ outln
+ done
+ fi
+
+ tmpfile_handle $FUNCNAME.txt
+ return 0
+}
+
+
+get_host_cert() {
+ # arg1 is proto or empty
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI $1 2>/dev/null </dev/null | \
+ awk '/-----BEGIN/,/-----END/ { print $0 }' >$HOSTCERT
+ return $((${PIPESTATUS[0]} + ${PIPESTATUS[1]}))
+}
+
+get_all_certs() {
+ local savedir
+ local nrsaved
+ local -i ret
+
+ $OPENSSL s_client -showcerts $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI 2>$ERRFILE </dev/null >$TEMPDIR/allcerts.txt
+ ret=$?
+ savedir=$(pwd); cd $TEMPDIR
+ # http://backreference.org/2010/05/09/ocsp-verification-with-openssl/
+ awk -v n=-1 '/-----BEGIN CERTIFICATE-----/{ inc=1; n++ }
+ inc { print > ("level" n ".crt") }
+ /---END CERTIFICATE-----/{ inc=0 }' $TEMPDIR/allcerts.txt
+ nrsaved=$(count_words "$(echo level?.crt 2>/dev/null)")
+ cd "$savedir"
+
+ echo $nrsaved
+ return $ret
+}
+
+
+tls_time() {
+ local now difftime
+
+ tls_sockets "01" "$TLS_CIPHER" # try first TLS 1.0 (mostfrequently used protocol)
+ [[ -z "$TLS_TIME" ]] && tls_sockets "03" "$TLS12_CIPHER" # TLS 1.2
+ [[ -z "$TLS_TIME" ]] && tls_sockets "02" "$TLS_CIPHER" # TLS 1.1
+ [[ -z "$TLS_TIME" ]] && tls_sockets "00" "$TLS_CIPHER" # SSL 3
+
+ if [[ -n "$TLS_TIME" ]]; then # nothing returned a time!
+ difftime=$(($TLS_TIME - $TLS_NOW)) # TLS_NOW is being set in tls_sockets()
+ if [[ "${#difftime}" -gt 5 ]]; then
+ # openssl >= 1.0.1f fills this field with random values! --> good for possible fingerprint
+ pr_bold " TLS timestamp" ; outln " random values, no fingerprinting possible "
+ else
+ [[ $difftime != "-"* ]] && [[ $difftime != "0" ]] && difftime="+$difftime"
+ pr_bold " TLS clock skew" ; outln " $difftime sec from localtime";
+ fi
+ debugme out "$TLS_TIME"
+ outln
+ else
+ pr_bold " TLS timestamp" ; outln " "; pr_litemagentaln "SSLv3 through TLS 1.2 didn't return a timestamp"
+ fi
+}
+
+run_server_defaults() {
+ local proto
+ local gost_status_problem=false
+ local extensions
+ local sessticket_str lifetime unit keysize sig_algo key_algo
+ local expire secs2warn ocsp_uri crl startdate enddate issuer_c issuer_o issuer sans san cn cn_nosni
+ local policy_oid
+ local spaces=" "
+ local wildcard=false
+
+ outln
+ pr_blue "--> Testing server defaults (Server Hello)"; outln "\n"
+
+ #TLS extensions follow now
+ # throwing 1st every cipher/protocol at the server to know what works
+ for proto in tls1_2 tls1_1 tls1 ssl3; do
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug -status </dev/null 2>$ERRFILE >$TMPFILE
+ ret=$?
+ get_host_cert "-$proto"
+ [[ $? -eq 0 ]] && [[ $ret -eq 0 ]] && break
+ ret=7
+ done # this loop is needed for IIS/6
+ if [[ $ret -eq 7 ]]; then
+ # "-status" above doesn't work for GOST only servers, so we do another test without it and see whether that works then:
+ if ! $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug </dev/null 2>>$ERRFILE >$TMPFILE; then
+ pr_litemagentaln "Strange, no SSL/TLS protocol seems to be supported (error around line $((LINENO - 6)))"
+ tmpfile_handle tlsextdebug+status.txt
+ return 7 # this is ugly, I know
+ else
+ gost_status_problem=true
+ fi
+ fi
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY -$proto 2>>$ERRFILE </dev/null | awk '/-----BEGIN/,/-----END/ { print $0 }' >$HOSTCERT.nosni
+ pr_bold " TLS server extensions "
+ extensions=$(grep -aw "^TLS server extension" $TMPFILE | sed -e 's/^TLS server extension \"//' -e 's/\".*$/,/g')
+ if [[ -z "$extensions" ]]; then
+ outln "(none)"
+ else
+ echo $extensions | sed 's/,$//' # remove last comma
+ fi
+
+ pr_bold " Session Tickets RFC 5077 "
+ sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime)
+ if [[ -z "$sessticket_str" ]]; then
+ outln "(none)"
+ else
+ lifetime=$(echo $sessticket_str | grep -a lifetime | sed 's/[A-Za-z:() ]//g')
+ unit=$(echo $sessticket_str | grep -a lifetime | sed -e 's/^.*'"$lifetime"'//' -e 's/[ ()]//g')
+ outln "$lifetime $unit"
+ fi
+
+ pr_bold " Server key size "
+ keysize=$(grep -aw "^Server public key is" $TMPFILE | sed -e 's/^Server public key is //' -e 's/bit//' -e 's/ //')
+ sig_algo=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep "Signature Algorithm" | sed 's/^.*Signature Algorithm: //' | sort -u )
+ key_algo=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | awk -F':' '/Public Key Algorithm:/ { print $2 }' | sort -u )
+
+ if [[ -z "$keysize" ]]; then
+ outln "(couldn't determine)"
+ else
+ if [[ "$keysize" -le 768 ]]; then
+ if [[ $sig_algo =~ ecdsa ]] || [[ $key_algo =~ ecPublicKey ]]; then
+ pr_litegreen "EC $keysize"
+ else
+ pr_red "$keysize"
+ fi
+ elif [[ "$keysize" -le 1024 ]]; then
+ pr_brown "$keysize"
+ elif [[ "$keysize" -le 2048 ]]; then
+ out "$keysize"
+ elif [[ "$keysize" -le 4096 ]]; then
+ pr_litegreen "$keysize"
+ else
+ out "weird keysize: $keysize"
+ fi
+ fi
+ outln " bit"
+
+ pr_bold " Signature Algorithm "
+ case $sig_algo in
+ sha1WithRSAEncryption) pr_brownln "SHA1 with RSA" ;;
+ sha256WithRSAEncryption) pr_litegreenln "SHA256 with RSA" ;;
+ sha512WithRSAEncryption) pr_litegreenln "SHA512 with RSA" ;;
+ ecdsa-with-SHA256) pr_litegreenln "ECDSA with SHA256" ;;
+ md5*) pr_redln "MD5" ;;
+ *) outln "$algo" ;;
+ esac
+ # old, but interesting: https://blog.hboeck.de/archives/754-Playing-with-the-EFF-SSL-Observatory.html
+
+ pr_bold " Fingerprint / Serial "
+ outln "$($OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha1 2>>$ERRFILE | sed 's/Fingerprint=//' | sed 's/://g' ) / $($OPENSSL x509 -noout -in $HOSTCERT -serial 2>>$ERRFILE | sed 's/serial=//')"
+ outln "$spaces$($OPENSSL x509 -noout -in $HOSTCERT -fingerprint -sha256 2>>$ERRFILE | sed 's/Fingerprint=//' | sed 's/://g' )"
+
+ pr_bold " Common Name (CN) "
+ if $OPENSSL x509 -in $HOSTCERT -noout -subject 2>>$ERRFILE | grep -wq CN; then
+ cn=$($OPENSSL x509 -in $HOSTCERT -noout -subject 2>>$ERRFILE | sed 's/subject= //' | sed -e 's/^.*CN=//' -e 's/\/emailAdd.*//')
+ pr_underline "$cn"
+ if echo -n "$cn" | grep -q '^*.' ; then
+ out " (wildcard certificate"
+ if [[ "$cn" == "*.$(echo -n "$cn" | sed 's/^\*.//')" ]]; then
+ out " match)"
+ wildcard=true
+ else
+ :
+ #FIXME: we need to test also the SANs as they can contain a wild card (google.de .e.g) ==> 2.7dev
+ fi
+ fi
+ else
+ cn="(no CN field in subject)"
+ out "$cn"
+ fi
+
+ cn_nosni=""
+ if [[ -s $HOSTCERT.nosni ]]; then
+ if $OPENSSL x509 -in $HOSTCERT.nosni -noout -subject 2>>$ERRFILE | grep -wq CN; then
+ cn_nosni=$($OPENSSL x509 -in $HOSTCERT.nosni -noout -subject 2>>$ERRFILE | sed 's/subject= //' | sed -e 's/^.*CN=//' -e 's/\/emailAdd.*//')
+ else
+ cn_nosni="no CN field in subject"
+ fi
+ fi
+
+ debugme out "\"$NODE\" | \"$cn\" | \"$cn_nosni\""
+ if [[ $NODE == "$cn_nosni" ]]; then
+ if [[ $SERVICE == "HTTP" ]]; then
+ outln " (works w/o SNI)"
+ else
+ outln " (matches certificate directly)"
+ # for services != HTTP it depends on the protocol, server and client but it is not named "SNI"
+ fi
+ else
+ if [[ $SERVICE != "HTTP" ]]; then
+ outln
+ #pr_brownln " (non-SNI clients don't match CN but for non-HTTP services it might be ok)"
+ #FIXME: this is irritating and needs to be redone. Then also the wildcard match needs to be tested against "$cn_nosni"
+ elif [[ -z "$cn_nosni" ]]; then
+ out " (request w/o SNI didn't succeed";
+ [[ $algo =~ ecdsa ]] && out ", usual for EC certificates"
+ outln ")"
+ elif [[ "$cn_nosni" == "*no CN field*" ]]; then
+ outln ", (request w/o SNI: $cn_nosni)"
+ else
+ out " (CN in response to request w/o SNI: "; pr_underline "$cn_nosni"; outln ")"
+ fi
+ fi
+
+ sans=$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A3 "Subject Alternative Name" | grep "DNS:" | \
+ sed -e 's/DNS://g' -e 's/ //g' -e 's/,/ /g' -e 's/othername:<unsupported>//g')
+# ^^^ CACert
+
+ pr_bold " subjectAltName (SAN) "
+ if [[ -n "$sans" ]]; then
+ for san in $sans; do
+ out "$underline$san$off "
+ done
+ else
+ out "-- "
+ fi
+ outln
+ pr_bold " Issuer "
+ issuer=$($OPENSSL x509 -in $HOSTCERT -noout -issuer 2>>$ERRFILE| sed -e 's/^.*CN=//g' -e 's/\/.*$//g')
+ issuer_o=$($OPENSSL x509 -in $HOSTCERT -noout -issuer 2>>$ERRFILE | sed 's/^.*O=//g' | sed 's/\/.*$//g')
+ if $OPENSSL x509 -in $HOSTCERT -noout -issuer 2>>$ERRFILE | grep -q 'C=' ; then
+ issuer_c=$($OPENSSL x509 -in $HOSTCERT -noout -issuer 2>>$ERRFILE | sed 's/^.*C=//g' | sed 's/\/.*$//g')
+ else
+ issuer_c="" # CACert would have 'issuer= ' here otherwise
+ fi
+ if [[ "$issuer_o" == "issuer=" ]] || [[ "$issuer_o" == "issuer= " ]] || [[ "$issuer" == "$CN" ]]; then
+ pr_redln "selfsigned (not OK)"
+ else
+ [[ "$issuer_c" == "" ]] && \
+ outln "$underline$issuer$off ($underline$issuer_o$off)" || \
+ outln "$underline$issuer$off ($underline$issuer_o$off from $underline$issuer_c$off)"
+ fi
+
+ # http://events.ccc.de/congress/2010/Fahrplan/attachments/1777_is-the-SSLiverse-a-safe-place.pdf, see page 40pp
+ pr_bold " EV cert"; out " (experimental) "
+ policy_oid=$($OPENSSL x509 -in $HOSTCERT -text 2>>$ERRFILE | awk '/ .Policy: / { print $2 }')
+ if echo "$issuer" | egrep -q 'Extended Validation|Extended Validated|EV SSL|EV CA' || \
+ [[ "2.16.840.1.114028.10.1.2" == "$policy_oid" ]] || \
+ [[ 2.16.840.1.114412.1.3.0.2 == "$policy_oid" ]] || \
+ [[ 2.16.840.1.114412.2.1 == "$policy_oid" ]] || \
+ [[ 2.16.578.1.26.1.3.3 == "$policy_oid" ]] || \
+ [[ 1.3.6.1.4.1.17326.10.14.2.1.2 == "$policy_oid" ]] || \
+ [[ 1.3.6.1.4.1.17326.10.8.12.1.2 == "$policy_oid" ]] || \
+ [[ 1.3.6.1.4.1.13177.10.1.3.10 == "$policy_oid" ]] ; then
+ out "yes "
+ else
+ out "no "
+ fi
+ debugme echo "($(newline_to_spaces "$policy_oid"))"
+ outln
+#TODO: use browser OIDs:
+# https://mxr.mozilla.org/mozilla-central/source/security/certverifier/ExtendedValidation.cpp
+# http://src.chromium.org/chrome/trunk/src/net/cert/ev_root_ca_metadata.cc
+# https://certs.opera.com/03/ev-oids.xml
+
+ pr_bold " Certificate Expiration "
+ expire=$($OPENSSL x509 -in $HOSTCERT -checkend 0 2>>$ERRFILE)
+ if ! echo $expire | grep -qw not; then
+ pr_red "expired!"
+ else
+ secs2warn=$((24 * 60 * 60 * DAYS2WARN2)) # low threshold first
+ expire=$($OPENSSL x509 -in $HOSTCERT -checkend $secs2warn 2>>$ERRFILE)
+ if echo "$expire" | grep -qw not; then
+ secs2warn=$((24 * 60 * 60 * DAYS2WARN1))
+ expire=$($OPENSSL x509 -in $HOSTCERT -checkend $secs2warn 2>>$ERRFILE)
+ if echo "$expire" | grep -qw not; then
+ pr_litegreen ">= $DAYS2WARN1 days"
+ else
+ pr_brown "expires < $DAYS2WARN1 days"
+ fi
+ else
+ pr_litered "expires < $DAYS2WARN2 days!"
+ fi
+ fi
+
+ if $HAS_GNUDATE ; then
+ enddate=$(date --date="$($OPENSSL x509 -in $HOSTCERT -noout -enddate 2>>$ERRFILE | cut -d= -f 2)" +"%F %H:%M %z")
+ startdate=$(date --date="$($OPENSSL x509 -in $HOSTCERT -noout -startdate 2>>$ERRFILE | cut -d= -f 2)" +"%F %H:%M")
+ else
+ enddate=$(date -j -f "%b %d %T %Y %Z" "$($OPENSSL x509 -in $HOSTCERT -noout -enddate 2>>$ERRFILE | cut -d= -f 2)" +"%F %H:%M %z")
+ startdate=$(date -j -f "%b %d %T %Y %Z" "$($OPENSSL x509 -in $HOSTCERT -noout -startdate 2>>$ERRFILE | cut -d= -f 2)" +"%F %H:%M")
+ fi
+
+ outln " ($startdate --> $enddate)"
+
+
+ pr_bold " # of certificates provided"; outln " $(get_all_certs)"
+
+ pr_bold " Certificate Revocation List "
+ crl="$($OPENSSL x509 -in $HOSTCERT -noout -text 2>>$ERRFILE | grep -A 4 "CRL Distribution" | grep URI | sed 's/^.*URI://')"
+ case $(count_lines "$crl") in
+ 0) pr_literedln "--" ;;
+ 1) outln "$crl" ;;
+ *) out_row_aligned "$crl" "$spaces" ;;
+ esac
+
+ pr_bold " OCSP URI "
+ ocsp_uri=$($OPENSSL x509 -in $HOSTCERT -noout -ocsp_uri 2>>$ERRFILE)
+ [[ x"$ocsp_uri" == "x" ]] && pr_literedln "--" || echo "$ocsp_uri"
+
+ pr_bold " OCSP stapling "
+ if grep "OCSP response" $TMPFILE | grep -q "no response sent" ; then
+ out " not offered"
+ else
+ if grep "OCSP Response Status" $TMPFILE | grep -q successful; then
+ pr_litegreen " offered"
+ else
+ if $gost_status_problem; then
+ outln " (GOST servers make problems here, sorry)"
+ ret=0
+ else
+ outln " not sure what's going on here, debug:"
+ grep -A 20 "OCSP response" $TMPFILE
+ ret=2
+ fi
+ fi
+ fi
+ outln
+
+ # if we call tls_time before tmpfile_handle it throws an error because the function tls_sockets removed $TMPFILE
+ # already -- and that was a different one -- means that would get overwritten anyway
+ tmpfile_handle tlsextdebug+status.txt
+
+ tls_time
+
+ return $ret
+}
+# FIXME: revoked, see checkcert.sh
+# FIXME: Trust (only CN)
+
+
+# http://www.heise.de/security/artikel/Forward-Secrecy-testen-und-einrichten-1932806.html
+run_pfs() {
+ local ret ret2
+ local -i pfs_offered=1
+ local tmpfile
+ local dhlen
+ local hexcode dash pfs_cipher sslvers kx auth enc mac
+ # https://community.qualys.com/blogs/securitylabs/2013/08/05/configuring-apache-nginx-and-openssl-for-forward-secrecy -- but with RC4:
+ #local pfs_ciphers='EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA256 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EDH+aRSA EECDH RC4 !RC4-SHA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS:@STRENGTH'
+ #w/o RC4:
+ #local pfs_ciphers='EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA256 EECDH+aRSA+SHA256 EDH+aRSA EECDH !RC4-SHA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS:@STRENGTH'
+#
+# hardcoded: (the exclusion via ! doesn't work with libressl and openssl 0.9.8) and it's reproducible
+ local pfs_cipher_list="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-CAMELLIA128-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-CAMELLIA128-SHA256:DHE-RSA-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA"
+ local -i no_supported_ciphers=0
+
+ outln
+ pr_blue "--> Testing (perfect) forward secrecy, (P)FS"; outln " -- omitting 3DES, RC4 and Null Encryption here"
+ ! $HAS_DH_BITS && $WIDE && pr_litemagentaln " (Your $OPENSSL cannot show DH/ECDH bits)"
+
+ no_supported_ciphers=$(count_ciphers $(actually_supported_ciphers $pfs_cipher_list))
+ if [[ "$no_supported_ciphers" -le "$CLIENT_MIN_PFS" ]]; then
+ outln
+ local_problem "You only have $number_pfs PFS ciphers on the client side "
+ return 1
+ fi
+
+ $OPENSSL s_client -cipher 'ECDH:DH' $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ outln
+ if [[ $ret -ne 0 ]] || [[ $(grep -ac "BEGIN CERTIFICATE" $TMPFILE) -eq 0 ]]; then
+ pr_brownln "Not OK: No ciphers supporting Forward Secrecy offered"
+ else
+ pfs_offered=0
+ pr_litegreen " PFS is offered (OK)"
+ if $WIDE; then
+ outln ", ciphers follow (client/browser support is here specially important) \n"
+ neat_header
+ else
+ out " "
+ fi
+ while read hexcode dash pfs_cipher sslvers kx auth enc mac; do
+ tmpfile=$TMPFILE.$hexcode
+ $OPENSSL s_client -cipher $pfs_cipher $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI &>$tmpfile </dev/null
+ ret2=$?
+ if [[ $ret2 -ne 0 ]] && [[ "$SHOW_EACH_C" -eq 0 ]]; then
+ continue # no successful connect AND not verbose displaying each cipher
+ fi
+ if $WIDE; then
+ normalize_ciphercode $hexcode
+ if [[ $kx == "Kx=ECDH" ]] || [[ $kx == "Kx=DH" ]] || [[ $kx == "Kx=EDH" ]]; then
+ dhlen=$(read_dhbits_from_file "$tmpfile" quiet)
+ kx="$kx $dhlen"
+ fi
+ neat_list $HEXC $pfs_cipher "$kx" $enc $strength
+ let "pfs_offered++"
+ if [[ "$SHOW_EACH_C" -ne 0 ]]; then
+ if [[ $ret2 -eq 0 ]]; then
+ pr_green "works"
+ else
+ out "not a/v"
+ fi
+ fi
+ outln
+ else
+ out "$pfs_cipher "
+ fi
+ done < <($OPENSSL ciphers -V "$pfs_cipher_list" 2>$ERRFILE) # -V doesn't work with openssl < 1.0
+ # ^^^^^ posix redirect as shopt will either segfault or doesn't work with old bash versions
+ debugme echo $pfs_offered
+
+ if [[ "$pfs_offered" -eq 1 ]]; then
+ pr_brown "no PFS ciphers found"
+ fi
+ fi
+ outln
+ $WIDE && outln
+
+ debugme echo $(actually_supported_ciphers $pfs_cipher_list)
+ debugme echo $no_supported_ciphers
+
+ tmpfile_handle $FUNCNAME.txt
+ return $pfs_offered
+}
+
+
+# good source for configuration and bugs: https://wiki.mozilla.org/Security/Server_Side_TLS
+# good start to read: http://en.wikipedia.org/wiki/Transport_Layer_Security#Attacks_against_TLS.2FSSL
+
+
+spdy_pre(){
+ if [[ ! -z "$STARTTLS" ]]; then
+ [[ -n "$1" ]] && out "$1"
+ out "(SPDY is a HTTP protocol and thus not tested here)"
+ return 1
+ fi
+ if [[ ! -z "$PROXY" ]]; then
+ [[ -n "$1" ]] && pr_litemagenta "$1 "
+ pr_litemagenta "not tested as proxies do not support proxying it"
+ return 1
+ fi
+ # first, does the current openssl support it?
+ $OPENSSL s_client help 2>&1 | grep -qw nextprotoneg
+ if [[ $? -ne 0 ]]; then
+ local_problem "$OPENSSL doesn't support SPDY/NPN";
+ return 7
+ fi
+ return 0
+}
+
+run_spdy() {
+ local tmpstr
+ local -i ret=0
+
+ pr_bold " SPDY/NPN "
+ if ! spdy_pre ; then
+ echo
+ return 0
+ fi
+ $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg $NPN_PROTOs </dev/null 2>$ERRFILE >$TMPFILE
+ tmpstr=$(grep -a '^Protocols' $TMPFILE | sed 's/Protocols.*: //')
+ if [[ -z "$tmpstr" ]] || [[ "$tmpstr" == " " ]]; then
+ out "not offered"
+ ret=1
+ else
+ # now comes a strange thing: "Protocols advertised by server:" is empty but connection succeeded
+ if echo $tmpstr | egrep -aq "spdy|http" ; then
+ out "$tmpstr" ; out " (advertised)"
+ ret=0
+ else
+ pr_litemagenta "please check manually, server response was ambigious ..."
+ ret=10
+ fi
+ fi
+ outln
+ # btw: nmap can do that too http://nmap.org/nsedoc/scripts/tls-nextprotoneg.html
+ # nmap --script=tls-nextprotoneg #NODE -p $PORT is your friend if your openssl doesn't want to test this
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+
+# arg1: string to send
+# arg2: possible success strings a egrep pattern, needed!
+starttls_line() {
+ debugme echo -e "\n=== sending \"$1\" ..."
+ echo -e "$1" >&5
+
+ # we don't know how much to read and it's blocking! So we just put a cat into the
+ # background and read until $STARTTLS_SLEEP and: cross our fingers
+ cat <&5 >$TMPFILE &
+ wait_kill $! $STARTTLS_SLEEP
+ debugme echo "... received result: "
+ debugme cat $TMPFILE
+ if [[ -n "$2" ]]; then
+ if egrep -q "$2" $TMPFILE; then
+ debugme echo "---> reply matched \"$2\""
+ else
+ debugme echo "---> reply didn't match \"$2\", see $TMPFILE"
+ pr_magenta "STARTTLS handshake problem. "
+ outln "Either switch to native openssl (--ssl-native), "
+ outln " give the server more time to reply (STARTTLS_SLEEP=<seconds> ./testssh.sh ..) -- "
+ outln " or debug what happened (add --debug=2)"
+ exit -3
+ fi
+ fi
+
+ return 0
+}
+
+starttls_just_send(){
+ debugme echo -e "\n=== sending \"$1\" ..."
+ echo -e "$1" >&5
+}
+
+starttls_just_read(){
+ debugme echo "=== just read banner ==="
+ if [[ "$DEBUG" -ge 2 ]]; then
+ cat <&5 &
+ wait_kill $! $STARTTLS_SLEEP
+ else
+ dd of=/dev/null count=8 <&5 2>/dev/null &
+ wait_kill $! $STARTTLS_SLEEP
+ fi
+
+ return 0
+}
+
+
+# arg for a fd doesn't work here
+fd_socket() {
+ local jabber=""
+ local proyxline=""
+
+ if [[ -n "$PROXY" ]]; then
+ if ! exec 5<> /dev/tcp/${PROXYIP}/${PROXYPORT}; then
+ outln
+ pr_magenta "$PROG_NAME: unable to open a socket to proxy $PROXYIP:$PROXYPORT"
+ return 6
+ fi
+ echo "CONNECT $NODEIP:$PORT" >&5
+ while true ; do
+ read proyxline <&5
+ if [[ "${proyxline%/*}" == "HTTP" ]]; then
+ proyxline=${proyxline#* }
+ if [[ "${proyxline%% *}" != "200" ]]; then
+ [[ "$PORT" != 443 ]] && outln "Check whether your proxy supports port $PORT and the underlying protocol."
+ pr_magenta "Unable to CONNECT via proxy. "
+ return 6
+ fi
+ fi
+ if [[ "$proyxline" == $'\r' ]]; then
+ break
+ fi
+ done
+ elif ! exec 5<>/dev/tcp/$NODEIP/$PORT; then # 2>/dev/null would remove an error message, but disables debugging
+ outln
+ pr_magenta "Unable to open a socket to $NODEIP:$PORT. "
+ # It can last ~2 minutes but for for those rare occasions we don't do a timeout handler here, KISS
+ return 6
+ fi
+
+ if [[ -n "$STARTTLS" ]]; then
+ case "$STARTTLS_PROTOCOL" in # port
+ ftp|ftps) # https://tools.ietf.org/html/rfc4217
+ $FAST_STARTTLS || starttls_just_read
+ $FAST_STARTTLS || starttls_line "FEAT" "211" && starttls_just_send "FEAT"
+ starttls_line "AUTH TLS" "successful|234"
+ ;;
+ smtp|smtps) # SMTP, see https://tools.ietf.org/html/rfc4217
+ $FAST_STARTTLS || starttls_just_read
+ $FAST_STARTTLS || starttls_line "EHLO testssl.sh" "220|250" && starttls_just_send "EHLO testssl.sh"
+ starttls_line "STARTTLS" "220"
+ ;;
+ pop3|pop3s) # POP, see https://tools.ietf.org/html/rfc2595
+ $FAST_STARTTLS || starttls_just_read
+ starttls_line "STLS" "OK"
+ ;;
+ nntp|nntps) # NNTP, see https://tools.ietf.org/html/rfc4642
+ $FAST_STARTTLS || starttls_just_read
+ $FAST_STARTTLS || starttls_line "CAPABILITIES" "101|200" && starttls_just_send "CAPABILITIES"
+ starttls_line "STARTTLS" "382"
+ ;;
+ imap|imaps) # IMAP, https://tools.ietf.org/html/rfc2595
+ $FAST_STARTTLS || starttls_just_read
+ $FAST_STARTTLS || starttls_line "a001 CAPABILITY" "OK" && starttls_just_send "a001 CAPABILITY"
+ starttls_line "a002 STARTTLS" "OK"
+ ;;
+ ldap|ldaps) # LDAP, https://tools.ietf.org/html/rfc2830, https://tools.ietf.org/html/rfc4511
+ fatal "FIXME: LDAP+STARTTLS over sockets not yet supported (try \"--ssl-native\")" -4
+ ;;
+ acap|acaps) # ACAP = Application Configuration Access Protocol, see https://tools.ietf.org/html/rfc2595
+ fatal "ACAP Easteregg: not implemented -- probably never will" -4
+ ;;
+ xmpp|xmpps) # XMPP, see https://tools.ietf.org/html/rfc6120
+ starttls_just_read
+ [[ -z $XMPP_HOST ]] && XMPP_HOST="$NODE"
+ jabber=$(cat <<EOF
+<?xml version='1.0' ?>
+<stream:stream
+xmlns:stream='http://etherx.jabber.org/streams'
+xmlns='jabber:client'
+to='$XMPP_HOST'
+xml:lang='en'
+version='1.0'>
+EOF
+)
+ starttls_line "$jabber"
+ starttls_line "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" "proceed"
+ # BTW: https://xmpp.net !
+ ;;
+ *) # we need to throw an error here -- otherwise testssl.sh treats the STARTTLS protocol as plain SSL/TLS which leads to FP
+ fatal "FIXME: STARTTLS protocol $STARTTLS_PROTOCOL is not yet supported" -4
+ esac
+ fi
+
+ return 0
+}
+
+
+close_socket(){
+ exec 5<&-
+ exec 5>&-
+ return 0
+}
+
+
+# first: helper function for protocol checks
+code2network() {
+ # arg1: formatted string here in the code
+ NW_STR=$(echo "$1" | sed -e 's/,/\\\x/g' | sed -e 's/# .*$//g' -e 's/ //g' -e '/^$/d' | tr -d '\n' | tr -d '\t')
+ #TODO: just echo, no additional global var
+}
+
+len2twobytes() {
+ local len_arg1=${#1}
+ [[ $len_arg1 -le 2 ]] && LEN_STR=$(printf "00, %02s \n" "$1")
+ [[ $len_arg1 -eq 3 ]] && LEN_STR=$(printf "%02s, %02s \n" "${1:0:1}" "${1:1:2}")
+ [[ $len_arg1 -eq 4 ]] && LEN_STR=$(printf "%02s, %02s \n" "${1:0:2}" "${1:2:2}")
+}
+
+socksend_sslv2_clienthello() {
+ local data=""
+
+ code2network "$1"
+ data="$NW_STR"
+ [[ "$DEBUG" -ge 4 ]] && echo "\"$data\""
+ printf -- "$data" >&5 2>/dev/null &
+ sleep $USLEEP_SND
+}
+
+# for SSLv2 to TLS 1.2:
+sockread_serverhello() {
+ [[ -z "$2" ]] && maxsleep=$MAX_WAITSOCK || maxsleep=$2
+
+ SOCK_REPLY_FILE=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7
+ dd bs=$1 of=$SOCK_REPLY_FILE count=1 <&5 2>/dev/null &
+ wait_kill $! $maxsleep
+
+ return $?
+}
+
+# arg1: name of file with socket reply
+parse_sslv2_serverhello() {
+ # server hello: in hex representation, see below
+ # byte 1+2: length of server hello 0123
+ # 3: 04=Handshake message, server hello 45
+ # 4: session id hit or not (boolean: 00=false, this 67
+ # is the normal case)
+ # 5: certificate type, 01 = x509 89
+ # 6+7 version (00 02 = SSLv2) 10-13
+ # 8+9 certificate length 14-17
+ # 10+11 cipher spec length 17-20
+ # 12+13 connection id length
+ # [certificate length] ==> certificate
+ # [cipher spec length] ==> ciphers GOOD: HERE ARE ALL CIPHERS ALREADY!
+
+ local ret=3
+
+ v2_hello_ascii=$(hexdump -v -e '16/1 "%02X"' $1)
+ [[ "$DEBUG" -ge 5 ]] && echo "$v2_hello_ascii"
+ if [[ -z "$v2_hello_ascii" ]]; then
+ ret=0 # 1 line without any blanks: no server hello received
+ debugme echo "server hello empty"
+ else
+ # now scrape two bytes out of the reply per byte
+ v2_hello_initbyte="${v2_hello_ascii:0:1}" # normally this belongs to the next, should be 8!
+ v2_hello_length="${v2_hello_ascii:1:3}" # + 0x8000 see above
+ v2_hello_handshake="${v2_hello_ascii:4:2}"
+ v2_hello_cert_length="${v2_hello_ascii:14:4}"
+ v2_hello_cipherspec_length="${v2_hello_ascii:18:4}"
+
+ V2_HELLO_CIPHERSPEC_LENGTH=$(printf "%d\n" "0x$v2_hello_cipherspec_length" 2>/dev/null)
+ [[ $? -ne 0 ]] && ret=7
+
+ if [[ $v2_hello_initbyte != "8" ]] || [[ $v2_hello_handshake != "04" ]]; then
+ ret=1
+ if [[ $DEBUG -ge 2 ]]; then
+ echo "no correct server hello"
+ echo "SSLv2 server init byte: 0x0$v2_hello_initbyte"
+ echo "SSLv2 hello handshake : 0x$v2_hello_handshake"
+ fi
+ fi
+
+ if [[ $DEBUG -ge 3 ]]; then
+ echo "SSLv2 server hello length: 0x0$v2_hello_length"
+ echo "SSLv2 certificate length: 0x$v2_hello_cert_length"
+ echo "SSLv2 cipher spec length: 0x$v2_hello_cipherspec_length"
+ fi
+ fi
+ return $ret
+}
+
+
+# arg1: name of file with socket reply
+parse_tls_serverhello() {
+ local tls_hello_ascii=$(hexdump -v -e '16/1 "%02X"' "$1")
+ local tls_content_type tls_protocol tls_len_all
+#TODO: all vars here
+
+ TLS_TIME=""
+ DETECTED_TLS_VERSION=""
+
+ # server hello, handshake details see http://en.wikipedia.org/wiki/Transport_Layer_Security-SSL#TLS_record
+ # byte 0: content type: 0x14=CCS, 0x15=TLS alert x16=Handshake, 0x17 Aplication, 0x18=HB
+ # byte 1+2: TLS version word, major is 03, minor 00=SSL3, 01=TLS1 02=TLS1.1 03=TLS 1.2
+ # byte 3+4: length all
+ # byte 5: handshake type (2=hello) TLS alert: level (2=fatal), descr (0x28=handshake failure)
+ # byte 6+7+8: length server hello
+ # byte 9+10: 03, TLS version word see byte 1+2
+ # byte 11-14: TLS timestamp for OpenSSL <1.01f
+ # byte 15-42: random, 28 bytes
+ # byte 43: session id length
+ # byte 44+45+sid-len: cipher suite!
+ # byte 46+sid-len: compression method: 00: none, 01: deflate
+ # byte 47+48+sid-len: extension length
+
+ [[ "$DEBUG" -eq 5 ]] && echo $tls_hello_ascii # one line without any blanks
+ if [[ -z "$tls_hello_ascii" ]]; then
+ debugme echo "server hello empty, TCP connection closed"
+ return 1 # no server hello received
+ fi
+
+ # now scrape two bytes out of the reply per byte
+ tls_content_type="${tls_hello_ascii:0:2}" # normally this is x16 (Handshake) here
+ tls_protocol="${tls_hello_ascii:2:4}"
+ DETECTED_TLS_VERSION=$tls_protocol
+
+ tls_len_all=${tls_hello_ascii:6:4}
+
+ sid_len_offset=86
+ tls_hello="${tls_hello_ascii:10:2}" # normally this is x02
+ tls_protocol2="${tls_hello_ascii:18:4}"
+ tls_hello_time="${tls_hello_ascii:22:8}"
+
+ if [[ $tls_content_type == "15" ]]; then # TLS ALERT
+ tls_err_level=${tls_hello_ascii:10:2} # 1: warning, 2: fatal
+ tls_err_descr=${tls_hello_ascii:12:2} # 112/0x70: Unrecognized name, 111/0x6F: certificate_unobtainable,
+ # 113/0x71: bad_certificate_status_response, #114/0x72: bad_certificate_hash_value
+ if [[ $DEBUG -ge 2 ]]; then
+ echo "tls_content_type: 0x$tls_content_type"
+ echo "tls_protocol: 0x$tls_protocol"
+ echo "tls_len_all: $tls_len_all"
+ echo "tls_err_descr: 0x${tls_err_descr} / = $(hex2dec ${tls_err_descr})"
+ echo "tls_err_level: ${tls_err_level} (warning:1, fatal:2)"
+ fi
+ # now, here comes a strange thing... -- on the first glance
+ # IF an apache 2.2/2.4 server e.g. has a default servername configured but we send SNI <myhostname>
+ # we get a TLS ALERT saying "unrecognized_name" (0x70) and a warning (0x1), see RFC https://tools.ietf.org/html/rfc6066#page-17
+ # note that RFC recommended to fail instead: https://tools.ietf.org/html/rfc6066#section-3
+ # we need to handle this properly -- otherwise we always return that the protocol or cipher is not available!
+ if [[ "$tls_err_descr" == 70 ]] && [[ "${tls_err_level}" == "01" ]]; then
+ sid_len_offset=100 # we are 2x7 bytes off (formerly: 86 instead of 100)
+ tls_hello="${tls_hello_ascii:24:2}" # here, too (normally this is (02)
+ tls_protocol2="${tls_hello_ascii:32:4}" # here, too
+ tls_hello_time="${tls_hello_ascii:36:8}" # and here, too
+ else
+ return 1
+ fi
+ fi
+
+ TLS_TIME=$(hex2dec "$tls_hello_time")
+ tls_sid_len=$(hex2dec "${tls_hello_ascii:$sid_len_offset:2}")
+ let sid_offset=$sid_len_offset+2+$tls_sid_len*2
+ tls_cipher_suite="${tls_hello_ascii:$sid_offset:4}"
+ let sid_offset=$sid_len_offset+6++$tls_sid_len*2
+ tls_compression_method="${tls_hello_ascii:$sid_offset:2}"
+
+ if [[ $DEBUG -ge 2 ]]; then
+ echo "tls_hello: 0x$tls_hello"
+ if [[ $DEBUG -ge 4 ]]; then
+ echo "tls_protocol2: 0x$tls_protocol2"
+ echo "tls_sid_len: 0x$(dec2hex $tls_sid_len) / = $tls_sid_len"
+ fi
+ echo -n "tls_hello_time: 0x$tls_hello_time "
+ if $HAS_GNUDATE ; then
+ date --date="@$TLS_TIME" "+%Y-%m-%d %r"
+ else
+ date -j -f %s "$TLS_TIME" "+%Y-%m-%d %r"
+ fi
+ echo "tls_cipher_suite: 0x$tls_cipher_suite"
+ echo "tls_compression_method: 0x$tls_compression_method"
+ outln
+ fi
+ return 0
+}
+
+
+sslv2_sockets() {
+ local ciphers_detected
+
+ fd_socket 5 || return 6
+ [[ "$DEBUG" -ge 2 ]] && outln "sending client hello... "
+ socksend_sslv2_clienthello "$SSLv2_CLIENT_HELLO"
+
+ sockread_serverhello 32768
+ [[ "$DEBUG" -ge 2 ]] && outln "reading server hello... "
+ if [[ "$DEBUG" -ge 4 ]]; then
+ hexdump -C "$SOCK_REPLY_FILE" | head -6
+ outln
+ fi
+
+ parse_sslv2_serverhello "$SOCK_REPLY_FILE"
+ case $? in
+ 7) # strange reply, couldn't convert the cipher spec length to a hex number
+ pr_litemagenta "strange v2 reply "
+ outln " (rerun with DEBUG >=2)"
+ [[ $DEBUG -ge 3 ]] && hexdump -C "$SOCK_REPLY_FILE" | head -1
+ ret=7 ;;
+ 1) # no sslv2 server hello returned, like in openlitespeed which returns HTTP!
+ pr_greenln "not offered (OK)"
+ ret=0 ;;
+ 0) # reset
+ pr_greenln "not offered (OK)"
+ ret=0 ;;
+ 3) # everything else
+ lines=$(hexdump -C "$SOCK_REPLY_FILE" 2>/dev/null | wc -l | sed 's/ //g')
+ [[ "$DEBUG" -ge 2 ]] && out " ($lines lines) "
+ if [[ "$lines" -gt 1 ]]; then
+ ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3))
+ if [[ 0 -eq "$ciphers_detected" ]]; then
+ pr_litered "supported but couldn't detect a cipher"; outln " (may need further attention)"
+ else
+ pr_red "offered (NOT ok)"; outln " -- $ciphers_detected ciphers"
+ fi
+ ret=1
+ fi ;;
+ esac
+ pr_off
+ debugme outln
+
+ close_socket
+ TMPFILE=$SOCK_REPLY_FILE
+ tmpfile_handle $FUNCNAME.dd
+ return $ret
+}
+
+
+# ARG1: TLS version low byte (00: SSLv3, 01: TLS 1.0, 02: TLS 1.1, 03: TLS 1.2)
+# ARG2: CIPHER_SUITES string
+socksend_tls_clienthello() {
+#FIXME: redo this with all extensions!
+ local tls_low_byte="$1"
+ local tls_low_byte1="01" # the first TLS version number is always 0301 -- except: SSLv3
+ local servername_hexstr len_servername len_servername_hex
+ local hexdump_format_str
+ local all_extensions
+ local len_sni_listlen len_sni_ext len_extension_hex
+ local cipher_suites len_ciph_suites len_ciph_suites_word
+ local len_client_hello_word len_all_word
+
+ #len_servername=$(echo ${#NODE})
+ len_servername=${#NODE}
+ hexdump_format_str="$len_servername/1 \"%02x,\""
+ servername_hexstr=$(printf $NODE | hexdump -v -e "${hexdump_format_str}" | sed 's/,$//')
+
+ code2network "$2" # convert CIPHER_SUITES
+ cipher_suites="$NW_STR" # we don't have the leading \x here so string length is two byte less, see next
+
+#formatted example for SNI
+#00 00 # extension server_name
+#00 1a # length = the following +2 = server_name length + 5
+#00 18 # server_name list_length = server_name length +3
+#00 # server_name type (hostname)
+#00 15 # server_name length
+#66 66 66 66 66 66 2e 66 66 66 66 66 66 66 66 66 66 2e 66 66 66 target.mydomain1.tld # server_name target
+
+ # convert lengths we need to fill in from dec to hex:
+ len_servername_hex=$(printf "%02x\n" $len_servername)
+ len_sni_listlen=$(printf "%02x\n" $((len_servername+3)))
+ len_sni_ext=$(printf "%02x\n" $((len_servername+5)))
+ len_extension_hex=$(printf "%02x\n" $((len_servername+9))) #FIXME: for TLS 1.2 and IIS servers we need extension_signature_algorithms!!
+
+ len_ciph_suites_byte=$(echo ${#cipher_suites})
+ let "len_ciph_suites_byte += 2"
+
+ # we have additional 2 chars \x in each 2 byte string and 2 byte ciphers, so we need to divide by 4:
+ len_ciph_suites=$(printf "%02x\n" $(($len_ciph_suites_byte / 4 )))
+ len2twobytes "$len_ciph_suites"
+ len_ciph_suites_word="$LEN_STR"
+ #[[ $DEBUG -ge 3 ]] && echo $len_ciph_suites_word
+
+ # RFC 3546 doesn't specify SSLv3 to have SNI, openssl just ignores the switch if supplied
+ if [[ "$tls_low_byte" == "00" ]]; then
+ len2twobytes $(printf "%02x\n" $((0x$len_ciph_suites + 0x27)))
+ else
+ len2twobytes $(printf "%02x\n" $((0x$len_ciph_suites + 0x27 + 0x$len_extension_hex + 0x2)))
+ fi
+ len_client_hello_word="$LEN_STR"
+ #[[ $DEBUG -ge 3 ]] && echo $len_client_hello_word
+
+ if [[ "$tls_low_byte" == "00" ]]; then
+ len2twobytes $(printf "%02x\n" $((0x$len_ciph_suites + 0x2b)))
+ else
+ len2twobytes $(printf "%02x\n" $((0x$len_ciph_suites + 0x2b + 0x$len_extension_hex + 0x2)))
+ fi
+ len_all_word="$LEN_STR"
+ #[[ $DEBUG -ge 3 ]] && echo $len_all_word
+
+ # if we have SSLv3, the first occurence of TLS protocol is SSLv3, otherwise TLS 1.0
+ [[ $tls_low_byte == "00" ]] && tls_low_byte1="00"
+
+ TLS_CLIENT_HELLO="
+ # TLS header ( 5 bytes)
+ ,16, 03, $tls_low_byte1 # TLS Version: in wireshark this is always 00 for TLS 1.0-1.2
+ ,$len_all_word # Length <---
+ # Handshake header:
+ ,01 # Type (x01 for ClientHello)
+ ,00, $len_client_hello_word # Length ClientHello
+ ,03, $tls_low_byte # TLS Version (again)
+ ,54, 51, 1e, 7a # Unix time since see www.moserware.com/2009/06/first-few-milliseconds-of-https.html
+ ,de, ad, be, ef # Random 28 bytes
+ ,31, 33, 07, 00, 00, 00, 00, 00
+ ,cf, bd, 39, 04, cc, 16, 0a, 85
+ ,03, 90, 9f, 77, 04, 33, d4, de
+ ,00 # Session ID length
+ ,$len_ciph_suites_word # Cipher suites length
+ ,$cipher_suites
+ ,01 # Compression methods length
+ ,00" # Compression method (x00 for NULL)
+
+#TODO,add (see heartbleed)
+# extension lenghth (word)
+# extension ec_point_formats (4 words) 1st: 00 0b
+#len 00 04
+# ec prot formats len: 03
+# uncompressed 00
+# EC point format: ansiX962_compressed_prime 01
+# EC point format: ansiX962_compressed_char2 02
+
+# ec, 1st: 00 0a
+# 2nd length: (word) e.g. 0x34
+# 3rd: ec curve len ln-2 e.g. 0x32
+# 4.-n. curves e.g. 25 words
+
+# Extension: Session Ticket 00 23
+
+ extension_signature_algorithms="
+ 00, 0d, # Type: signature_algorithms , see RFC 5246
+ 00, 20, # len
+ 00,1e, 06,01, 06,02, 06,03, 05,01, 05,02, 05,03,
+ 04,01, 04,02, 04,03, 03,01, 03,02, 03,03, 02,01, 02,02, 02,03"
+
+# Extension: Haertbeat 00 0f
+# len 00 01
+# peer allowed to send requests 01
+
+ if [[ "$tls_low_byte" == "00" ]]; then
+ all_extensions=""
+ else #FIXME: we (probably) need extension_signature_algorithms here. TLS 1.2 fails on IIS otherwise
+ all_extensions="
+ ,00, $len_extension_hex # first the len of all (here: 1) extentions. We assume len(hostname) < FF - 9
+ ,00, 00 # extension server_name
+ ,00, $len_sni_ext # length SNI EXT
+ ,00, $len_sni_listlen # server_name list_length
+ ,00 # server_name type (hostname)
+ ,00, $len_servername_hex # server_name length
+ ,$servername_hexstr" # server_name target
+ fi
+
+ fd_socket 5 || return 6
+
+ code2network "$TLS_CLIENT_HELLO$all_extensions"
+ data=$(echo $NW_STR)
+ [[ "$DEBUG" -ge 4 ]] && echo "\"$data\""
+ printf -- "$data" >&5 2>/dev/null &
+ sleep $USLEEP_SND
+
+ return 0
+}
+
+# arg1: TLS version low byte
+# (00: SSLv3, 01: TLS 1.0, 02: TLS 1.1, 03: TLS 1.2)
+tls_sockets() {
+ local -i ret=0
+ local -i save=0
+ local lines
+ local tls_low_byte
+ local cipher_list_2send
+
+ tls_low_byte="$1"
+ if [[ -n "$2" ]]; then # use supplied string in arg2 if there is one
+ cipher_list_2send="$2"
+ else # otherwise use std ciphers then
+ if [[ "$tls_low_byte" == "03" ]]; then
+ cipher_list_2send="$TLS12_CIPHER"
+ else
+ cipher_list_2send="$TLS_CIPHER"
+ fi
+ fi
+
+ [[ "$DEBUG" -ge 2 ]] && echo "sending client hello..."
+ socksend_tls_clienthello "$tls_low_byte" "$cipher_list_2send"
+ ret=$? # 6 means opening socket didn't succeed, e.g. timeout
+
+ # if sending didn't succeed we don't bother
+ if [[ $ret -eq 0 ]]; then
+ sockread_serverhello 32768
+ TLS_NOW=$(date "+%s")
+ [[ "$DEBUG" -ge 2 ]] && outln "reading server hello..."
+ if [[ "$DEBUG" -ge 3 ]]; then
+ hexdump -C $SOCK_REPLY_FILE | head -6
+ echo
+ fi
+
+ parse_tls_serverhello "$SOCK_REPLY_FILE"
+ save=$?
+
+ # see https://secure.wand.net.nz/trac/libprotoident/wiki/SSL
+ lines=$(count_lines "$(hexdump -C "$SOCK_REPLY_FILE" 2>$ERRFILE)")
+ [[ "$DEBUG" -ge 2 ]] && out " (returned $lines lines) "
+
+ # determine the return value for higher level, so that they can tell what the result is
+ if [[ $save -eq 1 ]] || [[ $lines -eq 1 ]]; then
+ ret=1 # NOT available
+ else
+ if [[ 03$tls_low_byte -eq $DETECTED_TLS_VERSION ]]; then
+ ret=0 # protocol available, TLS version returned equal to the one send
+ else
+ [[ $DEBUG -ge 2 ]] && echo -n "protocol send: 0x03$tls_low_byte, returned: 0x$DETECTED_TLS_VERSION"
+ ret=2 # protocol NOT available, server downgraded to $DETECTED_TLS_VERSION
+ fi
+ fi
+ debugme outln
+ else
+ debugme "stuck on sending: $ret"
+ fi
+
+ close_socket
+ TMPFILE=$SOCK_REPLY_FILE
+ tmpfile_handle $FUNCNAME.dd
+ return $ret
+}
+
+
+####### vulnerabilities follow #######
+
+# general overview which browser "supports" which vulnerability:
+# http://en.wikipedia.org/wiki/Transport_Layer_Security-SSL#Web_browsers
+
+
+# mainly adapted from https://gist.github.com/takeshixx/10107280
+run_heartbleed(){
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for heartbleed vulnerability" && outln "\n"
+ pr_bold " Heartbleed\c"; out " (CVE-2014-0160) "
+
+ #if [[ -n "$STARTTLS" ]] && [[ $EXPERIMENTAL != "yes" ]]; then
+ # outln "(not yet implemented for STARTTLS)"
+ # return 0
+ #fi
+
+ # determine TLS versions available:
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY -tlsextdebug &>$TMPFILE </dev/null
+
+ if $HAS_SED_E; then
+ tls_proto_offered=$(grep -aw Protocol $TMPFILE | sed -E 's/[^[:digit:]]//g')
+ else
+ tls_proto_offered=$(grep -aw Protocol $TMPFILE | sed -r 's/[^[:digit:]]//g')
+ fi
+ case $tls_proto_offered in
+ 12) tls_hexcode="x03, x03" ;;
+ 11) tls_hexcode="x03, x02" ;;
+ *) tls_hexcode="x03, x01" ;;
+ esac
+ heartbleed_payload=", x18, $tls_hexcode, x00, x03, x01, x40, x00"
+
+ client_hello="
+ # TLS header ( 5 bytes)
+ ,x16, # content type (x16 for handshake)
+ $tls_hexcode, # TLS version
+ x00, xdc, # length
+ # Handshake header
+ x01, # type (x01 for ClientHello)
+ x00, x00, xd8, # length
+ $tls_hexcode, # TLS version
+ # Random (32 byte)
+ x53, x43, x5b, x90, x9d, x9b, x72, x0b,
+ xbc, x0c, xbc, x2b, x92, xa8, x48, x97,
+ xcf, xbd, x39, x04, xcc, x16, x0a, x85,
+ x03, x90, x9f, x77, x04, x33, xd4, xde,
+ x00, # session ID length
+ x00, x66, # cipher suites length
+ # cipher suites (51 suites)
+ xc0, x14, xc0, x0a, xc0, x22, xc0, x21,
+ x00, x39, x00, x38, x00, x88, x00, x87,
+ xc0, x0f, xc0, x05, x00, x35, x00, x84,
+ xc0, x12, xc0, x08, xc0, x1c, xc0, x1b,
+ x00, x16, x00, x13, xc0, x0d, xc0, x03,
+ x00, x0a, xc0, x13, xc0, x09, xc0, x1f,
+ xc0, x1e, x00, x33, x00, x32, x00, x9a,
+ x00, x99, x00, x45, x00, x44, xc0, x0e,
+ xc0, x04, x00, x2f, x00, x96, x00, x41,
+ xc0, x11, xc0, x07, xc0, x0c, xc0, x02,
+ x00, x05, x00, x04, x00, x15, x00, x12,
+ x00, x09, x00, x14, x00, x11, x00, x08,
+ x00, x06, x00, x03, x00, xff,
+ x01, # compression methods length
+ x00, # compression method (x00 for NULL)
+ x00, x49, # extensions length
+ # extension: ec_point_formats
+ x00, x0b, x00, x04, x03, x00, x01, x02,
+ # extension: elliptic_curves
+ x00, x0a, x00, x34, x00, x32, x00, x0e,
+ x00, x0d, x00, x19, x00, x0b, x00, x0c,
+ x00, x18, x00, x09, x00, x0a, x00, x16,
+ x00, x17, x00, x08, x00, x06, x00, x07,
+ x00, x14, x00, x15, x00, x04, x00, x05,
+ x00, x12, x00, x13, x00, x01, x00, x02,
+ x00, x03, x00, x0f, x00, x10, x00, x11,
+ # extension: session ticket TLS
+ x00, x23, x00, x00,
+ # extension: heartbeat
+ x00, x0f, x00, x01, x01"
+
+ fd_socket 5 || return 6
+
+ [[ $DEBUG -ge 2 ]] && outln "\nsending client hello (TLS version $tls_hexcode)"
+ socksend "$client_hello" 1
+ sockread 16384
+
+ [[ $DEBUG -ge 2 ]] && outln "\nreading server hello"
+ if [[ $DEBUG -ge 3 ]]; then
+ echo "$SOCKREPLY" | "${HEXDUMPVIEW[@]}" | head -20
+ outln "[...]"
+ outln "\nsending payload with TLS version $tls_hexcode:"
+ fi
+
+ socksend "$heartbleed_payload" 1
+ sockread 16384 $HEARTBLEED_MAX_WAITSOCK
+ retval=$?
+
+ if [[ $DEBUG -ge 3 ]]; then
+ outln "\nheartbleed reply: "
+ echo "$SOCKREPLY" | "${HEXDUMPVIEW[@]}"
+ outln
+ fi
+
+ lines_returned=$(echo "$SOCKREPLY" | "${HEXDUMP[@]}" | wc -l | sed 's/ //g')
+ if [[ $lines_returned -gt 1 ]]; then
+ pr_red "VULNERABLE (NOT ok)"
+ ret=1
+ else
+ pr_green "not vulnerable (OK)"
+ ret=0
+ fi
+ [[ $retval -eq 3 ]] && out " (timed out)"
+ outln
+
+ close_socket
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+# helper function
+ok_ids(){
+ pr_greenln "\n ok -- something resetted our ccs packets"
+ return 0
+}
+
+#FIXME: At a certain point heartbleed and ccs needs to be changed and make use of code2network using a file, then tls_sockets
+run_ccs_injection(){
+ # see https://www.openssl.org/news/secadv_20140605.txt
+ # mainly adapted from Ramon de C Valle's C code from https://gist.github.com/rcvalle/71f4b027d61a78c42607
+ [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_blue "--> Testing for CCS injection vulnerability" && outln "\n"
+ pr_bold " CCS"; out " (CVE-2014-0224) "
+
+ #if [[ -n "$STARTTLS" ]] && [[ $EXPERIMENTAL != "yes" ]]; then
+ # outln "(not yet implemented for STARTTLS)"
+ # return 0
+ #fi
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY &>$TMPFILE </dev/null
+
+ if $HAS_SED_E; then
+ tls_proto_offered=$(grep -aw Protocol $TMPFILE | sed -E 's/[^[:digit:]]//g')
+ else
+ tls_proto_offered=$(grep -aw Protocol $TMPFILE | sed -r 's/[^[:digit:]]//g')
+ fi
+ case $tls_proto_offered in
+ 12) tls_hexcode="x03, x03" ;;
+ 11) tls_hexcode="x03, x02" ;;
+ *) tls_hexcode="x03, x01" ;;
+ esac
+ ccs_message=", x14, $tls_hexcode ,x00, x01, x01"
+
+ client_hello="
+ # TLS header (5 bytes)
+ ,x16, # content type (x16 for handshake)
+ $tls_hexcode, # TLS version
+ x00, x93, # length
+ # Handshake header
+ x01, # type (x01 for ClientHello)
+ x00, x00, x8f, # length
+ $tls_hexcode, # TLS version
+ # Random (32 byte)
+ x53, x43, x5b, x90, x9d, x9b, x72, x0b,
+ xbc, x0c, xbc, x2b, x92, xa8, x48, x97,
+ xcf, xbd, x39, x04, xcc, x16, x0a, x85,
+ x03, x90, x9f, x77, x04, x33, xd4, xde,
+ x00, # session ID length
+ x00, x68, # cipher suites length
+ # Cipher suites (51 suites)
+ xc0, x13, xc0, x12, xc0, x11, xc0, x10,
+ xc0, x0f, xc0, x0e, xc0, x0d, xc0, x0c,
+ xc0, x0b, xc0, x0a, xc0, x09, xc0, x08,
+ xc0, x07, xc0, x06, xc0, x05, xc0, x04,
+ xc0, x03, xc0, x02, xc0, x01, x00, x39,
+ x00, x38, x00, x37, x00, x36, x00, x35, x00, x34,
+ x00, x33, x00, x32, x00, x31, x00, x30,
+ x00, x2f, x00, x16, x00, x15, x00, x14,
+ x00, x13, x00, x12, x00, x11, x00, x10,
+ x00, x0f, x00, x0e, x00, x0d, x00, x0c,
+ x00, x0b, x00, x0a, x00, x09, x00, x08,
+ x00, x07, x00, x06, x00, x05, x00, x04,
+ x00, x03, x00, x02, x00, x01, x01, x00"
+
+ fd_socket 5 || return 6
+
+# we now make a standard handshake ...
+ debugme out "\nsending client hello, "
+ socksend "$client_hello" 1
+ sockread 16384
+
+ debugme outln "\nreading server hello"
+ if [[ $DEBUG -ge 3 ]]; then
+ echo "$SOCKREPLY" | "${HEXDUMPVIEW[@]}" | head -20
+ outln "[...]"
+ outln "\npayload #1 with TLS version $tls_hexcode:"
+ fi
+
+# ... and then send the a change cipher spec message
+ socksend "$ccs_message" 1 || ok_ids
+ sockread 2048 $CCS_MAX_WAITSOCK
+ if [[ $DEBUG -ge 3 ]]; then
+ outln "\n1st reply: "
+ out "$SOCKREPLY" | "${HEXDUMPVIEW[@]}" | head -20
+# ok: 15 | 0301 | 02 | 02 | 0a
+# ALERT | TLS 1.0 | Length=2 | Unexpected Message (0a)
+# or just timed out
+ outln
+ outln "payload #2 with TLS version $tls_hexcode:"
+ fi
+
+ socksend "$ccs_message" 2 || ok_ids
+ sockread 2048 $CCS_MAX_WAITSOCK
+ retval=$?
+
+ if [[ $DEBUG -ge 3 ]]; then
+ outln "\n2nd reply: "
+ printf -- "$SOCKREPLY" | "${HEXDUMPVIEW[@]}"
+# not ok: 15 | 0301 | 02 | 02 | 15
+# ALERT | TLS 1.0 | Length=2 | Decryption failed (21)
+# ok: 0a or nothing: ==> RST
+ outln
+ fi
+
+ byte6=$(echo "$SOCKREPLY" | "${HEXDUMPPLAIN[@]}" | sed 's/^..........//')
+ lines=$(echo "$SOCKREPLY" | "${HEXDUMP[@]}" | count_lines )
+ debugme echo "lines: $lines, byte6: $byte6"
+
+ if [[ "$byte6" == "0a" ]] || [[ "$lines" -gt 1 ]]; then
+ pr_green "not vulnerable (OK)"
+ ret=0
+ else
+ pr_red "VULNERABLE (NOT ok)"
+ ret=1
+ fi
+ [[ $retval -eq 3 ]] && out " (timed out)"
+ outln
+
+ close_socket
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+local_problem() {
+ pr_litemagentaln "Local problem: $1"
+}
+
+run_renego() {
+# no SNI here. Not needed as there won't be two different SSL stacks for one IP
+ local legacycmd=""
+ local insecure_renogo_str="Secure Renegotiation IS NOT"
+ local sec_renego sec_client_renego
+
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for Renegotiation vulnerability" && outln "\n"
+
+ pr_bold " Secure Renegotiation "; out "(CVE-2009-3555) " # and RFC5746, OSVDB 59968-59974
+ # community.qualys.com/blogs/securitylabs/2009/11/05/ssl-and-tls-authentication-gap-vulnerability-discovered
+ if $OPENSSL s_client $OPTIMAL_PROTO $STARTTLS -connect $NODEIP:$PORT $SNI $PROXY 2>&1 </dev/null &>$TMPFILE; then
+ grep -iaq "$insecure_renogo_str" $TMPFILE
+ sec_renego=$? # 0= Secure Renegotiation IS NOT supported
+#FIXME: didn't occur to me yet but why not also to check on "Secure Renegotiation IS supported"
+ case $sec_renego in
+ 0) pr_redln "VULNERABLE (NOT ok)" ;;
+ 1) pr_greenln "not vulnerable (OK)" ;;
+ *) pr_litemagentaln "FIXME (bug): $sec_renego" ;;
+ esac
+ else
+ pr_litemagentaln "handshake didn't succeed"
+ fi
+
+ pr_bold " Secure Client-Initiated Renegotiation " # RFC 5746
+ # see: https://community.qualys.com/blogs/securitylabs/2011/10/31/tls-renegotiation-and-denial-of-service-attacks
+ # http://blog.ivanristic.com/2009/12/testing-for-ssl-renegotiation.html -- head/get doesn't seem to be needed though
+ case "$OSSL_VER" in
+ 0.9.8*) # we need this for Mac OSX unfortunately
+ case "$OSSL_VER_APPENDIX" in
+ [a-l]) local_problem "$OPENSSL cannot test this secure renegotiation vulnerability"
+ return 3 ;;
+ [m-z]) ;; # all ok
+ esac ;;
+ 1.0.1*|1.0.2*) legacycmd="-legacy_renegotiation" ;;
+ 0.9.9*|1.0*) ;; # all ok
+ esac
+
+ # We need up to two tries here, as some LiteSpeed servers don't answer on "R" and block. Thus first try in the background
+ echo R | $OPENSSL s_client $OPTIMAL_PROTO $legacycmd $STARTTLS -msg -connect $NODEIP:$PORT $SNI $PROXY &>$TMPFILE & # msg enables us to look deeper into it while debugging
+ wait_kill $! $HEADER_MAXSLEEP
+ if [[ $? -eq 3 ]]; then
+ pr_litegreen "likely not vulnerable (OK)"; outln " (timed out)" # it hung
+ sec_client_renego=1
+ else
+ # second try in the foreground as we are sure now it won't hang
+ echo R | $OPENSSL s_client $legacycmd $STARTTLS -msg -connect $NODEIP:$PORT $SNI $PROXY &>$TMPFILE
+ sec_client_renego=$? # 0=client is renegotiating & doesn't return an error --> vuln!
+ case $sec_client_renego in
+ 0) pr_litered "VULNERABLE (NOT ok)"; outln ", DoS threat" ;;
+ 1) pr_litegreenln "not vulnerable (OK)" ;;
+ *) "FIXME (bug): $sec_client_renego" ;;
+ esac
+ fi
+
+ #FIXME Insecure Client-Initiated Renegotiation is missing
+
+ tmpfile_handle $FUNCNAME.txt
+ return $(($sec_renego + $sec_client_renego))
+#FIXME: the return value is wrong, should be 0 if all ok. But as the caller doesn't care we don't care either ... yet ;-)
+}
+
+run_crime() {
+ local -i ret=0
+ local addcmd=""
+ # in a nutshell: don't offer TLS/SPDY compression on the server side
+ # This tests for CRIME Vulnerability (www.ekoparty.org/2012/juliano-rizzo.php) on HTTPS, not SPDY (yet)
+ # Please note that it is an attack where you need client side control, so in regular situations this
+ # means anyway "game over", w/wo CRIME
+ # www.h-online.com/security/news/item/Vulnerability-in-SSL-encryption-is-barely-exploitable-1708604.html
+
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for CRIME vulnerability" && outln "\n"
+ pr_bold " CRIME, TLS " ; out "(CVE-2012-4929) "
+
+ # first we need to test whether OpenSSL binary has zlib support
+ $OPENSSL zlib -e -a -in /dev/stdin &>/dev/stdout </dev/null | grep -q zlib
+ if [[ $? -eq 0 ]]; then
+ local_problem "$OPENSSL lacks zlib support"
+ return 7
+ fi
+
+ [[ "$OSSL_VER" == "*0.9.8*" ]] && addcmd="-no_ssl2"
+ $OPENSSL s_client $OPTIMAL_PROTO $addcmd $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI </dev/null &>$TMPFILE
+ if grep -a Compression $TMPFILE | grep -aq NONE >/dev/null; then
+ pr_litegreen "not vulnerable (OK)"
+ [[ $SERVICE == "HTTP" ]] || out " (not using HTTP anyway)"
+ ret=0
+ else
+ if [[ $SERVICE == "HTTP" ]]; then
+ pr_litered "VULNERABLE (NOT ok)"
+ else
+ pr_brown "VULNERABLE (NOT ok), but not using HTTP: probably no exploit known"
+ fi
+ ret=1
+ fi
+ # not clear whether this is a protocol != HTTP as one needs to have the ability to modify the
+ # compression input which is done via javascript in the context of HTTP
+ outln
+
+# this needs to be re-done i order to remove the redundant check for spdy
+
+ # weed out starttls, spdy-crime is a web thingy
+# if [[ "x$STARTTLS" != "x" ]]; then
+# echo
+# return $ret
+# fi
+
+ # weed out non-webports, spdy-crime is a web thingy. there's a catch thoug, you see it?
+# case $PORT in
+# 25|465|587|80|110|143|993|995|21)
+# echo
+# return $ret
+# esac
+
+# $OPENSSL s_client help 2>&1 | grep -qw nextprotoneg
+# if [[ $? -eq 0 ]]; then
+# $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg $NPN_PROTOs $SNI </dev/null 2>/dev/null >$TMPFILE
+# if [[ $? -eq 0 ]]; then
+# echo
+# pr_bold "CRIME Vulnerability, SPDY \c" ; outln "(CVE-2012-4929): \c"
+
+# STR=$(grep Compression $TMPFILE )
+# if echo $STR | grep -q NONE >/dev/null; then
+# pr_green "not vulnerable (OK)"
+# ret=$((ret + 0))
+# else
+# pr_red "VULNERABLE (NOT ok)"
+# ret=$((ret + 1))
+# fi
+# fi
+# fi
+# [[ $DEBUG -eq 2 ]] outln "$STR"
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+# BREACH is a HTTP-level compression & an attack which works against any cipher suite and is agnostic
+# to the version of TLS/SSL, more: http://www.breachattack.com/ . Foreign referrers are the important thing here!
+run_breach() {
+ local header
+ local -i ret=0
+ local referer useragent
+ local url
+
+ [[ $SERVICE != "HTTP" ]] && return 7
+
+ [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_blue "--> Testing for BREACH (HTTP compression) vulnerability" && outln "\n"
+ pr_bold " BREACH"; out " (CVE-2013-3587) "
+
+ url="$1"
+ [[ -z "$url" ]] && url="/"
+ if $SNEAKY; then
+ # see https://community.qualys.com/message/20360
+ if [[ "$NODE" =~ google ]]; then
+ referer="http://yandex.ru/" # otherwise we have a false positive for google.com
+ else
+ referer="http://google.com/"
+ fi
+ useragent="$UA_SNEAKY"
+ else
+ referer="TLS/SSL-Tester from $SWURL"
+ useragent="$UA_STD"
+ fi
+ (
+ $OPENSSL s_client $OPTIMAL_PROTO -quiet -connect $NODEIP:$PORT $PROXY $SNI << EOF
+GET $url HTTP/1.1
+Host: $NODE
+User-Agent: $useragent
+Accept: text/*
+Accept-Language: en-US,en
+Accept-encoding: gzip,deflate,compress
+Referer: $referer
+Connection: close
+
+EOF
+) >$HEADERFILE_BREACH 2>$ERRFILE &
+ if wait_kill $! $HEADER_MAXSLEEP; then
+ result=$(grep -a '^Content-Encoding' $HEADERFILE_BREACH | sed -e 's/^Content-Encoding//' -e 's/://' -e 's/ //g')
+ result=$(echo $result | tr -cd '\40-\176')
+ if [[ -z $result ]]; then
+ pr_green "no HTTP compression (OK) "
+ ret=0
+ else
+ pr_litered "NOT ok: uses $result HTTP compression "
+ ret=1
+ fi
+ # Catch: any URL can be vulnerable. I am testing now only the root. URL!
+ outln "(only \"$url\" tested)"
+ else
+ pr_litemagentaln "failed (HTTP header request stalled)"
+ ret=3
+ fi
+ return $ret
+}
+
+# Padding Oracle On Downgraded Legacy Encryption, in a nutshell: don't use CBC Ciphers in SSLv3
+run_ssl_poodle() {
+ local -i ret=0
+ local cbc_ciphers
+ local cbc_ciphers="SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:SRP-AES-256-CBC-SHA:RSA-PSK-AES256-CBC-SHA:PSK-AES256-CBC-SHA:SRP-DSS-AES-128-CBC-SHA:SRP-RSA-AES-128-CBC-SHA:SRP-AES-128-CBC-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RSA-PSK-AES128-CBC-SHA:PSK-AES128-CBC-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:SRP-DSS-3DES-EDE-CBC-SHA:SRP-RSA-3DES-EDE-CBC-SHA:SRP-3DES-EDE-CBC-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:AECDH-DES-CBC3-SHA:ADH-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:RSA-PSK-3DES-EDE-CBC-SHA:PSK-3DES-EDE-CBC-SHA:EXP1024-DHE-DSS-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:ADH-DES-CBC-SHA:EXP1024-DES-CBC-SHA:DES-CBC-SHA:EXP1024-RC2-CBC-MD5:DES-CBC-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-ADH-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5"
+ local cbc_ciphers_krb="KRB5-IDEA-CBC-SHA:KRB5-IDEA-CBC-MD5:KRB5-DES-CBC3-SHA:KRB5-DES-CBC3-MD5:KRB5-DES-CBC-SHA:KRB5-DES-CBC-MD5:EXP-KRB5-RC2-CBC-SHA:EXP-KRB5-DES-CBC-SHA:EXP-KRB5-RC2-CBC-MD5:EXP-KRB5-DES-CBC-MD5"
+
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for SSLv3 POODLE (Padding Oracle On Downgraded Legacy Encryption)" && outln "\n"
+ pr_bold " POODLE, SSL"; out " (CVE-2014-3566) "
+ cbc_ciphers=$($OPENSSL ciphers -v 'ALL:eNULL' 2>$ERRFILE | awk '/CBC/ { print $1 }' | tr '\n' ':')
+#FIXME: even with worst openssl client (FreeBSD9) we have 17 reasonable ciphers but is that enough to check??
+ debugme echo $cbc_ciphers
+ $OPENSSL s_client -ssl3 $STARTTLS -cipher $cbc_ciphers -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ [[ $DEBUG -eq 2 ]] && egrep -q "error|failure" $TMPFILE | egrep -av "unable to get local|verify error"
+ if [[ $ret -eq 0 ]]; then
+ pr_litered "VULNERABLE (NOT ok)"; out ", uses SSLv3+CBC (check TLS_FALLBACK_SCSV mitigation below)"
+ else
+ pr_green "not vulnerable (OK)"
+ fi
+ outln
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+# for appliance which use padding, no fallback needed
+run_tls_poodle() {
+ pr_bold " POODLE, SSL"; out " CVE-2014-8730), experimental "
+ #FIXME
+ echo "#FIXME"
+ return 7
+}
+
+run_tls_fallback_scsv() {
+ local -i ret=0
+
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for TLS_FALLBACK_SCSV Protection" && outln "\n"
+ pr_bold " TLS_FALLBACK_SCSV"; out " (RFC 7507), experim. "
+ # This isn't a vulnerability check per se, but checks for the existence of
+ # the countermeasure to protect against protocol downgrade attacks.
+
+ # First check we have support for TLS_FALLBACK_SCSV in our local OpenSSL
+ $OPENSSL s_client -h 2>&1 | grep -q "\-fallback_scsv"
+ if [[ $? -gt 0 ]]; then
+ local_problem "$OPENSSL lacks TLS_FALLBACK_SCSV support"
+ return 4
+ fi
+ #TODO: this need some tuning: a) if one protocol is supported only it has practcally no value (theoretical it's interesting though)
+ # b) for IIS6 + openssl 1.0.2 this won't work
+ # c) best to make sure that we hit a specific protocol, see https://alpacapowered.wordpress.com/2014/10/20/ssl-poodle-attack-what-is-this-scsv-thingy/
+ # d) minor: we should do "-state" here
+
+ # first: make sure we have tls1_2:
+ if ! $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI -no_tls1_2 &>/dev/null </dev/null; then
+ pr_litemagenta "Check failed: seems like TLS 1.2 is the only protocol "
+ ret=7
+ else
+ # ...and do the test
+ $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI -no_tls1_2 -fallback_scsv &>$TMPFILE </dev/null
+ if grep -q "CONNECTED(00" "$TMPFILE"; then
+ if grep -qa "BEGIN CERTIFICATE" "$TMPFILE"; then
+ pr_brown "Downgrade attack prevention NOT supported"
+ ret=1
+ elif grep -qa "alert inappropriate fallback" "$TMPFILE"; then
+ pr_litegreen "Downgrade attack prevention supported (OK)"
+ ret=0
+ elif grep -qa "alert handshake failure" "$TMPFILE"; then
+ # see RFC 7507, https://github.com/drwetter/testssl.sh/issues/121
+ pr_brown "\"handshake failure\" instead of \"inappropriate fallback\" (likely NOT ok)"
+ ret=2
+ elif grep -qa "ssl handshake failure" "$TMPFILE"; then
+ pr_brown "some unexpected \"handshake failure\" instead of \"inappropriate fallback\" (likely NOT ok)"
+ ret=3
+ else
+ pr_litemagenta "Check failed, unexpected result "
+ out ", run $PROG_NAME -Z --debug=1 and look at $TEMPDIR/*tls_fallback_scsv.txt"
+ fi
+ else
+ pr_litemagenta "test failed (couldn't connect)"
+ ret=7
+ fi
+ fi
+
+ outln
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+
+# Factoring RSA Export Keys: don't use EXPORT RSA ciphers, see https://freakattack.com/
+run_freak() {
+ local -i ret=0
+ local -i no_supported_ciphers=0
+ # with correct build it should list these 7 ciphers (plus the two latter as SSLv2 ciphers):
+ local exportrsa_cipher_list="EXP1024-DES-CBC-SHA:EXP1024-RC4-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-DH-RSA-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-RC4-MD5:EXP-RC4-MD5"
+ local addtl_warning=""
+
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for FREAK attack" && outln "\n"
+ pr_bold " FREAK"; out " (CVE-2015-0204) "
+
+ no_supported_ciphers=$(count_ciphers $(actually_supported_ciphers $exportrsa_cipher_list))
+ #echo "========= ${PIPESTATUS[*]}
+
+ case $no_supported_ciphers in
+ 0) local_problem "$OPENSSL doesn't have any EXPORT RSA ciphers configured"
+ return 7 ;;
+ 1|2|3)
+ addtl_warning=" ($magenta""tested only with $no_supported_ciphers out of 9 ciphers only!$off)" ;;
+ 8|9|10|11)
+ addtl_warning="" ;;
+ 4|5|6|7)
+ addtl_warning=" (tested with $no_supported_ciphers/9 ciphers)" ;;
+ esac
+ $OPENSSL s_client $STARTTLS -cipher $exportrsa_cipher_list -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ [[ $DEBUG -eq 2 ]] && egrep -a "error|failure" $TMPFILE | egrep -av "unable to get local|verify error"
+ if [[ $ret -eq 0 ]]; then
+ pr_red "VULNERABLE (NOT ok)"; out ", uses EXPORT RSA ciphers"
+ else
+ pr_green "not vulnerable (OK)"; out "$addtl_warning"
+ fi
+ outln
+
+ debugme echo $(actually_supported_ciphers $exportrsa_cipher_list)
+ debugme echo $no_supported_ciphers
+
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+
+
+# see https://weakdh.org/logjam.html
+run_logjam() {
+ local -i ret=0
+ local exportdhe_cipher_list="EXP1024-DHE-DSS-DES-CBC-SHA:EXP1024-DHE-DSS-RC4-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA"
+ local -i no_supported_ciphers=0
+ local addtl_warning=""
+
+ [ $VULN_COUNT -le $VULN_THRESHLD ] && outln && pr_blue "--> Testing for LOGJAM vulnerability" && outln "\n"
+ pr_bold " LOGJAM"; out " (CVE-2015-4000), experimental "
+
+ no_supported_ciphers=$(count_ciphers $(actually_supported_ciphers $exportdhe_cipher_list))
+
+ case $no_supported_ciphers in
+ 0) local_problem "$OPENSSL doesn't have any DHE EXPORT ciphers configured"
+ return 3 ;;
+ 1|2) addtl_warning=" ($magenta""tested w/ $no_supported_ciphers/4 ciphers only!$off)" ;;
+ 3) addtl_warning=" (tested w/ $no_supported_ciphers/4 ciphers)" ;;
+ 4) ;;
+ esac
+ $OPENSSL s_client $STARTTLS -cipher $exportdhe_cipher_list -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE </dev/null
+ ret=$?
+ [[ $DEBUG -eq 2 ]] && egrep -a "error|failure" $TMPFILE | egrep -av "unable to get local|verify error"
+ addtl_warning="$addtl_warning, common primes not checked."
+ if $HAS_DH_BITS; then
+ if ! $do_allciphers && ! $do_cipher_per_proto && $HAS_DH_BITS; then
+ addtl_warning="$addtl_warning \"$PROG_NAME -E/-e\" spots candidates"
+ else
+ addtl_warning="$addtl_warning See below for any DH ciphers + bit size"
+ fi
+ fi
+
+ if [[ $ret -eq 0 ]]; then
+ pr_red "VULNERABLE (NOT ok)"; out ", uses DHE EXPORT ciphers, common primes not checked."
+ else
+ pr_green "not vulnerable (OK)"; out "$addtl_warning"
+ fi
+ outln
+
+ debugme echo $(actually_supported_ciphers $exportdhe_cipher_list)
+ debugme echo $no_supported_ciphers
+
+ tmpfile_handle $FUNCNAME.txt
+ return $ret
+}
+# TODO: perfect candidate for replacement by sockets, so is freak
+
+
+
+# Browser Exploit Against SSL/TLS: don't use CBC Ciphers in SSLv3 TLSv1.0
+run_beast(){
+ local hexcode dash cbc_cipher sslvers kx auth enc mac export
+ local detected_proto
+ local -i ret=0
+ local detected_cbc_ciphers=""
+ local higher_proto_supported=""
+ local openssl_ret=0
+ local vuln_beast=false
+ local spaces=" "
+ local cr=$'\n'
+ local first=true
+ local continued=false
+ local cbc_cipher_list="SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:SRP-AES-256-CBC-SHA:RSA-PSK-AES256-CBC-SHA:PSK-AES256-CBC-SHA:SRP-DSS-AES-128-CBC-SHA:SRP-RSA-AES-128-CBC-SHA:SRP-AES-128-CBC-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RSA-PSK-AES128-CBC-SHA:PSK-AES128-CBC-SHA:KRB5-IDEA-CBC-SHA:KRB5-IDEA-CBC-MD5:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:SRP-DSS-3DES-EDE-CBC-SHA:SRP-RSA-3DES-EDE-CBC-SHA:SRP-3DES-EDE-CBC-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:AECDH-DES-CBC3-SHA:ADH-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:RSA-PSK-3DES-EDE-CBC-SHA:PSK-3DES-EDE-CBC-SHA:KRB5-DES-CBC3-SHA:KRB5-DES-CBC3-MD5:EXP1024-DHE-DSS-DES-CBC-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DH-RSA-DES-CBC-SHA:DH-DSS-DES-CBC-SHA:ADH-DES-CBC-SHA:EXP1024-DES-CBC-SHA:DES-CBC-SHA:DES-CBC-MD5:KRB5-DES-CBC-SHA:KRB5-DES-CBC-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DH-RSA-DES-CBC-SHA:EXP-DH-DSS-DES-CBC-SHA:EXP-ADH-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-KRB5-RC2-CBC-SHA:EXP-KRB5-DES-CBC-SHA:EXP-KRB5-RC2-CBC-MD5:EXP-KRB5-DES-CBC-MD5"
+
+ if [[ $VULN_COUNT -le $VULN_THRESHLD ]] || $WIDE; then
+ outln
+ pr_blue "--> Testing for BEAST vulnerability" && outln "\n"
+ fi
+ pr_bold " BEAST"; out " (CVE-2011-3389) "
+ $WIDE && outln
+
+ >$ERRFILE
+ # 2) test handfull of common CBC ciphers
+ for proto in ssl3 tls1; do
+ $OPENSSL s_client -"$proto" $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>>$ERRFILE </dev/null
+ if [[ $? -ne 0 ]]; then # protocol supported?
+ if $continued; then # second round: we hit TLS1:
+ pr_litegreenln "no SSL3 or TLS1"
+ return 0
+ else # protocol not succeeded but it';s the first time
+ continued=true
+ continue # protocol no supported, so we do not need to check each cipher with that protocol
+ fi
+ fi # protocol succeeded
+ # protocol with cbc_cipher check follows now
+
+ if $WIDE; then
+ outln "\n $(toupper $proto):";
+ neat_header # NOTTHATNICE: we display the header also if in the end no cbc cipher is available on the client side
+ fi
+ while read hexcode dash cbc_cipher sslvers kx auth enc mac; do
+ $OPENSSL s_client -cipher "$cbc_cipher" -"$proto" $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>>$ERRFILE </dev/null
+ openssl_ret=$?
+ [[ $openssl_ret -eq 0 ]] && vuln_beast=true
+ if $WIDE; then
+ normalize_ciphercode $hexcode
+ if [[ "$SHOW_EACH_C" -ne 0 ]]; then
+ neat_list $HEXC $cbc_cipher $kx $enc
+ if [[ $openssl_ret -eq 0 ]]; then
+ pr_brownln "available"
+ else
+ outln "not a/v"
+ fi
+ else
+ [[ $openssl_ret -eq 0 ]] && neat_list $HEXC $cbc_cipher $kx $enc && outln
+ fi
+ else # short display:
+ if [[ $openssl_ret -eq 0 ]]; then
+ detected_cbc_ciphers="$detected_cbc_ciphers ""$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g')"
+ vuln_beast=true
+ fi
+ fi
+ done < <($OPENSSL ciphers -V 'ALL:eNULL' 2>>$ERRFILE | grep -a CBC) # -V doesn't work with openssl < 1.0
+ # ^^^^^ process substitution as shopt will either segfault or doesn't work with old bash versions
+
+ if ! $WIDE; then
+ if [[ -n "$detected_cbc_ciphers" ]]; then
+ detected_cbc_ciphers=$(echo "$detected_cbc_ciphers" | sed -e "s/ /\\${cr} ${spaces}/9" -e "s/ /\\${cr} ${spaces}/6" -e "s/ /\\${cr} ${spaces}/3")
+ ! $first && out "$spaces"
+ out "$(toupper $proto):"; pr_brownln "$detected_cbc_ciphers"
+ detected_cbc_ciphers="" # empty for next round
+ first=false
+ else
+ [[ $proto == "tls1" ]] && ! $first && echo -n "$spaces"
+ pr_litegreenln "no CBC ciphers for $(toupper $proto) (OK)"
+ first=false
+ fi
+ else
+ $vuln_beast || pr_litegreenln " no CBC ciphers for $(toupper $proto) (OK)"
+ fi
+ done # for proto in ssl3 tls1
+
+ # 2) support for TLS 1.1+1.2?
+ for proto in tls1_1 tls1_2; do
+ $OPENSSL s_client -state -"$proto" $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI 2>>$ERRFILE >$TMPFILE </dev/null
+ if [[ $? -eq 0 ]]; then
+ higher_proto_supported="$higher_proto_supported ""$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol .*://' -e 's/ //g')"
+ fi
+ done
+ if $vuln_beast ; then
+ if [[ ! -z "$higher_proto_supported" ]]; then
+ if $WIDE; then
+ outln
+ pr_brown "VULNERABLE"
+ ret=1
+ outln " -- but also supports higher protocols (possible mitigation):$higher_proto_supported"
+ else
+ outln "${spaces}-- but also supports higher protocols (possible mitigation):$higher_proto_supported"
+ fi
+ fi
+ fi
+# printf "For a full individual test of each CBC cipher suites support by your $OPENSSL run \"$0 -x CBC $NODE\"\n"
+
+ tmpfile_handle $FUNCNAME.txt
+ return
+}
+
+run_lucky13() {
+#FIXME: to do . CVE-2013-0169
+# in a nutshell: don't offer CBC suites (again). MAC as a fix for padding oracles is not enough. Best: TLS v1.2+ AES GCM
+ echo "FIXME"
+ return -1
+}
+
+
+# https://tools.ietf.org/html/rfc7465 REQUIRES that TLS clients and servers NEVER negotiate the use of RC4 cipher suites!
+# https://en.wikipedia.org/wiki/Transport_Layer_Security#RC4_attacks
+# http://blog.cryptographyengineering.com/2013/03/attack-of-week-rc4-is-kind-of-broken-in.html
+run_rc4() {
+ local -i rc4_offered=0
+ local hexcode dash rc4_cipher sslvers kx auth enc mac export
+ local rc4_ciphers_list="ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:DHE-DSS-RC4-SHA:AECDH-RC4-SHA:ADH-RC4-MD5:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:RC4-MD5:RSA-PSK-RC4-SHA:PSK-RC4-SHA:KRB5-RC4-SHA:KRB5-RC4-MD5:RC4-64-MD5:EXP1024-DHE-DSS-RC4-SHA:EXP1024-RC4-SHA:EXP-ADH-RC4-MD5:EXP-RC4-MD5:EXP-RC4-MD5:EXP-KRB5-RC4-SHA:EXP-KRB5-RC4-MD5"
+
+ if [[ $VULN_COUNT -le $VULN_THRESHLD ]] || $WIDE; then
+ outln
+ pr_blue "--> Checking for vulnerable RC4 Ciphers" ; outln "\n"
+ fi
+ pr_bold " RC4"; out " (CVE-2013-2566, CVE-2015-2808) "
+
+ $OPENSSL ciphers -V 'RC4:@STRENGTH' >$TMPFILE 2>$ERRFILE # -V doesn't work with openssl < 1.0, feeding this into the while loop below
+ $OPENSSL s_client -cipher $rc4_ciphers_list $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI &>/dev/null </dev/null
+ if [[ $? -eq 0 ]]; then
+ # FF >=39 won't connect to them unless it's in this white list: http://mxr.mozilla.org/mozilla-central/source/security/manager/ssl/IntolerantFallbackList.inc
+ pr_litered "VULNERABLE (NOT ok): "
+ $WIDE && outln "\n"
+ rc4_offered=1
+ $WIDE && neat_header
+ while read hexcode dash rc4_cipher sslvers kx auth enc mac; do
+ $OPENSSL s_client -cipher $rc4_cipher $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI </dev/null &>/dev/null
+ ret=$? # here we have a fp with openssl < 1.0
+ if [[ $ret -ne 0 ]] && [[ "$SHOW_EACH_C" -eq 0 ]]; then
+ continue # no successful connect AND not verbose displaying each cipher
+ fi
+ if $WIDE; then
+ normalize_ciphercode $hexcode
+ neat_list $HEXC $rc4_cipher $kx $enc
+ if [[ "$SHOW_EACH_C" -ne 0 ]]; then
+ if [[ $ret -eq 0 ]]; then
+ pr_litered "available"
+ else
+ out "not a/v"
+ fi
+ else
+ rc4_offered=1
+ out
+ fi
+ outln
+ else
+ pr_litered "$rc4_cipher "
+ fi
+ done < $TMPFILE
+ # ^^^^^ posix redirect as shopt will either segfault or doesn't work with old bash versions
+ outln
+ else
+ pr_litegreenln "no RC4 ciphers detected (OK)"
+ rc4_offered=0
+ fi
+ outln
+
+ tmpfile_handle $FUNCNAME.txt
+ return $rc4_offered
+}
+
+
+run_youknowwho() {
+# CVE-2013-2566,
+# NOT FIXME as there's no code: http://www.isg.rhul.ac.uk/tls/
+# http://blog.cryptographyengineering.com/2013/03/attack-of-week-rc4-is-kind-of-broken-in.html
+return 0
+# in a nutshell: don't use RC4, really not!
+}
+
+# https://www.usenix.org/conference/woot13/workshop-program/presentation/smyth
+# https://secure-resumption.com/tlsauth.pdf
+run_tls_truncation() {
+#FIXME: difficult to test, is there any test available: pls let me know
+:
+}
+
+old_fart() {
+ outln "Get precompiled bins or compile https://github.com/PeterMosmans/openssl ."
+ fatal "Your $OPENSSL $OSSL_VER version is an old fart... . It doesn\'t make much sense to proceed." -2
+}
+
+# try very hard to determine th install path to get ahold of the mapping file
+# it provides "keycode/ RFC style name", see RFCs, cipher(1), www.carbonwind.net/TLS_Cipher_Suites_Project/tls_ssl_cipher_suites_simple_table_all.htm
+get_install_dir() {
+ #INSTALL_DIR=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
+ INSTALL_DIR=$(dirname ${BASH_SOURCE[0]})
+
+ [[ -r "$RUN_DIR/mapping-rfc.txt" ]] && MAPPING_FILE_RFC="$RUN_DIR/mapping-rfc.txt"
+ [[ -r "$INSTALL_DIR/mapping-rfc.txt" ]] && MAPPING_FILE_RFC="$INSTALL_DIR/mapping-rfc.txt"
+
+ # we haven't found the mapping file yet...
+ if [[ ! -r "$MAPPING_FILE_RFC" ]] && which readlink &>/dev/null ; then
+ readlink -f ls &>/dev/null && \
+ INSTALL_DIR=$(readlink -f $(basename ${BASH_SOURCE[0]})) || \
+ INSTALL_DIR=$(readlink $(basename ${BASH_SOURCE[0]}))
+ # not sure whether Darwin has -f
+ INSTALL_DIR=$(dirname $INSTALL_DIR 2>/dev/null)
+ [[ -r "$INSTALL_DIR/mapping-rfc.txt" ]] && MAPPING_FILE_RFC="$INSTALL_DIR/mapping-rfc.txt"
+ fi
+
+ # still no mapping file:
+ if [[ ! -r "$MAPPING_FILE_RFC" ]] && which realpath &>/dev/null ; then
+ INSTALL_DIR=$(dirname $(realpath ${BASH_SOURCE[0]}))
+ MAPPING_FILE_RFC="$INSTALL_DIR/mapping-rfc.txt"
+ fi
+
+ [[ ! -r "$MAPPING_FILE_RFC" ]] && unset MAPPING_FILE_RFC && pr_litemagentaln "\nNo mapping file found"
+ debugme echo "$MAPPING_FILE_RFC"
+}
+
+
+test_openssl_suffix() {
+ local naming_ext="$(uname).$(uname -m)"
+ local uname_arch="$(uname -m)"
+ local myarch_suffix=""
+
+ [[ $uname_arch =~ 64 ]] && myarch_suffix=64 || myarch_suffix=32
+ if [[ -f "$1/openssl" ]] && [[ -x "$1/openssl" ]]; then
+ OPENSSL="$1/openssl"
+ return 0
+ elif [[ -f "$1/openssl.$naming_ext" ]] && [[ -x "$1/openssl.$naming_ext" ]]; then
+ OPENSSL="$1/openssl.$naming_ext"
+ return 0
+ elif [[ -f "$1/openssl.$uname_arch" ]] && [[ -x "$1/openssl.$uname_arch" ]]; then
+ OPENSSL="$1/openssl.$uname_arch"
+ return 0
+ elif [[ -f "$1/openssl$myarch_suffix" ]] && [[ -x "$1/openssl$myarch_suffix" ]]; then
+ OPENSSL="$1/openssl$myarch_suffix"
+ return 0
+ fi
+ return 1
+}
+
+
+find_openssl_binary() {
+ # 0. check environment variable whether it's executable
+ if [[ -n "$OPENSSL" ]] && [[ ! -x "$OPENSSL" ]]; then
+ pr_litemagentaln "\ncannot find specified (\$OPENSSL=$OPENSSL) binary."
+ outln " Looking some place else ..."
+ elif [[ -x "$OPENSSL" ]]; then
+ : # 1. all ok supplied $OPENSSL was found and has excutable bit set -- testrun comes below
+ elif test_openssl_suffix $RUN_DIR; then
+ : # 2. otherwise try openssl in path of testssl.sh
+ elif test_openssl_suffix $RUN_DIR/bin; then
+ : # 3. otherwise here, this is supposed to be the standard --platform independed path in the future!!!
+ elif test_openssl_suffix "$(dirname "$(which openssl)")"; then
+ : # 5. we tried hard and failed, so now we use the system binaries
+ fi
+
+ # no ERRFILE initialized yet, thus we use /dev/null for stderr directly
+ $OPENSSL version -a 2>/dev/null >/dev/null
+ if [[ $? -ne 0 ]] || [[ ! -x "$OPENSSL" ]]; then
+ fatal "\ncannot exec or find any openssl binary" -1
+ fi
+
+ # http://www.openssl.org/news/openssl-notes.html
+ OSSL_VER=$($OPENSSL version 2>/dev/null | awk -F' ' '{ print $2 }')
+ OSSL_VER_MAJOR=$(echo "$OSSL_VER" | sed 's/\..*$//')
+ OSSL_VER_MINOR=$(echo "$OSSL_VER" | sed -e 's/^.\.//' | tr -d '[a-zA-Z]-')
+ OSSL_VER_APPENDIX=$(echo "$OSSL_VER" | tr -d '0-9.')
+ OSSL_VER_PLATFORM=$($OPENSSL version -p 2>/dev/null | sed 's/^platform: //')
+ OSSL_BUILD_DATE=$($OPENSSL version -a 2>/dev/null | grep '^built' | sed -e 's/built on//' -e 's/: ... //' -e 's/: //' -e 's/ UTC//' -e 's/ +0000//' -e 's/.000000000//')
+ echo $OSSL_BUILD_DATE | grep -q "not available" && OSSL_BUILD_DATE=""
+
+ if $OPENSSL version 2>/dev/null | grep -qi LibreSSL; then
+ HAS_DH_BITS=false # as of version 2.2.1
+ else
+ [[ $OSSL_VER_MAJOR -ne 1 ]] && HAS_DH_BITS=false
+ [[ "$OSSL_VER_MINOR" == "0.1" ]] && HAS_DH_BITS=false
+ fi
+
+ if $OPENSSL version 2>/dev/null | grep -qi LibreSSL; then
+ outln
+ pr_litemagenta "Please note: LibreSSL is not a good choice for testing INSECURE features!"
+ fi
+
+ $OPENSSL s_client -ssl2 2>&1 | grep -aq "unknown option" || \
+ HAS_SSL2=true && \
+ HAS_SSL2=false
+ $OPENSSL s_client -ssl3 2>&1 | grep -aq "unknown option" || \
+ HAS_SSL3=true && \
+ HAS_SSL3=false
+
+ return 0
+}
+
+openssl_age() {
+ case "$OSSL_VER" in
+ 0.9.7*|0.9.6*|0.9.5*)
+ # 0.9.5a was latest in 0.9.5 an released 2000/4/1, that'll NOT suffice for this test
+ old_fart ;;
+ 0.9.8)
+ case $OSSL_VER_APPENDIX in
+ a|b|c|d|e) old_fart;; # no SNI!
+ # other than that we leave this for MacOSX and FreeBSD but it's a pain and likely gives false negatives/positives
+ esac
+ ;;
+ esac
+ if [[ $OSSL_VER_MAJOR -lt 1 ]]; then ## mm: Patch for libressl
+ pr_magentaln " Your \"$OPENSSL\" is way too old (<version 1.0) !"
+ case $SYSTEM in
+ *BSD|Darwin)
+ outln " Please use binary provided in \$INSTALLDIR/bin/ or from ports/brew or compile from github.com/PeterMosmans/openssl" ;;
+ *) outln " Update openssl binaries or compile from github.com/PeterMosmans/openssl" ;;
+ esac
+ ignore_no_or_lame " Type \"yes\" to accept some false negatives or positives "
+ fi
+ outln
+}
+
+
+help() {
+ cat << EOF
+
+$PROG_NAME <options>
+
+ -h, --help what you're looking at
+ -b, --banner displays banner + version of $PROG_NAME
+ -v, --version same as previous
+ -V, --local pretty print all local ciphers
+ -V, --local <pattern> which local ciphers with <pattern> are available?
+ (if pattern not a number: word match)
+
+$PROG_NAME <options> URI ("$PROG_NAME URI" does everything except -E)
+
+ -e, --each-cipher checks each local cipher remotely
+ -E, --cipher-per-proto checks those per protocol
+ -f, --ciphers checks common cipher suites
+ -p, --protocols checks TLS/SSL protocols
+ -S, --server_defaults displays the servers default picks and certificate info
+ -P, --preference displays the servers picks: protocol+cipher
+ -y, --spdy, --npn checks for SPDY/NPN
+ -x, --single-cipher <pattern> tests matched <pattern> of ciphers
+ (if <pattern> not a number: word match)
+ -U, --vulnerable tests all vulnerabilities
+ -B, --heartbleed tests for heartbleed vulnerability
+ -I, --ccs, --ccs-injection tests for CCS injection vulnerability
+ -R, --renegotiation tests for renegotiation vulnerabilities
+ -C, --compression, --crime tests for CRIME vulnerability
+ -T, --breach tests for BREACH vulnerability
+ -O, --poodle tests for POODLE (SSL) vulnerability
+ -Z, --tls-fallback checks TLS_FALLBACK_SCSV mitigation
+ -F, --freak tests for FREAK vulnerability
+ -A, --beast tests for BEAST vulnerability
+ -J, --logjam tests for LOGJAM vulnerability
+ -s, --pfs, --fs,--nsa checks (perfect) forward secrecy settings
+ -4, --rc4, --appelbaum which RC4 ciphers are being offered?
+ -H, --header, --headers tests HSTS, HPKP, server/app banner, security headers, cookie, reverse proxy, IPv4 address
+
+ special invocations:
+
+ -t, --starttls <protocol> does a default run against a STARTTLS enabled <protocol>
+ --xmpphost <to_domain> for STARTTLS enabled XMPP it supplies the XML stream to-'' domain -- sometimes needed
+ --mx <domain/host> tests MX records from high to low priority (STARTTLS, port 25)
+ --ip <ipv4> a) tests the supplied <ipv4> instead of resolving host(s) in URI
+ b) arg "one" means: just test the first DNS returns (useful for multiple IPs)
+ --file <file name> mass testing option: Just put multiple $PROG_NAME command lines in <file name>,
+ one line per instance. Comments via # allowed, EOF signals end of <file name>.
+
+partly mandatory parameters:
+
+ URI host|host:port|URL|URL:port (port 443 is assumed unless otherwise specified)
+ pattern an ignore case word pattern of cipher hexcode or any other string in the name, kx or bits
+ protocol is one of ftp,smtp,pop3,imap,xmpp,telnet,ldap (for the latter two you need e.g. the supplied openssl)
+
+tuning options:
+
+ --assuming-http if protocol check fails it assumes HTTP protocol and enforces HTTP checks
+ --ssl-native fallback to checks with OpenSSL where sockets are normally used
+ --openssl <PATH> use this openssl binary (default: look in \$PATH, \$RUN_DIR of $PROG_NAME
+ --proxy <host>:<port> connect via the specified HTTP proxy
+ --sneaky be less verbose wrt referer headers
+ --quiet don't output the banner. By doing this you acknowledge usage terms normally appearing in the banner
+ --wide wide output for tests like RC4, BEAST. PFS also with hexcode, kx, strength, RFC name
+ --show-each for wide outputs: display all ciphers tested -- not only succeeded ones
+ --warnings <batch|off|false> "batch" doesn't wait for keypress, "off" or "false" skips connection warning
+ --color <0|1|2> 0: no escape or other codes, 1: b/w escape codes, 2: color (default)
+ --debug <0-6> 1: screen output normal but debug output in temp files. 2-6: see line ~105
+
+All options requiring a value can also be called with '=' (e.g. testssl.sh -t=smtp --wide --openssl=/usr/bin/openssl <URI>.
+<URI> is always the last parameter.
+
+Need HTML output? Just pipe through "aha" (Ansi HTML Adapter: github.com/theZiz/aha) like
+
+ "$PROG_NAME <options> <URI> | aha >output.html"
+EOF
+ exit $1
+}
+
+
+mybanner() {
+ local nr_ciphers
+ local idtag
+ local bb
+ local openssl_location="$(which $OPENSSL)"
+ local cwd=""
+
+ $QUIET && return
+ nr_ciphers=$(count_ciphers "$($OPENSSL ciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>/dev/null)")
+ [[ -z "$GIT_REL" ]] && \
+ idtag="$CVS_REL" || \
+ idtag="$GIT_REL -- $CVS_REL_SHORT"
+ [[ "$COLOR" -ne 0 ]] && idtag="\033[1;30m$idtag\033[m\033[1m"
+ bb=$(cat <<EOF
+
+###########################################################
+ $PROG_NAME $VERSION from $SWURL
+ ($idtag)
+
+ This program is free software. Distribution and
+ modification under GPLv2 permitted.
+ USAGE w/o ANY WARRANTY. USE IT AT YOUR OWN RISK!
+
+ Please file bugs @ https://testssl.sh/bugs/
+
+###########################################################
+EOF
+)
+ pr_bold "$bb"
+ outln "\n"
+ outln " Using \"$($OPENSSL version 2>/dev/null)\" [~$nr_ciphers ciphers] on"
+ out " $(hostname):"
+
+ [[ -n "$GIT_REL" ]] && \
+ cwd=$(/bin/pwd) || \
+ cwd=$RUN_DIR
+ if [[ "$openssl_location" =~ $(/bin/pwd)/bin ]]; then
+ echo "\$PWD/bin/$(basename "$openssl_location")"
+ elif [[ "$openssl_location" =~ $cwd ]] && [[ "$cwd" != '.' ]]; then
+ echo "${openssl_location%%$cwd}"
+ else
+ echo "$openssl_location"
+ fi
+ outln " (built: \"$OSSL_BUILD_DATE\", platform: \"$OSSL_VER_PLATFORM\")\n"
+}
+
+
+maketempf() {
+ TEMPDIR=$(mktemp -d /tmp/ssltester.XXXXXX) || exit -6
+ TMPFILE=$TEMPDIR/tempfile.txt || exit -6
+ if [[ "$DEBUG" -eq 0 ]]; then
+ ERRFILE="/dev/null"
+ else
+ ERRFILE=$TEMPDIR/errorfile.txt || exit -6
+ >$ERRFILE
+ fi
+ HOSTCERT=$TEMPDIR/host_certificate.txt
+ HEADERFILE=$TEMPDIR/http_header.txt
+ HEADERFILE_BREACH=$TEMPDIR/http_header_breach.txt
+ LOGFILE=$TEMPDIR/logfile.txt
+ initialize_engine
+ if [[ $DEBUG -ne 0 ]]; then
+ cat >$TEMPDIR/environment.txt << EOF
+
+CVS_REL: $CVS_REL
+GIT_REL: $GIT_REL
+
+PID: $$
+bash version: ${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}.${BASH_VERSINFO[2]}
+status: ${BASH_VERSINFO[4]}
+machine: ${BASH_VERSINFO[5]}
+operating system: $SYSTEM
+shellopts: $SHELLOPTS
+
+$OPENSSL version -a:
+$($OPENSSL version -a)
+OSSL_VER_MAJOR: $OSSL_VER_MAJOR
+OSSL_VER_MINOR: $OSSL_VER_MINOR
+OSSL_VER_APPENDIX: $OSSL_VER_APPENDIX
+OSSL_BUILD_DATE: "$OSSL_BUILD_DATE"
+OSSL_VER_PLATFORM: "$OSSL_VER_PLATFORM"
+
+OPENSSL_CONF: $OPENSSL_CONF
+
+PATH: $PATH
+PROG_NAME: $PROG_NAME
+INSTALL_DIR: $INSTALL_DIR
+RUN_DIR: $RUN_DIR
+MAPPING_FILE_RFC: $MAPPING_FILE_RFC
+
+
+CAPATH: $CAPATH
+ECHO: $ECHO
+COLOR: $COLOR
+TERM_DWITH: $TERM_DWITH
+HAS_GNUDATE: $HAS_GNUDATE
+HAS_SED_E: $HAS_SED_E
+
+SHOW_EACH_C: $SHOW_EACH_C
+SSL_NATIVE: $SSL_NATIVE
+ASSUMING_HTTP $ASSUMING_HTTP
+SNEAKY: $SNEAKY
+
+DEBUG: $DEBUG
+
+HSTS_MIN: $HSTS_MIN
+HPKP_MIN: $HPKP_MIN
+CLIENT_MIN_PFS: $CLIENT_MIN_PFS
+DAYS2WARN1: $DAYS2WARN1
+DAYS2WARN2: $DAYS2WARN2
+
+HEADER_MAXSLEEP: $HEADER_MAXSLEEP
+MAX_WAITSOCK: $MAX_WAITSOCK
+HEARTBLEED_MAX_WAITSOCK: $HEARTBLEED_MAX_WAITSOCK
+CCS_MAX_WAITSOCK: $CCS_MAX_WAITSOCK
+USLEEP_SND $USLEEP_SND
+USLEEP_REC $USLEEP_REC
+
+
+EOF
+ which locale &>/dev/null && locale >>$TEMPDIR/environment.txt || echo "locale doesn't exist" >>$TEMPDIR/environment.txt
+ $OPENSSL ciphers -V 'ALL:COMPLEMENTOFALL' &>$TEMPDIR/all_local_ciphers.txt
+ fi
+
+
+}
+
+cleanup () {
+ if [[ "$DEBUG" -ge 1 ]]; then
+ outln
+ pr_underline "DEBUG (level $DEBUG): see files in $TEMPDIR"
+ outln
+ else
+ [[ -d "$TEMPDIR" ]] && rm -rf "$TEMPDIR";
+ fi
+ outln
+}
+
+fatal() {
+ pr_magentaln "Fatal error: $1" >&2
+ exit $2
+}
+
+
+# for now only GOST engine
+initialize_engine(){
+ grep -q '^# testssl config file' "$OPENSSL_CONF" 2>/dev/null && return 0 # have been here already
+
+ if ! $OPENSSL engine gost -vvvv -t -c 2>/dev/null >/dev/null; then
+ outln
+ pr_litemagenta "No engine or GOST support via engine with your $OPENSSL"; outln
+ return 1
+ elif $OPENSSL engine gost -vvvv -t -c 2>&1 | grep -iq "No such" ; then
+ outln
+ pr_litemagenta "No engine or GOST support via engine with your $OPENSSL"; outln
+ return 1
+ else # we have engine support
+ if [[ -n "$OPENSSL_CONF" ]]; then
+ pr_litemagentaln "For now I am providing the config file in to have GOST support"
+ else
+ OPENSSL_CONF=$TEMPDIR/gost.conf || exit -6
+ # see https://www.mail-archive.com/openssl-users@openssl.org/msg65395.html
+ cat >$OPENSSL_CONF << EOF
+# testssl config file for openssl
+
+openssl_conf = openssl_def
+
+[ openssl_def ]
+engines = engine_section
+
+[ engine_section ]
+gost = gost_section
+
+[ gost_section ]
+engine_id = gost
+default_algorithms = ALL
+CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet
+
+EOF
+ export OPENSSL_CONF
+ fi
+ fi
+ return 0
+}
+
+
+ignore_no_or_lame() {
+ local a
+
+ [[ "$WARNINGS" == "off" ]] && return 0
+ [[ "$WARNINGS" == "false" ]] && return 0
+ [[ "$WARNINGS" == "batch" ]] && return 1
+ pr_magenta "$1 "
+ read a
+ case $a in
+ Y|y|Yes|YES|yes) return 0;;
+ default) ;;
+ esac
+ return 1
+}
+
+# arg1: URI
+# arg2: protocol
+parse_hn_port() {
+ local tmp_port
+
+ NODE="$1"
+ # strip "https" and trailing urlpath supposed it was supplied additionally
+ echo "$NODE" | grep -q 'https://' && NODE=$(echo "$NODE" | sed -e 's/^https\:\/\///')
+
+ # strip trailing urlpath
+ NODE=$(echo "$NODE" | sed -e 's/\/.*$//')
+
+ # was the address supplied like [AA:BB:CC::]:port ?
+ if echo "$NODE" | grep -q ']' ; then
+ tmp_port=$(printf "$NODE" | sed 's/\[.*\]//' | sed 's/://')
+ # determine v6 port, supposed it was supplied additionally
+ if [[ -n "$tmp_port" ]]; then
+ PORT=$tmp_port
+ NODE=$(sed "s/:$PORT//" <<< "$NODE")
+ fi
+ NODE=$(sed -e 's/\[//' -e 's/\]//' <<< "$NODE")
+ else
+ # determine v4 port, supposed it was supplied additionally
+ echo "$NODE" | grep -q ':' && \
+ PORT=$(echo "$NODE" | sed 's/^.*\://') && NODE=$(echo "$NODE" | sed 's/\:.*$//')
+ fi
+ debugme echo $NODE:$PORT
+ SNI="-servername $NODE"
+
+ URL_PATH=$(echo "$1" | sed 's/https:\/\///' | sed 's/'"${NODE}"'//' | sed 's/.*'"${PORT}"'//') # remove protocol and node part and port
+ URL_PATH=$(echo "$URL_PATH" | sed 's/\/\//\//g') # we rather want // -> /
+ [[ -z "$URL_PATH" ]] && URL_PATH="/"
+ debugme echo $URL_PATH
+ return 0 # NODE, URL_PATH, PORT is set now
+}
+
+
+is_ipv4addr() {
+ local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
+ local ipv4address="$octet\\.$octet\\.$octet\\.$octet"
+
+ [[ -z "$1" ]] && return 1
+ # more than numbers, important for hosts like AAA.BBB.CCC.DDD.in-addr.arpa.DOMAIN.TLS
+ [[ -n $(tr -d '0-9\.' <<< "$1") ]] && return 1
+
+ echo -n "$1" | grep -Eq "$ipv4address" && \
+ return 0 || \
+ return 1
+}
+
+# a bit easier
+is_ipv6addr() {
+ [[ -z "$1" ]] && return 1
+ # less than 2x ":"
+ [[ $(count_lines "$(echo -n "$1" | tr ':' '\n')") -le 1 ]] && \
+ return 1
+ #check on chars allowed:
+ [[ -n "$(tr -d '0-9:a-fA-F ' <<< "$1" | sed -e '/^$/d')" ]] && \
+ return 1
+ return 0
+}
+
+
+# args: string containing ip addresses
+filter_ip6_address() {
+ local a
+
+ for a in "$@"; do
+ if ! is_ipv6addr "$a"; then
+ continue
+ fi
+ if $HAS_SED_E; then
+ echo "$a" | sed -E 's/^abcdeABCDEFf0123456789:]//g' | sed -e '/^$/d' -e '/^;;/d'
+ else
+ echo "$a" | sed -r 's/[^abcdefABCDEF0123456789:]//g' | sed -e '/^$/d' -e '/^;;/d'
+ fi
+ done
+}
+
+filter_ip4_address() {
+ local a
+
+ for a in "$@"; do
+ if ! is_ipv4addr "$a"; then
+ continue
+ fi
+ if $HAS_SED_E; then
+ echo "$a" | sed -E 's/[^[:digit:].]//g' | sed -e '/^$/d'
+ else
+ echo "$a" | sed -r 's/[^[:digit:].]//g' | sed -e '/^$/d'
+ fi
+ done
+}
+
+get_local_aaaa() {
+ local ip6=""
+ local etchosts="/etc/hosts /c/Windows/System32/drivers/etc/hosts"
+
+ # for security testing sometimes we have local entries. Getent is BS under Linux for localhost: No network, no resolution
+ ip6=$(grep -wh "$NODE" $etchosts 2>/dev/null | grep ':' | grep -v '^#' | egrep "[[:space:]]$NODE" | awk '{ print $1 }')
+ if is_ipv6addr "$ip6"; then
+ echo "$ip6"
+ else
+ echo ""
+ fi
+}
+
+get_local_a() {
+ local ip4=""
+ local etchosts="/etc/hosts /c/Windows/System32/drivers/etc/hosts"
+
+ # for security testing sometimes we have local entries. Getent is BS under Linux for localhost: No network, no resolution
+ ip4=$(grep -wh "$1" $etchosts 2>/dev/null | egrep -v ':|^#' | egrep "[[:space:]]$1" | awk '{ print $1 }')
+ if is_ipv4addr "$ip4"; then
+ echo "$ip4"
+ else
+ echo ""
+ fi
+}
+
+# arg1: a host name. Returned will be 0-n IPv4 addresses
+get_a_record() {
+ local ip4=""
+ local saved_openssl_conf="$OPENSSL_CONF"
+
+ OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134
+ if [[ -z "$ip4" ]]; then
+ which dig &> /dev/null && \
+ ip4=$(filter_ip4_address $(dig +short -t a "$1" 2>/dev/null | sed '/^;;/d'))
+ fi
+ if [[ -z "$ip4" ]]; then
+ which host &> /dev/null && \
+ ip4=$(filter_ip4_address $(host -t a "$1" 2>/dev/null | grep -v alias | sed 's/^.*address //'))
+ fi
+ if [[ -z "$ip4" ]]; then
+ if which nslookup &>/dev/null; then
+ # filtering from Name to EOF, remove iline with 'Name', the filter out non-numbers and ".'", and empty lines
+ ip4=$(filter_ip4_address $(nslookup -querytype=a "$1" 2>/dev/null | awk '/^Name/,/EOF/ { print $0 }' | grep -v Name))
+ fi
+ fi
+ OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134
+ echo "$ip4"
+}
+
+# arg1: a host name. Returned will be 0-n IPv6 addresses
+get_aaaa_record() {
+ local ip6=""
+ local saved_openssl_conf="$OPENSSL_CONF"
+
+ OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134
+ if [[ -z "$ip6" ]]; then
+ if which host &> /dev/null ; then
+ ip6=$(filter_ip6_address $(host -t aaaa "$NODE" | grep -v alias | grep -v "no AAAA record" | sed 's/^.*address //'))
+ elif which dig &> /dev/null; then
+ ip6=$(filter_ip6_address $(dig +short -t aaaa "$NODE" 2>/dev/null))
+ elif which nslookup &>/dev/null; then
+ # same as above. Only we're using grep -A instead of awk
+ ip6=$(filter_ip6_address $(nslookup -type=aaaa "$NODE" 2>/dev/null | grep -A10 Name | grep -v Name))
+ fi
+ fi
+ OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134
+ echo "$ip6"
+}
+
+
+# now get all IP addresses
+determine_ip_addresses() {
+ local ip4=""
+ local ip6=""
+
+ if is_ipv4addr "$NODE"; then
+ ip4="$NODE" # only an IPv4 address was supplied as an argument, no hostname
+ SNI="" # override Server Name Indication as we test the IP only
+ else
+ ip4=$(get_local_a $NODE) # is there a local host entry?
+ if [[ -z $ip4 ]]; then # empty: no (LOCAL_A is predefined as false)
+ ip4=$(get_a_record $NODE)
+ else
+ LOCAL_A=true # we have the ip4 from local host entry and need to set this
+ fi
+ # same now for ipv6 (though not supported) <-- can't do this yet as it shows up under "further IP addresses"
+ # and we didn't bother to show the fact that it is local there
+ ip6=$(get_local_aaaa $NODE)
+ #if [[ -z $ip6 ]]; then
+ ip6=$(get_aaaa_record $NODE)
+ #else
+ # LOCAL_AAAA=true # we have the ip4 from local host entry and need to set this
+ #fi
+ fi
+ IPADDRs=$(newline_to_spaces "$ip4")
+ if [[ -z "$IPADDRs" ]] && [[ -z "$CMDLINE_IP" ]]; then
+ fatal "No IPv4 address for \"$NODE\" available" -1
+ fi
+ [[ -z "$ip6" ]] && IP46ADDRs="$IPADDRs" || IP46ADDRs="$ip4 $ip6"
+ IP46ADDRs=$(newline_to_spaces "$IP46ADDRs")
+ return 0 # IPADDR and IP46ADDR is set now
+}
+
+determine_rdns() {
+ local saved_openssl_conf="$OPENSSL_CONF"
+ OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134
+
+ if which dig &> /dev/null; then
+ rDNS=$(dig -x $NODEIP +noall +answer | awk '/PTR/ { print $NF }') # +short returns also CNAME, e.g. openssl.org
+ elif which host &> /dev/null; then
+ rDNS=$(host -t PTR $NODEIP 2>/dev/null | awk '/pointer/ { print $NF }')
+ elif which nslookup &> /dev/null; then
+ rDNS=$(nslookup -type=PTR $NODEIP 2>/dev/null | grep -v 'canonical name =' | grep 'name = ' | awk '{ print $NF }' | sed 's/\.$//')
+ fi
+ OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134
+ rDNS=$(echo $rDNS)
+ [[ -z "$rDNS" ]] && rDNS="--"
+ return 0
+}
+
+get_mx_record() {
+ local mx=""
+ local saved_openssl_conf="$OPENSSL_CONF"
+
+ OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134
+ if which host &> /dev/null; then
+ mxs=$(host -t MX "$1" 2>/dev/null | grep 'handled by' | sed -e 's/^.*by //g' -e 's/\.$//')
+ elif which dig &> /dev/null; then
+ mxs=$(dig +short -t MX "$1" 2>/dev/null)
+ elif which nslookup &> /dev/null; then
+ mxs=$(nslookup -type=MX "$1" 2>/dev/null | grep 'mail exchanger = ' | sed 's/^.*mail exchanger = //g')
+ else
+ fatal "No dig, host or nslookup" -3
+ fi
+ OPENSSL_CONF="$saved_openssl_conf"
+ echo "$mxs"
+}
+
+# We need to get the IP address of the proxy so we can use it in fd_socket
+check_proxy(){
+ local save_LOCAL_A=$LOCAL_A
+ local save_LOCAL_AAAA=$LOCAL_AAAA
+
+ if [[ -n "$PROXY" ]]; then
+ if ! $OPENSSL s_client help 2>&1 | grep -qw proxy; then
+ fatal "Your $OPENSSL is too old to support the \"--proxy\" option" -1
+ fi
+ PROXYNODE=${PROXY%:*}
+ PROXYPORT=${PROXY#*:}
+
+ PROXYIP=$(get_a_record $PROXYNODE 2>/dev/null | grep -v alias | sed 's/^.*address //')
+ LOCAL_A=$save_LOCAL_A
+ LOCAL_AAAA=$save_LOCAL_AAAA
+ # no RFC 1918:
+ #if ! is_ipv4addr $PROXYIP ; then
+ [[ -z "$PROXYIP" ]] && fatal "Proxy IP cannot be determined from \"$PROXYNODE\"" "-3"
+ PROXY="-proxy $PROXYIP:$PROXYPORT"
+ fi
+}
+
+
+# this function determines OPTIMAL_PROTO. It is a workaround function as under certain circumstances
+# openssl 1.0.2 (as opposed to 1.0.1) needs a protocol otherwise s_client -connect will fail!
+# Circumstances so far: 1.) IIS 6 2.) starttls + dovecot imap
+# The first try in the loop is empty as we prefer not to specify always a protocol f it works w/o.
+#
+determine_optimal_proto() {
+ local all_failed
+ local addcmd=""
+
+ #TODO: maybe qeury known openssl version before this workaround
+
+ if [[ -n "$1" ]]; then
+ # starttls workaround needed see https://github.com/drwetter/testssl.sh/issues/188
+ # kind of odd
+ for STARTTLS_OPTIMAL_PROTO in -tls1_2 -tls1 -ssl3 -tls1_1 -ssl2; do
+ $OPENSSL s_client $STARTTLS_OPTIMAL_PROTO -connect "$NODEIP:$PORT" $PROXY -starttls $1 </dev/null &>/dev/null && all_failed=1 && break
+ all_failed=0
+ done
+ debugme echo "STARTTLS_OPTIMAL_PROTO: $STARTTLS_OPTIMAL_PROTO"
+ else
+ for OPTIMAL_PROTO in '' -tls1_2 -tls1 -ssl3 -tls1_1 -ssl2 ''; do
+ $OPENSSL s_client $OPTIMAL_PROTO -connect "$NODEIP:$PORT" $PROXY $SNI </dev/null &>/dev/null && all_failed=1 && break
+ all_failed=0
+ done
+ debugme echo "OPTIMAL_PROTO: $OPTIMAL_PROTO"
+ fi
+
+ if [[ $all_failed -eq 0 ]]; then
+ outln
+ pr_boldln " $NODEIP:$PORT doesn't seem a TLS/SSL enabled server or it requires a certificate";
+ ignore_no_or_lame " Note that the results might look ok but they are nonsense. Proceed ? "
+ [[ $? -ne 0 ]] && exit -2
+ fi
+}
+
+
+# arg1: ftp smtp, pop3, imap, xmpp, telnet, ldap (maybe with trailing s)
+determine_service() {
+ local ua
+ local protocol
+
+ if ! fd_socket; then # check if we can connect to $NODEIP:$PORT
+ fatal "can't connect to \"$NODEIP:$PORT\"\nMake sure a firewall is not between you and your scanning target!" -2
+ fi
+ close_socket
+
+ datebanner "Testing"
+ outln
+ if [[ -z "$1" ]]; then # no STARTTLS. For STARTTLS we do this where it fails ("Has server cipher order")
+ determine_optimal_proto "$1" # in order to avoid unneccessary connects
+ $SNEAKY && \
+ ua="$UA_SNEAKY" || \
+ ua="$UA_STD"
+ GET_REQ11="GET $URL_PATH HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $ua\r\nConnection: Close\r\nAccept: text/*\r\n\r\n"
+ HEAD_REQ11="HEAD $URL_PATH HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $ua\r\nAccept: text/*\r\n\r\n"
+ GET_REQ10="GET $URL_PATH HTTP/1.0\r\nUser-Agent: $ua\r\nConnection: Close\r\nAccept: text/*\r\n\r\n"
+ HEAD_REQ10="HEAD $URL_PATH HTTP/1.0\r\nUser-Agent: $ua\r\nAccept: text/*\r\n\r\n"
+ runs_HTTP $OPTIMAL_PROTO
+ else # STARTTLS
+ protocol=$(echo "$1" | sed 's/s$//') # strip trailing s in ftp(s), smtp(s), pop3(s), imap(s), ldap(s), telnet(s)
+ case "$protocol" in
+ ftp|smtp|pop3|imap|xmpp|telnet|ldap)
+ STARTTLS="-starttls $protocol"
+ SNI=""
+ if [[ $protocol == "xmpp" ]]; then
+ # for XMPP, openssl has a problem using -connect $NODEIP:$PORT. thus we use -connect $NODE:$PORT instead!
+ NODEIP="$NODE"
+
+ if [[ -n "$XMPP_HOST" ]]; then
+ if ! $OPENSSL s_client --help 2>&1 | grep -q xmpphost; then
+ fatal "Your $OPENSSL does not support the \"-xmpphost\" option" -3
+ fi
+ STARTTLS="$STARTTLS -xmpphost $XMPP_HOST" # it's a hack -- instead of changing calls all over the place
+ # see http://xmpp.org/rfcs/rfc3920.html
+ fi
+ fi
+ $OPENSSL s_client -connect $NODEIP:$PORT $PROXY $STARTTLS 2>$ERRFILE >$TMPFILE </dev/null
+ if [[ $? -ne 0 ]]; then
+ debugme cat $TMPFILE
+ outln
+ fatal " $OPENSSL couldn't establish STARTTLS via $protocol to $NODEIP:$PORT" -2
+ fi
+ out " Service set: STARTTLS via "
+ toupper "$protocol"
+ [[ -n "$XMPP_HOST" ]] && echo -n " (XMPP domain=\'$XMPP_HOST\')"
+ outln
+ ;;
+ *) outln
+ fatal "momentarily only ftp, smtp, pop3, imap, xmpp, telnet and ldap allowed" -1
+ ;;
+ esac
+ fi
+ outln
+ return 0 # OPTIMAL_PROTO, GET_REQ*/HEAD_REQ* is set now
+}
+
+
+display_rdns_etc() {
+ local i
+
+ if [[ $(count_words "$(echo -n "$IP46ADDRs")") -gt 1 ]]; then
+ out " further IP addresses: "
+ for i in $IP46ADDRs; do
+ [[ "$i" == "$NODEIP" ]] && continue
+ out " $i"
+ done
+ outln
+ fi
+ [[ -n "$rDNS" ]] && printf " %-23s %s" "rDNS ($NODEIP):" "$rDNS"
+ if "$LOCAL_A"; then
+ out " (A record via /etc/hosts) "
+ fi
+}
+
+datebanner() {
+ local tojour="$(date +%F) $(date +%R)"
+
+ pr_reverse "$1 now ($tojour) ---> $NODEIP:$PORT ($NODE) <---"; outln "\n"
+ [[ "$1" == "Testing" ]] && display_rdns_etc
+}
+
+# one line with char $1 over screen width $2
+draw_dotted_line() {
+ printf -- "$1"'%.s' $(eval "echo {1.."$(($2))"}")
+}
+
+
+mx_all_ips() {
+ local mxs mx
+ local mxport
+ local -i ret=0
+
+ STARTTLS_PROTOCOL="smtp"
+
+ # test first higher priority servers
+ mxs=$(get_mx_record "$1" | sort -n | sed -e 's/^.* //' -e 's/\.$//' | tr '\n' ' ')
+ mxport=${2:-25}
+ if [[ -n "$mxs" ]] && [[ "$mxs" != ' ' ]]; then
+ [[ $mxport == "465" ]] && \
+ STARTTLS_PROTOCOL="" # no starttls for Port 465, on all other ports we speak starttls
+ pr_bold "Testing now all MX records (on port $mxport): "; outln "$mxs"
+ for mx in $mxs; do
+ draw_dotted_line "-" $((TERM_DWITH * 2 / 3))
+ outln
+ parse_hn_port "$mx:$mxport"
+ determine_ip_addresses || continue
+ if [[ $(count_words "$(echo -n "$IPADDRs")") -gt 1 ]]; then # we have more than one ipv4 address to check
+ pr_bold "Testing all IPv4 addresses (port $PORT): "; outln "$IPADDRs"
+ for ip in $IPADDRs; do
+ NODEIP="$ip"
+ lets_roll "${STARTTLS_PROTOCOL}"
+ done
+ else
+ NODEIP="$IPADDRs"
+ lets_roll "${STARTTLS_PROTOCOL}"
+ fi
+ ret=$(($? + ret))
+ done
+ draw_dotted_line "-" $((TERM_DWITH * 2 / 3))
+ outln
+ pr_bold "Done testing now all MX records (on port $mxport): "; outln "$mxs"
+ else
+ pr_boldln " $1 has no MX records(s)"
+ fi
+ return $ret
+}
+
+
+# This initializes boolean global do_* variables. They keep track of what to do
+# -- as the name insinuates
+initialize_globals() {
+ do_allciphers=false
+ do_vulnerabilities=false
+ do_beast=false
+ do_breach=false
+ do_ccs_injection=false
+ do_cipher_per_proto=false
+ do_crime=false
+ do_freak=false
+ do_logjam=false
+ do_header=false
+ do_heartbleed=false
+ do_mx_all_ips=false
+ do_read_from_file=false
+ do_pfs=false
+ do_protocols=false
+ do_rc4=false
+ do_renego=false
+ do_std_cipherlists=false
+ do_server_defaults=false
+ do_server_preference=false
+ do_spdy=false
+ do_ssl_poodle=false
+ do_tls_fallback_scsv=false
+ do_test_just_one=false
+ do_tls_sockets=false
+}
+
+
+# Set default scanning options for the boolean global do_* variables.
+set_scanning_defaults() {
+ do_allciphers=true
+ do_vulnerabilities=true
+ do_beast=true
+ do_breach=true
+ do_ccs_injection=true
+ do_crime=true
+ do_freak=true
+ do_logjam=true
+ do_header=true
+ do_heartbleed=true
+ do_pfs=true
+ do_protocols=true
+ do_rc4=true
+ do_renego=true
+ do_std_cipherlists=true
+ do_server_defaults=true
+ do_server_preference=true
+ do_spdy=true
+ do_ssl_poodle=true
+ do_tls_fallback_scsv=true
+ VULN_COUNT=10
+}
+
+query_globals() {
+ local gbl
+ local true_nr=0
+
+ for gbl in do_allciphers do_vulnerabilities do_beast do_breach do_ccs_injection do_cipher_per_proto do_crime \
+ do_freak do_logjam do_header do_heartbleed do_mx_all_ips do_pfs do_protocols do_rc4 do_renego \
+ do_std_cipherlists do_server_defaults do_server_preference do_spdy do_ssl_poodle do_tls_fallback_scsv \
+ do_test_just_one do_tls_sockets do_read_from_file; do
+ [[ "${!gbl}" == "true" ]] && let true_nr++
+ done
+ return $true_nr
+}
+
+
+debug_globals() {
+ local gbl
+
+ for gbl in do_allciphers do_vulnerabilities do_beast do_breach do_ccs_injection do_cipher_per_proto do_crime \
+ do_freak do_logjam do_header do_heartbleed do_rc4 do_mx_all_ips do_pfs do_protocols do_rc4 do_renego \
+ do_std_cipherlists do_server_defaults do_server_preference do_spdy do_ssl_poodle do_tls_fallback_scsv \
+ do_test_just_one do_tls_sockets do_read_from_file; do
+ printf "%-22s = %s\n" $gbl "${!gbl}"
+ done
+ printf "%-22s : %s\n" URI: "$URI"
+}
+
+
+# arg1+2 are just the options
+parse_opt_equal_sign() {
+ if [[ "$1" == *=* ]]; then
+ echo "$1" | awk -F'=' '{ print $2 }'
+ return 1 # = means we don't need to shift args!
+ else
+ echo $2
+ return 0 # we need to shift
+ fi
+}
+
+
+parse_cmd_line() {
+ # Set defaults if only an URI was specified, maybe ToDo: use "="-option, then: ${i#*=} i.e. substring removal
+ [[ "$#" -eq 1 ]] && set_scanning_defaults
+
+ while [[ $# -gt 0 ]]; do
+ case $1 in
+ -h|--help)
+ help 0
+ ;;
+ -b|--banner|-v|--version)
+ find_openssl_binary
+ maketempf
+ mybanner
+ exit 0
+ ;;
+ --mx)
+ do_mx_all_ips=true
+ PORT=25
+ ;;
+ --mx465) # doesn't work with major ISPs
+ do_mx_all_ips=true
+ PORT=465
+ ;;
+ --mx587) # doesn't work with major ISPs
+ do_mx_all_ips=true
+ PORT=587
+ ;;
+ --ip|--ip=*)
+ CMDLINE_IP=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ ;;
+ -V|-V=*|--local|--local=*) # this is only displaying local ciphers, thus we don't put it in the loop
+ find_openssl_binary
+ maketempf # for GOST support
+ mybanner
+ openssl_age
+ prettyprint_local $(parse_opt_equal_sign "$1" "$2")
+ exit $?
+ ;;
+ -x|-x=*|--single[-_]cipher|--single[-_]cipher=*)
+ do_test_just_one=true
+ single_cipher=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ ;;
+ -t|-t=*|--starttls|--starttls=*)
+ do_starttls=true
+ STARTTLS_PROTOCOL=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ case $STARTTLS_PROTOCOL in
+ ftp|smtp|pop3|imap|xmpp|telnet|ldap|nntp) ;;
+ ftps|smtps|pop3s|imaps|xmpps|telnets|ldaps|nntps) ;;
+ *) pr_magentaln "\nunrecognized STARTTLS protocol \"$1\", see help" 1>&2
+ help 1 ;;
+ esac
+ ;;
+ --xmpphost|--xmpphost=*)
+ XMPP_HOST=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ ;;
+ -e|--each-cipher)
+ do_allciphers=true
+ ;;
+ -E|--cipher-per-proto|--cipher_per_proto)
+ do_cipher_per_proto=true
+ ;;
+ -p|--protocols)
+ do_protocols=true
+ do_spdy=true
+ ;;
+ -y|--spdy|--npn)
+ do_spdy=true
+ ;;
+ -f|--ciphers)
+ do_std_cipherlists=true
+ ;;
+ -S|--server[-_]defaults)
+ do_server_defaults=true
+ ;;
+ -P|--server[_-]preference)
+ do_server_preference=true
+ ;;
+ -H|--header|--headers)
+ do_header=true
+ ;;
+ -U|--vulnerable)
+ do_vulnerabilities=true
+ do_heartbleed=true
+ do_ccs_injection=true
+ do_renego=true
+ do_crime=true
+ do_breach=true
+ do_ssl_poodle=true
+ do_tls_fallback_scsv=true
+ do_freak=true
+ do_logjam=true
+ do_beast=true
+ do_rc4=true
+ VULN_COUNT=10
+ ;;
+ -B|--heartbleed)
+ do_heartbleed=true
+ let "VULN_COUNT++"
+ ;;
+ -I|--ccs|--ccs[-_]injection)
+ do_ccs_injection=true
+ let "VULN_COUNT++"
+ ;;
+ -R|--renegotiation)
+ do_renego=true
+ let "VULN_COUNT++"
+ ;;
+ -C|--compression|--crime)
+ do_crime=true
+ let "VULN_COUNT++"
+ ;;
+ -T|--breach)
+ do_breach=true
+ let "VULN_COUNT++"
+ ;;
+ -O|--poodle)
+ do_ssl_poodle=true
+ do_tls_fallback_scsv=true
+ let "VULN_COUNT++"
+ ;;
+ -Z|--tls[_-]fallback|tls[_-]fallback[_-]scs)
+ do_tls_fallback_scsv=true
+ let "VULN_COUNT++"
+ ;;
+ -F|--freak)
+ do_freak=true
+ let "VULN_COUNT++"
+ ;;
+ -J|--logjam)
+ do_logjam=true
+ let "VULN_COUNT++"
+ ;;
+ -A|--beast)
+ do_beast=true
+ let "VULN_COUNT++"
+ ;;
+ -4|--rc4|--appelbaum)
+ do_rc4=true
+ let "VULN_COUNT++"
+ ;;
+ -s|--pfs|--fs|--nsa)
+ do_pfs=true
+ ;;
+ --devel) ### this development feature will soon disappear
+ HEX_CIPHER=""
+ # DEBUG=3 ./testssl.sh -q 03 "cc, 13, c0, 13" google.de
+ # DEBUG=3 ./testssl.sh -q 01 yandex.ru
+ # DEBUG=3 ./testssl.sh -q 00 <host which still supports SSLv2>
+ TLS_LOW_BYTE="$2";
+ if [[ $# -eq 4 ]]; then # protocol AND ciphers specified
+ HEX_CIPHER="$3"
+ shift
+ fi
+ shift
+ do_tls_sockets=true
+ outln "\nTLS_LOW_BYTE/HEX_CIPHER: ${TLS_LOW_BYTE}/${HEX_CIPHER}"
+ ;;
+ --wide)
+ WIDE=true
+ ;;
+ --assuming[_-]http|--assume[-_]http)
+ ASSUMING_HTTP=true
+ ;;
+ --sneaky)
+ SNEAKY=true
+ ;;
+ -q|--quiet)
+ QUIET=true
+ ;;
+ --file|--file=*)
+ # no shift here as otherwise URI is empty and it bails out
+ FNAME=$(parse_opt_equal_sign "$1" "$2")
+ IKNOW_FNAME=true
+ do_read_from_file=true
+ ;;
+ --warnings|--warnings=*)
+ WARNINGS=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ case "$WARNINGS" in
+ batch|off|false) ;;
+ *) pr_magentaln "\nwarnings can be either \"batch\", \"off\" or \"false\""
+ help 1;;
+ esac
+ ;;
+ --show[-_]each)
+ SHOW_EACH_C=1 #FIXME: sense is vice versa
+ ;;
+ --debug|--debug=*)
+ DEBUG=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ case $DEBUG in
+ [0-6]) ;;
+ *) pr_magentaln "\nunrecognized debug value \"$1\", must be between 0..6" 1>&2
+ help 1 ;;
+ esac
+ ;;
+ --color|--color=*)
+ COLOR=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ case $COLOR in
+ [0-2]) ;;
+ *) COLOR=2
+ pr_magentaln "\nunrecognized color: \"$1\", must be between 0..2" 1>&2
+ help 1 ;;
+ esac
+ ;;
+ --openssl|--openssl=*)
+ OPENSSL=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ ;;
+ --proxy|--proxy=*)
+ PROXY=$(parse_opt_equal_sign "$1" "$2")
+ [[ $? -eq 0 ]] && shift
+ ;;
+ --ssl_native|--ssl-native)
+ SSL_NATIVE=true
+ ;;
+ (--) shift
+ break
+ ;;
+ (-*) pr_magentaln "\n$0: unrecognized option \"$1\"" 1>&2;
+ help 1
+ ;;
+ (*) break
+ ;;
+ esac
+ shift
+ done
+
+ # Show usage if no options were specified
+ [[ -z "$1" ]] && help 0
+
+ # left off here is the URI
+ URI="$1"
+
+ [[ "$DEBUG" -ge 4 ]] && debug_globals
+ # if we have no "do_*" set here --> query_globals: we do a standard run -- otherwise just the one specified
+ query_globals && set_scanning_defaults
+}
+
+
+lets_roll() {
+ local ret
+
+ [[ -z "$NODEIP" ]] && fatal "$NODE doesn't resolve to an IP address" -1
+ determine_rdns
+ determine_service "$1" # any starttls service goes here
+
+ $do_tls_sockets && { [[ $TLS_LOW_BYTE -eq 0 ]] && \
+ sslv2_sockets || \
+ tls_sockets "$TLS_LOW_BYTE" "$HEX_CIPHER"; echo "$?" ; exit 0; }
+ $do_test_just_one && test_just_one ${single_cipher}
+
+ # all top level functions now following have the prefix "run_"
+ $do_protocols && { run_protocols; ret=$(($? + ret)); }
+ $do_spdy && { run_spdy; ret=$(($? + ret)); }
+ $do_std_cipherlists && { run_std_cipherlists; ret=$(($? + ret)); }
+ $do_pfs && { run_pfs; ret=$(($? + ret)); }
+ $do_server_preference && { run_server_preference; ret=$(($? + ret)); }
+ $do_server_defaults && { run_server_defaults; ret=$(($? + ret)); }
+
+ if $do_header; then
+ #TODO: refactor this into functions
+ if [[ $SERVICE == "HTTP" ]]; then
+ run_http_header "$URL_PATH"
+ run_http_date "$URL_PATH"
+ run_hsts "$URL_PATH"
+ run_hpkp "$URL_PATH"
+ run_server_banner "$URL_PATH"
+ run_application_banner "$URL_PATH"
+ run_cookie_flags "$URL_PATH"
+ run_more_flags "$URL_PATH"
+ run_rp_banner "$URL_PATH"
+ fi
+ fi
+
+ # vulnerabilities
+ if [[ $VULN_COUNT -gt $VULN_THRESHLD ]] || $do_vulnerabilities; then
+ outln; pr_blue "--> Testing vulnerabilities"
+ outln "\n"
+ fi
+ $do_heartbleed && { run_heartbleed; ret=$(($? + ret)); }
+ $do_ccs_injection && { run_ccs_injection; ret=$(($? + ret)); }
+ $do_renego && { run_renego; ret=$(($? + ret)); }
+ $do_crime && { run_crime; ret=$(($? + ret)); }
+ $do_breach && { run_breach "$URL_PATH" ; ret=$(($? + ret)); }
+ $do_ssl_poodle && { run_ssl_poodle; ret=$(($? + ret)); }
+ $do_tls_fallback_scsv && { run_tls_fallback_scsv; ret=$(($? + ret)); }
+ $do_freak && { run_freak; ret=$(($? + ret)); }
+ $do_logjam && { run_logjam; ret=$(($? + ret)); }
+ $do_beast && { run_beast; ret=$(($? + ret)); }
+ $do_rc4 && { run_rc4; ret=$(($? + ret)); }
+
+ $do_allciphers && { run_allciphers; ret=$(($? + ret)); }
+ $do_cipher_per_proto && { run_cipher_per_proto; ret=$(($? + ret)); }
+
+ outln
+ datebanner "Done"
+
+ return $ret
+}
+
+
+
+################# main #################
+
+get_install_dir
+
+initialize_globals
+parse_cmd_line "$@"
+set_color_functions
+find_openssl_binary
+maketempf
+mybanner
+check_proxy
+openssl_age
+
+# TODO: it's ugly to have those two vars here --> main()
+ret=0
+ip=""
+
+if $do_read_from_file; then
+ if [[ ! -r "$FNAME" ]] && $IKNOW_FNAME; then
+ fatal "Can't read file \"$FNAME\"" "-1"
+ fi
+ pr_reverse "====== Running in file batch mode with file=\"$FNAME\" ======"; outln "\n"
+ while read cmdline; do
+ cmdline=$(filter_input "$cmdline")
+ [[ -z "$cmdline" ]] && continue
+ [[ "$cmdline" == "EOF" ]] && break
+ echo "$0 -q $cmdline"
+ draw_dotted_line "=" $((TERM_DWITH / 2)); outln;
+ $0 -q $cmdline
+ done < "$FNAME"
+ exit $?
+fi
+
+#TODO: there shouldn't be the need for a special case for --mx, only the ip adresses we would need upfront and the do-parser
+if $do_mx_all_ips; then
+ query_globals # if we have just 1x "do_*" --> we do a standard run -- otherwise just the one specified
+ [[ $? -eq 1 ]] && set_scanning_defaults
+ mx_all_ips "${URI}" $PORT
+ ret=$?
+else
+ parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now
+ if ! determine_ip_addresses && [[ -z "$CMDLINE_IP" ]]; then
+ fatal "No IP address could be determined"
+ fi
+ if [[ -n "$CMDLINE_IP" ]]; then
+ [[ "$CMDLINE_IP" == "one" ]] && \
+ CMDLINE_IP=$(echo -n "$IPADDRs" | awk '{ print $1 }')
+ NODEIP="$CMDLINE_IP" # specific ip address for NODE was supplied
+ lets_roll "${STARTTLS_PROTOCOL}"
+ ret=$?
+ else # no --ip was supplied
+ if [[ $(count_words "$(echo -n "$IPADDRs")") -gt 1 ]]; then # we have more than one ipv4 address to check
+ pr_bold "Testing all IPv4 addresses (port $PORT): "; outln "$IPADDRs"
+ for ip in $IPADDRs; do
+ draw_dotted_line "-" $((TERM_DWITH / 2))
+ outln
+ NODEIP="$ip"
+ lets_roll "${STARTTLS_PROTOCOL}"
+ ret=$(($? + ret))
+ done
+ draw_dotted_line "-" $((TERM_DWITH / 2))
+ outln
+ pr_bold "Done testing now all IP addresses (on port $PORT): "; outln "$IPADDRs"
+ else # we need just one ip4v to check
+ NODEIP="$IPADDRs"
+ lets_roll "${STARTTLS_PROTOCOL}"
+ ret=$?
+ fi
+ fi
+fi
+
+exit $?
+
+
+# $Id: testssl.sh,v 1.379c 2015/09/29 16:47:47 dirkw Exp $
diff --git a/tone b/tone
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# record an audio file
+# credits to FRIGN
+
+ffmpeg -f alsa -ac 1 -i plughw:1,0 -sameq -strict -2 -acodec libmp3lame -qscale 8
diff --git a/translate b/translate
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# parazyd - (c) wtfpl 2016
+# use the google translate service
+
+if test -z "$1"; then
+ echo "usage: $(basename $0) text"
+ echo "examples:"
+ echo " $(basename $0) text"
+ echo " TL=el $(basename $0) text"
+ echo " SL=en TL=pl $(basename $0) text"
+ exit 1
+fi
+
+TEXT=$1
+SL=${SL:-en}
+TL=${TL:-hr}
+
+TRANSLATEURL='https://translate.google.com/'
+UA='Mozilla 5.0'
+NEWLINE='\
+'
+
+# do translate
+curl --user-agent "$UA" \
+ --data "sl=$SL" \
+ --data "tl=$TL" \
+ --data-urlencode "text=$TEXT" \
+ --silent $TRANSLATEURL \
+ | sed "s/<\/span>/$NEWLINE/g" | grep 'result_box' | sed 's/.*>//'
diff --git a/utf8.sh b/utf8.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+usage() {
+
+ echo "`basename $0` [min max] (min, max: 0x0000..0xffff)"
+
+}
+
+fast_chr() {
+ local __octal
+ local __char
+ printf -v __octal '%03o' $1
+ printf -v __char \\$__octal
+ REPLY=$__char
+}
+
+function unichr {
+ local c=$1 # ordinal of char
+ local l=0 # byte ctr
+ local o=63 # ceiling
+ local p=128 # accum. bits
+ local s='' # output string
+
+ (( c < 0x80 )) && { fast_chr "$c"; echo -n "$REPLY"; return; }
+
+ while (( c > o )); do
+ fast_chr $(( t = 0x80 | c & 0x3f ))
+ s="$REPLY$s"
+ (( c >>= 6, l++, p += o+1, o>>=1 ))
+ done
+
+ fast_chr $(( t = p | c ))
+ echo -n "$REPLY$s "
+}
+
+min=0xe000
+max=0xe1a0
+
+if test $# -eq 2; then
+ min=$1
+ max=$2
+elif test "$1" = "-h"; then
+ usage
+ exit 1
+fi
+
+## test harness
+for (( i=$min; i<$max; i++ )); do
+ unichr $i
+done
+echo
diff --git a/volume b/volume
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# z3bra - (c) wtfpl 2014
+
+CHANNEL=$(amixer | sed "1s/^.*'\(.*\)'.*$/\1/p;d")
+
+usage () {
+ cat <<EOF
+usage: $(basename $0) [-hsla] [-+!]
+ -h : print help
+ -s : print on/off
+ -l : print the current volume percentage (default)
+ -a : print both level and state
+ + : volume +5%
+ - : volume -5%
+ ! : toggle mute
+EOF
+}
+
+level() {
+ amixer get $CHANNEL | sed -n 's/^.*\[\([0-9]\+\)%.*$/\1/p' | uniq
+}
+
+state() {
+ amixer get $CHANNEL | sed -n 's/^.*\[\(o[nf]\+\)]$/\1/p' | uniq
+}
+
+# print out level and state if no argument is given
+test $# -eq 0 && echo "`level`" && exit 0
+
+case $1 in
+ -h) usage ;;
+ -s) state ;;
+ -l) level ;;
+ up|+) amixer set $CHANNEL 5%+ >/dev/null;;
+ down|-) amixer set $CHANNEL 5%- >/dev/null;;
+ toggle|!) amixer set $CHANNEL toggle >/dev/null;;
+ *) amixer set $CHANNEL $1 >/dev/null;;
+esac
diff --git a/weepop.sh b/weepop.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+duration=3
+
+(echo " irc | $@"; sleep ${duration}) | lemonbar -g '820x20+10+10' -B '#ff333333' -n 'popuplemonbar'
diff --git a/xkcd.py b/xkcd.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python2
+#
+# parazyd - (c) wtfpl
+# random xkcd comic
+
+import urllib, json, random
+latest = 1700
+num=str(random.randint(1,latest))
+info = json.load(urllib.urlopen("http://www.xkcd.com/" + num + "/info.0.json"))
+print info['img']
diff --git a/xurls b/xurls
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+
+use warnings;
+
+$hostchars = '[a-z0-9-._+]';
+$pathchars = '[a-z0-9-._+#=?&:;%/!,~]';
+
+sub scan($$$)
+{
+ my ($file, $lineno, $line) = @_;
+
+ chomp $line;
+
+ while($line =~ s!
+ ([a-z]+://)?
+
+# http://
+
+ $hostchars+\.[a-z]+/
+
+# www.tim.google.com/ - the [a-z].com is the main anchor for the whole regex - incase http:// is omitted
+
+ ($pathchars+/\?)*
+
+# check for the index.php? part
+
+ ($pathchars+|\($pathchars+\))*
+
+# check for pathchars, or a set of nested parens
+ !!xoi){ # allow space + comments, compile once, strcasecmp
+
+ my($p,$m,$e) = ($`,$&,$');
+
+ $e = '.' . $e if $m =~ s/\.$//;
+
+ if($opt{fname} && $file){
+ print "$col{red}$file$col{none}:";
+ }
+
+ if($opt{lineno}){
+ print "$col{green}$lineno$col{none}: ";
+ }elsif($opt{fname} && $file){
+ print ' ';
+ }
+
+ if($opt{hl}){
+ print "$p$col{brown}$m$col{none}$e\n";
+ }else{
+ print "$m\n";
+ }
+ }
+}
+
+sub usage(){
+ $printme =<<"!";
+Usage: $0 -[Chn] [FILES...]
+ -h: highlight
+ -c: force colour on (for pipes)
+ -C: colour off (only makes sense with -h)
+ -n: show line number
+!
+ print STDERR $printme;
+ exit 1;
+}
+
+
+%opt = (
+ colour => 1,
+ lineno => 0,
+ fname => 0,
+ hl => 0
+);
+%col = (
+ brown => "\e[0;31m", # hl
+ red => "\e[0;35m", # fname
+ green => "\e[0;32m", # lineno
+ none => "\e[0;0m"
+);
+
+for $arg (@ARGV){
+ if($arg eq '-h'){
+ $opt{hl} = 1;
+ }elsif($arg eq '-n'){
+ $opt{lineno} = 1;
+ }elsif($arg eq '-C'){
+ $opt{colour} = 0;
+ }elsif($arg eq '-c'){
+ usage() if $opt{colour} == 0;
+ $opt{colour} = 2; # force on
+ }elsif($arg eq '--help'){
+ usage();
+ }else{
+ push @files, $arg;
+ }
+}
+
+usage() if $opt{hl} && !$opt{colour};
+
+$opt{fname} = 1 if $#files > 0 || $opt{lineno};
+if(!$opt{colour} || ($opt{colour} == 1 && !-t STDOUT)){
+ $col{$_} = '' for keys %col;
+}
+
+$| = 1;
+
+if(@files){
+ for my $f (@files){
+ my $n = 1;
+ open F, '<', $f or warn "$f: $!\n";
+ scan($f, $n++, $_) for <F>;
+ close F;
+ }
+}else{
+ scan(undef, $., $_) while <STDIN>;
+}
diff --git a/ydl b/ydl
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# download the audio track of the first result of a youtube search
+# and add it to MPD library (will end up with a .mp3, for tags)
+
+MPD_DOWNLOAD_DIR=~/usr/msc/youtube/
+
+usage() {
+ echo "`basename $0` [-h] <query>"
+}
+
+ys() {
+ num=3
+ regex='^.*<a href="\(/watch[^"]*\)"[^>]*>\([^<]*\)</a>.*$'
+ output='\2 - http://youtube.com\1'
+
+ while getopts "hn:tu" OPT; do
+ case $OPT in
+ n) num=$OPTARG;;
+ t) output='\2';;
+ u) output='http://youtube.com\1';;
+ h) usage long; exit 0;;
+ *) usage; exit 1;;
+ esac
+ done
+
+ shift $((OPTIND - 1))
+
+ query=$(echo $@ | tr ' ' '+')
+ url="https://www.youtube.com/results?search_query=${query}"
+
+ curl -s "$url" | sed -n "s,$regex,$output,p" | sed ${num}q
+}
+
+# don't process if no argument given
+test $# -eq 0 && usage && exit 1
+
+# you can either pass MULTIPLE search terms or a SINGLE url
+test $# -gt 1 && uri=$(ys -n1 -u $@) || uri=$1
+
+# give up if we got no uri
+if test -z "$uri"; then
+ echo "no result found"
+ exit 1
+fi
+
+# change to target dir if it exists
+test -d $MPD_DOWNLOAD_DIR && cd $MPD_DOWNLOAD_DIR
+
+# download and extract audio track
+youtube-dl -q -x -o '%(title)s.%(ext)s' "$uri"
+
+# update mpd lib if running
+pgrep mpd >/dev/null 2>&1&& mpc -q update
+
+exit 0
diff --git a/ys b/ys
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# perform a search on youtube and return the best result (title + link)
+
+usage() {
+ echo "`basename $0` [-htu] [-n <num>] <query>"
+
+ test -z "$1" && return
+
+ cat <<EOF
+ -h : display this help
+ -t : output titles only (default 'title - uri')
+ -u : output uris only
+ -n : print only <num> results (default: 3)
+EOF
+}
+
+num=3
+regex='^.*<a href="\(/watch[^"]*\)"[^>]*>\([^<]*\)</a>.*$'
+output='\2 - http://youtube.com\1'
+
+while getopts "hn:tu" OPT; do
+ case $OPT in
+ n) num=$OPTARG;;
+ t) output='\2';;
+ u) output='http://youtube.com\1';;
+ h) usage long; exit 0;;
+ *) usage; exit 1;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+query=$(echo $@ | tr ' ' '+')
+url="https://www.youtube.com/results?search_query=${query}"
+
+curl -s "$url" | sed -n "s,$regex,$output,p" | sed ${num}q