#!/bin/sh

sp_version="2.5"

download_url="http://web.mit.edu/touchstone/config/shibboleth2-sp/${sp_version}/"
template_url="${download_url}shibboleth2.xml.in"
attrmap_url="${download_url}attribute-map.xml"
support="touchstone-support@mit.edu"
files="shibboleth2.xml"

# Determine whether to use echo -n or \c to echo without a trailing newline.
case "`echo -n`" in
-n)
  n=''
  c='\c'
  ;;
*)
  n='-n'
  c=''
  ;;
esac

echononewline()
{
  echo $n "$@$c"
}

canonicalize_hostname()
{
  case "$1" in
  *.*)
    name="$1"
    ;;    
  *)
    name="$1.mit.edu"
    ;;
  esac
  echo "$name" | tr '[:upper:]' '[:lower:]'
}

# Generate the self-signed certificate/key pair of files using the
# Shibboleth keygen script.  Any existing files are renamed.
generate_cert()
{
  # Determine where the keygen script lives.  On Red Hat, it is in
  # the Shibboleth configuration directory (/etc/shibboleth).  On
  # Debian, it is in /usr/sbin/shib-keygen.
  if [ -x ./keygen.sh ]; then
    keygen="./keygen.sh"
  elif [ -x /usr/sbin/shib-keygen ]; then
    keygen="/usr/sbin/shib-keygen"
  else
    echo "Error: Shibboleth keygen script is not available." 1>&2
    echo "Please contact $support" 1>&2
    exit 1
  fi
  # Save any existing cert/key files, and regenerate.
  if [ -f sp-cert.pem ]; then
    echo "Saving sp-cert.pem to sp-cert.pem.saved-by-gen-shib2..."
    mv sp-cert.pem sp-cert.pem.saved-by-gen-shib2
  fi
  if [ -f sp-key.pem ]; then
    echo "Saving sp-key.pem to sp-key.pem.saved-by-gen-shib2..."
    mv sp-key.pem sp-key.pem.saved-by-gen-shib2
  fi
  echo "Generating certificate/key pair..."
  $keygen -b -h "$hostname" || exit 1
  if id -u shibd > /dev/null 2>&1 ; then
    chown shibd:shibd sp-key.pem sp-cert.pem
  fi
}

# Prompt for the path of an existing (certificate/key) file, and
# output it.
get_path()
{
  prompt="$1"
  default_path="$2"
  path=
  while [ -z "$path" ]; do
    echo "" 1>&2
    echononewline "Enter the path for the $prompt: " 1>&2
    if [ -f "$default_path" ]; then
      echononewline "[$default_path] " 1>&2
    fi
    read path
    case "$path" in
    "")
      if [ -f "$default_path" ]; then
        path="$default_path"
      else
        echo "Please enter a valid path." 1>&2
        path=
        continue
      fi
      ;;
    esac
    if [ ! -f "$path" ]; then
      echo "$path is not a valid file" 1>&2
      path=
    fi
  done
  echo "$path"
}

# Ask a question with a yes/no answer, and output "true" or "false"
# accordingly.
get_yesno()
{
  prompt="$1"
  default_answer="$2"
  echo "" 1>&2
  echononewline "$prompt? [$default_answer] " 1>&2
  read answer
  if [ -z "$answer" ]; then
    answer="$default_answer"
  fi
  answer=`echo $answer | tr '[:upper:]' '[:lower:]'`
  case $answer in
  true|y|yes)
    answer=true
    ;;
  *)
    answer=false
    ;;
  esac
  echo "$answer"
}

default_hostname=`hostname`
default_hostname=`canonicalize_hostname "$default_hostname"`

# Make sure we are in the right place.
pkgsysconfdir=`pwd`
case "$pkgsysconfdir" in
/etc/shibboleth)
  prefix=/usr
  ;;
*/etc/shibboleth)
  prefix=`echo "$pkgsysconfdir" | sed -e 's:\(.*\)/etc/shibboleth$:\1:'`
  ;;
*)
  echo "Cannot determine Shibboleth install prefix." 1>&2
  echo "Please cd to the Shibboleth configuration directory to run this" 1>&2
  echo "(e.g. /etc/shibboleth or \$prefix/etc/shibboleth)." 1>&2
  exit 1
  ;;
esac

# Determine if wget or curl is available to download files.
if wget --version > /dev/null 2>&1 ; then
  download="wget -q -N"
elif curl --version > /dev/null 2>&1 ; then
  download="curl -s -O"
else
  download=
fi

# Download the template as needed or desired.
if [ -n "$download" ]; then
  if [ -e shibboleth2.xml.in ]; then
    get_template=`get_yesno "Download latest shibboleth2.xml.in" "Y"`
  else
    echo "Downloading $template_url..."
    get_template=true
  fi
  if [ "$get_template" = true ]; then
    $download "$template_url" || {
      echo "Error -- Failed to download $template_url" 1>&2
      echo "You must download this file in order to continue." 1>&2
      exit 1
    }
  fi
fi
if [ ! -e shibboleth2.xml.in ]; then
  echo "You must download $template_url in order to continue." 1>&2
  exit 1
fi

# Maybe download the latest attribute-map.xml.
got_attrmap=false
if [ -n "$download" ]; then
  get_attrmap=`get_yesno "Download latest attribute-map.xml" "Y"`
  if [ "$get_attrmap" = true ]; then
    echo "Saving previous version as attribute-map.xml.old" 1>&2
    cp -p attribute-map.xml attribute-map.xml.old
    $download "$attrmap_url"
    if [ $? -ne 0 ]; then
      echo "Warning -- Failed to download $attrmap_url" 1>&2
    else
      got_attrmap=true
    fi
  fi
fi
if [ "$got_attrmap" = false ]; then
  echo "Please make sure you have a current version of attribute-map.xml" 1>&2
  echo "You can download it from $attrmap_url" 1>&2
fi

while [ -z "$hostname" ]; do
  echo ""
  echononewline "Enter the web server host name: [$default_hostname] "
  read hostname
  if [ -z "$hostname" ]; then
    hostname="$default_hostname"
  fi
  hostname=`canonicalize_hostname "$hostname"`
done

case `uname` in
SunOS)
  files="$files shibd shibd-wrapper"
  if [ -d "/usr/athena/lib" ]; then
    default_ssldir=/usr/athena/lib
  elif [ -d "/usr/local/ssl/lib" ]; then
    default_ssldir=/usr/local/ssl/lib
  fi
  while [ -z "$ssldir" ]; do
    echo ""
    echononewline "Enter the OpenSSL library directory: [$default_ssldir] "
    read ssldir
    if [ -z "$ssldir" ]; then
      ssldir="$default_ssldir"
    fi
    if [ ! -d "$ssldir" ]; then
      echo "$ssldir is not a valid directory" 1>&2
      ssldir=
    fi
  done
  ;;
Linux)
  if [ -d "/usr/athena/lib" ]; then
    ssldir=/usr/athena/lib
  else
    ssldir=/lib
  fi
  ;;
esac

if openssl version > /dev/null 2>&1 ; then
  have_openssl=true
else
  have_openssl=false
fi

# Prompt for the certificate and key files.  We prefer to use the
# self-signed pair usually generated at install time.

if [ ! -f sp-cert.pem ]; then
  gen_cert=`get_yesno "Generate certificate/key pair" "Y"`
  if [ "$gen_cert" = true ]; then
    generate_cert
  fi
fi

certpath=
while [ -z "$certpath" ]; do
  keypath=
  certpath=`get_path "Shibboleth certificate file" "sp-cert.pem"`
  if [ "$have_openssl" = true ]; then
    subject_hash=`openssl x509 -in "$certpath" -noout -subject_hash 2>/dev/null`
    if [ $? -ne 0 ]; then
      echo "$certpath does not appear to be a valid certificate." 1>&2
      certpath=
      continue
    fi
    issuer_hash=`openssl x509 -in "$certpath" -noout -issuer_hash 2>/dev/null`
    if [ "$issuer_hash" = "$subject_hash" ]; then
      # We have a self-signed certificate.
      self_signed=true
    else
      echo ""
      echo "Warning: Shibboleth should use a self-signed certificate."
      cont=`get_yesno "Continue anyway" "Y"`
      if [ "$cont" = false ]; then
        certpath=
        continue
      fi
      self_signed=false
    fi
    # Check that the certificate CN matches our host name.
    cn=`openssl x509 -in "$certpath" -noout -subject -nameopt sep_multiline \
          | awk '/CN=/ { print substr($1,4); }'`
    case "$cn" in
    *$hostname*)
      ;;
    *)
      echo "The certificate's subject CN must match your web server host name." 1>&2
      echo "(The subject CN is $cn, given host name is $hostname)." 1>&2
      regen=`get_yesno "Generate a new (self-signed) certificate/key pair" "Y"`
      if [ "$regen" = false ]; then
        cont=`get_yesno "Continue anyway" "N"`
        if [ "$cont" = false ]; then
          echo "Please contact $support for further assistance." 1>&2
          exit 1
        fi
      else
        generate_cert
        certpath="sp-cert.pem"
      fi
      ;;
    esac
  fi

  if [ "$self_signed" = true ]; then
    echo "Please include the contents of $certpath when you register the server."
  fi

  # Get the key file.
  if [ "$certpath" = sp-cert.pem ]; then
    default_keypath="sp-key.pem"
  else
    default_keypath=""
  fi
  while [ -z "$keypath" ]; do
    keypath=`get_path "Shibboleth private key file" "$default_keypath"`
    if [ "$have_openssl" = true ]; then
      # Check that it is a valid key file.
      if openssl rsa -noout -in "$keypath" > /dev/null 2>&1 ; then
        :
      else
        echo "$keypath does not appear to be a valid key file." 1>&2
        keypath=
        continue
      fi
      # Make sure the certificate and key files match.
      cert_mod=`openssl x509 -in "$certpath" -noout -modulus | openssl md5`
      key_mod=`openssl rsa -in "$keypath" -noout -modulus | openssl md5`
      if [ "$cert_mod" != "$key_mod" ]; then
        echo "$certpath and $keypath do not appear to be a matched pair." 1>&2
        keypath=
        certpath=
        continue 2
      fi
    fi
  done
done

handlerSSL=`get_yesno "Always use SSL for Shibboleth handler" "Y"`

cookie_props="http"
if [ "$handlerSSL" = true ]; then
  secure=`get_yesno "Set cookies secure (requires SSL for all protected content)" "Y"`
  if [ "$secure" = true ]; then
    cookie_props="https"
    echo ""
    echo "To avoid loops, be sure to redirect any non-https requests to SSL."
    echononewline "Enter <return> to continue: "
    read junk
  fi
fi

# Get the contact email address.
case "$hostname" in
*.*.*)
  default_contact=`echo "$hostname" | sed -e 's/\./-help@/'`
  ;;
*.*)
  default_contact="help@$hostname"
  ;;
*)
  default_contact="$hostname-help@mit.edu"
  ;;
esac
echo ""
echononewline "Support contact email address? [$default_contact] "
read contact
if [ -z "$contact" ]; then
  contact="$default_contact"
fi

incommon=`get_yesno "Will this server be joining the InCommon Federation" "N"`
if [ "$incommon" = true ]; then
  begin_incommon='<!-- Begin InCommon addition -->'
  end_incommon='<!-- End InCommon addition -->'
  if [ ! -e inc-md-cert.pem ]; then
    url="http://md.incommon.org/certs/inc-md-cert.pem"
    if [ -n "$download" ]; then
      echo "Downloading InCommon metadata signing certificate..."
      $download "$url" || {
        echo "Warning -- Failed to download $url" 1>&2
        echo "You must download this certificate in order to validate" 1>&2
        echo "the published InCommon metadata." 1>&2
      }
      if [ "$have_openssl" = true ]; then
        fingerprint=`openssl x509 -sha1 -noout -fingerprint -in inc-md-cert.pem | sed -e 's/^SHA1 Fingerprint=//'`
        if [ "$fingerprint" != "7D:B4:BB:28:D3:D5:C8:52:E0:80:B3:62:43:2A:AF:34:B2:A6:0E:DD" ]; then
          echo "Warning -- fingerprint mismatch on downloaded InCommon signing certificate." 1>&2
          echo "Please contact $support for assistance." 1>&2
          exit 1
        fi
      fi
    else
      echo "You must download the InCommon metadata signing certificate" 1>&2
      echo "from $url" 1>&2
      echo "in order to validate the published InCommon metadata." 1>&2
    fi
    echo ""
  fi
else
  begin_incommon='<!--'
  end_incommon='-->'
fi

echo "Using prefix $prefix..."
pkgxmldir=$prefix/share/xml/shibboleth
libexecdir=$prefix/libexec
# XXX
varrundir=/var/run

for file in $files ; do
  tmpfile=$file.$$
  sed -e "s:%%HOSTNAME%%:$hostname:" \
      -e "s:%%SSLDIR%%:$ssldir:" \
      -e "s:%%KEYPATH%%:$keypath:" \
      -e "s:%%CERTPATH%%:$certpath:" \
      -e "s:%%HANDLERSSL%%:$handlerSSL:" \
      -e "s:%%COOKIEPROPS%%:$cookie_props:" \
      -e "s:%%CONTACT_EMAIL%%:$contact:" \
      -e "s:%%BEGIN_INCOMMON%%:$begin_incommon:" \
      -e "s:%%END_INCOMMON%%:$end_incommon:" \
      -e "s:@-PKGXMLDIR-@:$pkgxmldir:" \
      -e "s:@-PKGSYSCONFDIR-@:$pkgsysconfdir:" \
      -e "s:@-LIBEXECDIR-@:$libexecdir:" \
      -e "s:@-VARRUNDIR-@:$varrundir:" \
      -e "s:@-PREFIX-@:$prefix:" \
    < $file.in > $tmpfile || exit 1
  if [ -f "$file" ]; then
    echo "$file already exists, saving previous version as $file.old" 1>&2
    mv $file $file.old
  fi
  case $file in
  shibd-wrapper)
    mv $tmpfile $prefix/sbin/$file
    chmod 755 $prefix/sbin/$file
    ;;
  *)
    mv $tmpfile $file
    chmod 644 $file
    ;;
  esac
done
