From f8dbf7aef4f1b1648bd2980b62f2ef57d653e800 Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Fri, 29 May 2020 08:49:19 -0400 Subject: renames --- Makefile | 4 +- control.sh | 10 - dyndns-command.sh | 92 -------- etc/powerdns/add-zone | 42 ++++ etc/powerdns/dyndns-command.sh | 92 ++++++++ etc/powerdns/fixperms.sh | 5 + etc/powerdns/named.conf | 10 + etc/powerdns/pdns.conf | 17 ++ etc/powerdns/pdns.d/bind.conf | 2 + etc/powerdns/pdns.d/pdns.local.conf | 3 + etc/powerdns/pdns.d/pdns.local.gsqlite3.conf | 8 + etc/powerdns/powerdns.sqlite3 | 0 etc/sources.list.d/pdns.list | 1 + powerdns/add-zone | 42 ---- powerdns/dyndns-command.sh | 92 -------- powerdns/fixperms.sh | 5 - powerdns/named.conf | 10 - powerdns/pdns.conf | 17 -- powerdns/pdns.d/bind.conf | 2 - powerdns/pdns.d/pdns.local.conf | 3 - powerdns/pdns.d/pdns.local.gsqlite3.conf | 8 - powerdns/powerdns.sqlite3 | 0 samizdat-ssh-command | 324 --------------------------- samizdat-ssh-uid | 39 ---- sources.list.d/pdns.list | 1 - src/control.sh | 10 + src/dyndns-command.sh | 92 ++++++++ src/samizdat-ssh-command | 324 +++++++++++++++++++++++++++ src/samizdat-ssh-uid | 39 ++++ 29 files changed, 647 insertions(+), 647 deletions(-) delete mode 100755 control.sh delete mode 100755 dyndns-command.sh create mode 100755 etc/powerdns/add-zone create mode 100755 etc/powerdns/dyndns-command.sh create mode 100755 etc/powerdns/fixperms.sh create mode 100644 etc/powerdns/named.conf create mode 100644 etc/powerdns/pdns.conf create mode 100644 etc/powerdns/pdns.d/bind.conf create mode 100644 etc/powerdns/pdns.d/pdns.local.conf create mode 100644 etc/powerdns/pdns.d/pdns.local.gsqlite3.conf create mode 100644 etc/powerdns/powerdns.sqlite3 create mode 100644 etc/sources.list.d/pdns.list delete mode 100755 powerdns/add-zone delete mode 100755 powerdns/dyndns-command.sh delete mode 100755 powerdns/fixperms.sh delete mode 100644 powerdns/named.conf delete mode 100644 powerdns/pdns.conf delete mode 100644 powerdns/pdns.d/bind.conf delete mode 100644 powerdns/pdns.d/pdns.local.conf delete mode 100644 powerdns/pdns.d/pdns.local.gsqlite3.conf delete mode 100644 powerdns/powerdns.sqlite3 delete mode 100755 samizdat-ssh-command delete mode 100755 samizdat-ssh-uid delete mode 100644 sources.list.d/pdns.list create mode 100755 src/control.sh create mode 100755 src/dyndns-command.sh create mode 100755 src/samizdat-ssh-command create mode 100755 src/samizdat-ssh-uid diff --git a/Makefile b/Makefile index e1af5a9..4653156 100644 --- a/Makefile +++ b/Makefile @@ -57,5 +57,5 @@ endef ~$(DD_USER)/.ssh/authorized_keys: : writing file $@ $(file >$@,$(dyndns_authorized_keys_contents)) -control: control.sh depends.control .git/refs/heads/master - ./control.sh depends.control > $@ +control: src/control.sh depends.control .git/refs/heads/master + ./src/control.sh depends.control > $@ diff --git a/control.sh b/control.sh deleted file mode 100755 index 7106187..0000000 --- a/control.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -VERSION=${VERSION:-$(TZ=UTC date -Is | tr -d :- | tr T . | sed 's/+.*//')} -cat - "$@" < -END diff --git a/dyndns-command.sh b/dyndns-command.sh deleted file mode 100755 index 375d50b..0000000 --- a/dyndns-command.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash - -die() { printf '%s\n' "$*" >&2; exit 1; } - -sql_string() -{ - printf '%s' "'${1//\'/\'\'}'" -} - -powerdns_sqlite_add_replace_record() -{ - local sql_new_domain="$(sql_string "$1.$3")" - local sql_ip_address="$(sql_string "$2")" - local sql_domain="$(sql_string "$3")" - local record_type="$(sql_string "$4")" - - DBDIR=/etc/powerdns - DBNAME=powerdns.sqlite3 - DB=$DBDIR/$DBNAME - - test -r $DB && test -w $DB || die "Wrong permissions on $DB" - test -r $DBDIR && test -w $DBDIR || die "Wrong permissions on $DBDIR" - - sqlite3 $DB < " >&2; exit ${1:-1}; } + +check_args() +{ + [ $# = 2 ] || usage 1 + database=$1 + domain=$2 + + [ -f "$database" ] || exit 1 + [ -r "$database" ] || exit 2 + + case "$domain" in + -h|--help) exec 2>&1; usage 0 ;; + *\'*) usage 1 ;; + '') usage 1 ;; + *.) usage 1 ;; + *.*) ;; + *) [ "$ALLOW_TLD_ZONE" ] || usage 1 ;; + esac +} + +add_zone() +{ + local database="$1" domain="$2" soa + soa="$domain hostmaster.$domain 1 10800 3600 604800 3600" + sqlite3 "$database" <&2; exit 1; } + +sql_string() +{ + printf '%s' "'${1//\'/\'\'}'" +} + +powerdns_sqlite_add_replace_record() +{ + local sql_new_domain="$(sql_string "$1.$3")" + local sql_ip_address="$(sql_string "$2")" + local sql_domain="$(sql_string "$3")" + local record_type="$(sql_string "$4")" + + DBDIR=/etc/powerdns + DBNAME=powerdns.sqlite3 + DB=$DBDIR/$DBNAME + + test -r $DB && test -w $DB || die "Wrong permissions on $DB" + test -r $DBDIR && test -w $DBDIR || die "Wrong permissions on $DBDIR" + + sqlite3 $DB < " >&2; exit ${1:-1}; } - -check_args() -{ - [ $# = 2 ] || usage 1 - database=$1 - domain=$2 - - [ -f "$database" ] || exit 1 - [ -r "$database" ] || exit 2 - - case "$domain" in - -h|--help) exec 2>&1; usage 0 ;; - *\'*) usage 1 ;; - '') usage 1 ;; - *.) usage 1 ;; - *.*) ;; - *) [ "$ALLOW_TLD_ZONE" ] || usage 1 ;; - esac -} - -add_zone() -{ - local database="$1" domain="$2" soa - soa="$domain hostmaster.$domain 1 10800 3600 604800 3600" - sqlite3 "$database" <&2; exit 1; } - -sql_string() -{ - printf '%s' "'${1//\'/\'\'}'" -} - -powerdns_sqlite_add_replace_record() -{ - local sql_new_domain="$(sql_string "$1.$3")" - local sql_ip_address="$(sql_string "$2")" - local sql_domain="$(sql_string "$3")" - local record_type="$(sql_string "$4")" - - DBDIR=/etc/powerdns - DBNAME=powerdns.sqlite3 - DB=$DBDIR/$DBNAME - - test -r $DB && test -w $DB || die "Wrong permissions on $DB" - test -r $DBDIR && test -w $DBDIR || die "Wrong permissions on $DBDIR" - - sqlite3 $DB <&2; exit 1; } - [ "$SSH_CLIENT_FINGERPRINT" ] || { echo 'Error: no $SSH_CLIENT_FINGERPRINT' >&2; exit 1; } - - tty=$(tty) && [ "$tty" != 'not a tty' ] || tty= - - if [ "$SSH_ORIGINAL_COMMAND" ]; then - msg='You are not authorized to execute the command: %s\n' - if ! [ "$tty" ]; then - msg="$msg"'To authorize your public key via password, reconnect without a command, or with a terminal attached.\n' - msg="$msg"'To attach a terminal, use the "-t" option to ssh.\n' - else - msg="$msg"'To authorize your public key and execute the command, enter your password.\n' - fi - printf "\n$msg\n" "$SSH_ORIGINAL_COMMAND" >&2 - if ! [ "$tty" ]; then - exit 1 - fi - else - msg='You are not authorized to log in.\n' - msg="$msg"'To authorize your public key and log in, enter your password.\n' - printf "\n$msg\n" >&2 - fi - authorize || exit $? - # TODO: blacklist after too many authentication failures - if [ "$SSH_ORIGINAL_COMMAND" ]; then - exec "$SSH_ORIGINAL_COMMAND" - else - exec $(getent passwd "$USER"|cut -d: -f7) -i - fi - exit $? # exec failed -} - -die() { echo "Error: $*" >&2; exit 1; } - -dequote() -{ - # Sorry about the slashes. The perl would be: s{ \\(.) | '([^']+)' }{ $1$2 }gx - git_dir=$(echo -n "$git_dir" | sed -e "s/\\\\\\(.\\)\\|'\\([^']\\+\\)'/\\1\\2/g") -} - -homedir_expand() -{ - git_dir=$(homedir_expand_arg "$git_dir") && - [ "$git_dir" ] || die "Could not expand home directory. HOME=$HOME, USER=$USER, id=$(id)" -} - -homedir_expand_arg() -{ - [ "$HOME" ] || die '$HOME is not set.' - case "$1" in - \~) echo "$HOME";; - \~/*) echo "${HOME}${1#\~}";; - \~*) - local u - u=${1#\~} - u=${u%%/*} - u=$(getent passwd "$u"|cut -f6 -d:) && [ "$u" ] || return 1 - echo "$u/${1#*/}";; - /*) echo "$1";; - *) echo "$HOME/$1";; - esac -} - -initialize_git() -{ - local git_dir="$1" anonymous="$2" - if ! [ -e "$git_dir" ]; then - mkdir -p "$(dirname "$git_dir")" - git init --bare "$git_dir" >&2 - if [ "$anonymous" ]; then - git --git-dir "$git_dir" config samizdat.allow-anonymous-access true - git --git-dir "$git_dir" config samizdat.anonymous-ssh-owner "$anonymous" - fi - fi -} - -is_gitdir() { git rev-parse --resolve-git-dir "$1" >/dev/null 2>&1; } - -deny() { echo 'Error: permission denied.' >&2; exit 1; } - -valid_new_public_repo() -{ - local git_dir="$1" - [ ! -e "$git_dir" ] || return - [ "$HOME" -a -d "$HOME"/public_git ] || return - local dirname="$(dirname "$git_dir")" - - case "$git_dir" in - *.git) ;; - *) - echo 'Error: public repos must be named *.git' >&2 - return 1 ;; - esac - - case "$dirname" in - $HOME/public_git) return 0 ;; - $HOME/public_git/*) - # Ensure that no parent directory is named *.git - # Also enforce a maximum depth of 4. - # Valid: public_git/a/b/c/d.git - # Invalid: public_git/a/b/c/d/e.git - local n relative="${git_dir#$HOME/public_git/}" - for n in 1 2 3 4; do - local topmost="${relative%%/*}" - case "$topmost" in - "$relative") return 0;; - *.git) return 1;; - esac - relative=${relative#$topmost/} - done - echo 'Error: directories nest too deep' >&2 - return 1 - ;; - *) return 1 ;; - esac -} - -check_if_ssh_user_owns_repository() -{ - git --git-dir "$git_dir" config --get-all samizdat.anonymous-ssh-owner | grep -xqF "$SSH_CLIENT_FINGERPRINT" -} -ssh_user_owns_repository() -{ - if [ -z "$SSH_USER_OWNS_REPOSITORY" ]; then - check_if_ssh_user_owns_repository - SSH_USER_OWNS_REPOSITORY=$? - fi - return $SSH_USER_OWNS_REPOSITORY -} - -is_public_repository() -{ - case "$git_dir" in - */../*) false;; - "$HOME"/public_git/*) true;; - *) false;; - esac -} - -authorized() -{ - # TODO: check SSH_CLIENT_FINGERPRINT against a blacklist - ssh_user_owns_repository && return - is_public_repository && return - test "$(git --git-dir "$1" config --bool --get samizdat.allow-anonymous-access)" = true 2>/dev/null && return 0 - # TODO: check SSH_CLIENT_FINGERPRINT against a whitelist -} - -maybe_initialize_heads() -{ - [ "$GIT_NAMESPACE" ] || die 'Programmer error' - heads=$git_dir/refs/namespaces/$GIT_NAMESPACE/refs/heads - mkdir -p "$heads" - found_file=$(find "$heads" -type f -print -quit) - [ "$found_file" ] && return - [ -e "$git_dir/refs/heads/master" ] && cp "$git_dir/refs/heads/master" "$heads" - # TODO: copy actual file 'HEAD' and whatever it references -} - - -if [ "$1" = "authorize-full-access" ]; then - case "$SSH_ORIGINAL_COMMAND" in - git-receive-pack\ *) - git_cmd=git-receive-pack - git_dir="${SSH_ORIGINAL_COMMAND#git-receive-pack }" - dequote - homedir_expand - initialize_git "$git_dir" - exec "$git_cmd" "$git_dir" - ;; - "") - shell=$(getent passwd $USER|cut -d: -f7) - argv0=-${shell##*/} - if which chpst >/dev/null 2>&1; then - exec chpst -b "$argv0" "$shell" - else - exec "$shell" - fi - ;; - *) - exec /bin/sh -c "$SSH_ORIGINAL_COMMAND" - ;; - esac - exit -fi - -eval "$(samizdat-ssh-uid)" || die eval - -if [ $# -gt 0 ] -then - exec "$@" - exit -fi - -# TODO: call password_authentication on all authorization failures - -#echo "SSH_ORIGINAL_COMMAND=$SSH_ORIGINAL_COMMAND" >&2 -case "$SSH_ORIGINAL_COMMAND" in - git-upload-pack\ *|git-receive-pack\ *) - # set three variables - # 1. git_cmd - # 2. git_dir - # 3. git_ns (optional) - - git_cmd=${SSH_ORIGINAL_COMMAND%%\ *} - git_dir=${SSH_ORIGINAL_COMMAND#*\ } - - dequote - homedir_expand - - case "$git_dir" in - $HOME/git_namespace/*/public_git/*) - git_ns_subdir=${git_dir#$HOME/git_namespace/} - git_ns=${git_ns_subdir%%/*} - git_dir=$HOME/${git_ns_subdir#*/} - ;; - esac - - ;; - rsync\ --server\ --sender\ -de.LsfxC\ .\ public_git/|rsync\ --server\ --sender\ -de.LsfxC\ .\ public_git/|rsync\ --server\ --sender\ -de.Lsf\ .\ public_git/) - #echo "$SSH_ORIGINAL_COMMAND" >&2 - [ -d "$HOME"/public_git ] || { password_authentication; exit 1; } - exec $SSH_ORIGINAL_COMMAND - #exec rrsync -ro "$HOME"/public_git - exit 1 - ;; - rsync\ --server\ --sender\ *) - #echo "$SSH_ORIGINAL_COMMAND" >&2 - [ -d "$HOME"/public_rsync ] || { password_authentication; exit 1; } - exec rrsync -ro "$HOME"/public_rsync - exit 1 - ;; - rsync\ --server\ *) - [ -d "$HOME"/incoming_rsync -a "${SSH_CLIENT_FINGERPRINT}" ] || { password_authentication; exit 1; } - destdir=$HOME/incoming_rsync/$SSH_CLIENT_FINGERPRINT/ - mkdir -p "$destdir" && exec rrsync "$destdir" - exit 1 - ;; - *) - #password_authentication - $default_command - exit - ;; -esac - -if [ "$git_cmd" = 'git-upload-pack' ]; then - case "$git_dir" in - $HOME/public_git/*|public_git/*) - is_gitdir "$git_dir" || git_dir="$git_dir/.git" - if ! is_gitdir "$git_dir"; then - # git rev-parse --resolve-git-dir "${git_dir%/.git}" # show git's error message - deny - fi - if [ "$git_ns" -a -e "$git_dir/refs/namespaces/$git_ns" ]; then - export GIT_NAMESPACE="$git_ns" - # maybe_initialize_heads - fi - exec "$git_cmd" "$git_dir" - ;; - esac - -elif [ "$git_cmd" = 'git-receive-pack' ]; then - - if [ ! -d "$git_dir" ]; then - if valid_new_public_repo "$git_dir"; then - initialize_git "$git_dir" "$SSH_CLIENT_FINGERPRINT" - else - deny - fi - fi - -fi - -if authorized "$git_dir"; then - if [ "$git_cmd" = 'git-receive-pack' ]; then - if ! ssh_user_owns_repository - then - export GIT_NAMESPACE="$SSH_CLIENT_FINGERPRINT" - maybe_initialize_heads - printf '%s:%s\n' 'd@cryptonomic.net' "git_namespace/$GIT_NAMESPACE/${git_dir#${HOME}/}" >&2 - fi - fi - exec "$git_cmd" "$git_dir" -else - $default_command - exit - # echo 'Error: git access is unauthorized' >&2; exit 1 # unreached -fi diff --git a/samizdat-ssh-uid b/samizdat-ssh-uid deleted file mode 100755 index 2b4b311..0000000 --- a/samizdat-ssh-uid +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/dash - -die() { echo "$0: Error: $*" >&2; exit 1; } - -[ "$SSH_USER_AUTH" ] || die "not defined: \$SSH_USER_AUTH" -[ -f "$SSH_USER_AUTH" ] || die "file does not exist: \$SSH_USER_AUTH=${SSH_USER_AUTH}" - -PEMFILE="${SSH_USER_AUTH}.tmp" - -sed -ne 's/^publickey //p' < "${SSH_USER_AUTH}" > "${PEMFILE}" || die "could not rewrite SSH_USER_AUTH file" - -SSH_CLIENT_FINGERPRINT=$(ssh-keygen -r . -f "${PEMFILE}" | sed -ne 's/^. IN SSHFP [0-9]* 2 //p') && - [ "$SSH_CLIENT_FINGERPRINT" ] || die "could not determine ssh client fingerprint" - -read keytype keydata < "${PEMFILE}" || die "reading from PEMFILE=$PEMFILE" -case "$keytype" in - ssh-rsa|ssh-dss|ecdsa-sha2-nistp256|ssh-ed25519) - domain=$keytype.cryptonomic.net ;; - *) - die "Unsupported key type: $keytype" ;; -esac - -if [ "$1" = '--copy-pem' -a "$2" ] -then - if [ -d "$2" ] || mkdir "$2" - then - mv "${PEMFILE}" "$2"/${SSH_CLIENT_FINGERPRINT}.${keytype}.pem - fi -else - rm -f "${PEMFILE}" -fi - -env -i \ - SSH_CLIENT_FINGERPRINT="$SSH_CLIENT_FINGERPRINT" \ - SSH_CLIENT_KEYTYPE="$keytype" \ - SSH_CLIENT_DOMAIN="$domain" \ - SSH_CLIENT_PEMFILE="$PEMFILE" \ - SSH_CLIENT_KEYDATA="$keydata" - diff --git a/sources.list.d/pdns.list b/sources.list.d/pdns.list deleted file mode 100644 index 795d281..0000000 --- a/sources.list.d/pdns.list +++ /dev/null @@ -1 +0,0 @@ -deb http://repo.powerdns.com/debian stretch-auth-42 main diff --git a/src/control.sh b/src/control.sh new file mode 100755 index 0000000..7106187 --- /dev/null +++ b/src/control.sh @@ -0,0 +1,10 @@ +#!/bin/sh +VERSION=${VERSION:-$(TZ=UTC date -Is | tr -d :- | tr T . | sed 's/+.*//')} +cat - "$@" < +END diff --git a/src/dyndns-command.sh b/src/dyndns-command.sh new file mode 100755 index 0000000..375d50b --- /dev/null +++ b/src/dyndns-command.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +die() { printf '%s\n' "$*" >&2; exit 1; } + +sql_string() +{ + printf '%s' "'${1//\'/\'\'}'" +} + +powerdns_sqlite_add_replace_record() +{ + local sql_new_domain="$(sql_string "$1.$3")" + local sql_ip_address="$(sql_string "$2")" + local sql_domain="$(sql_string "$3")" + local record_type="$(sql_string "$4")" + + DBDIR=/etc/powerdns + DBNAME=powerdns.sqlite3 + DB=$DBDIR/$DBNAME + + test -r $DB && test -w $DB || die "Wrong permissions on $DB" + test -r $DBDIR && test -w $DBDIR || die "Wrong permissions on $DBDIR" + + sqlite3 $DB <&2; exit 1; } + [ "$SSH_CLIENT_FINGERPRINT" ] || { echo 'Error: no $SSH_CLIENT_FINGERPRINT' >&2; exit 1; } + + tty=$(tty) && [ "$tty" != 'not a tty' ] || tty= + + if [ "$SSH_ORIGINAL_COMMAND" ]; then + msg='You are not authorized to execute the command: %s\n' + if ! [ "$tty" ]; then + msg="$msg"'To authorize your public key via password, reconnect without a command, or with a terminal attached.\n' + msg="$msg"'To attach a terminal, use the "-t" option to ssh.\n' + else + msg="$msg"'To authorize your public key and execute the command, enter your password.\n' + fi + printf "\n$msg\n" "$SSH_ORIGINAL_COMMAND" >&2 + if ! [ "$tty" ]; then + exit 1 + fi + else + msg='You are not authorized to log in.\n' + msg="$msg"'To authorize your public key and log in, enter your password.\n' + printf "\n$msg\n" >&2 + fi + authorize || exit $? + # TODO: blacklist after too many authentication failures + if [ "$SSH_ORIGINAL_COMMAND" ]; then + exec "$SSH_ORIGINAL_COMMAND" + else + exec $(getent passwd "$USER"|cut -d: -f7) -i + fi + exit $? # exec failed +} + +die() { echo "Error: $*" >&2; exit 1; } + +dequote() +{ + # Sorry about the slashes. The perl would be: s{ \\(.) | '([^']+)' }{ $1$2 }gx + git_dir=$(echo -n "$git_dir" | sed -e "s/\\\\\\(.\\)\\|'\\([^']\\+\\)'/\\1\\2/g") +} + +homedir_expand() +{ + git_dir=$(homedir_expand_arg "$git_dir") && + [ "$git_dir" ] || die "Could not expand home directory. HOME=$HOME, USER=$USER, id=$(id)" +} + +homedir_expand_arg() +{ + [ "$HOME" ] || die '$HOME is not set.' + case "$1" in + \~) echo "$HOME";; + \~/*) echo "${HOME}${1#\~}";; + \~*) + local u + u=${1#\~} + u=${u%%/*} + u=$(getent passwd "$u"|cut -f6 -d:) && [ "$u" ] || return 1 + echo "$u/${1#*/}";; + /*) echo "$1";; + *) echo "$HOME/$1";; + esac +} + +initialize_git() +{ + local git_dir="$1" anonymous="$2" + if ! [ -e "$git_dir" ]; then + mkdir -p "$(dirname "$git_dir")" + git init --bare "$git_dir" >&2 + if [ "$anonymous" ]; then + git --git-dir "$git_dir" config samizdat.allow-anonymous-access true + git --git-dir "$git_dir" config samizdat.anonymous-ssh-owner "$anonymous" + fi + fi +} + +is_gitdir() { git rev-parse --resolve-git-dir "$1" >/dev/null 2>&1; } + +deny() { echo 'Error: permission denied.' >&2; exit 1; } + +valid_new_public_repo() +{ + local git_dir="$1" + [ ! -e "$git_dir" ] || return + [ "$HOME" -a -d "$HOME"/public_git ] || return + local dirname="$(dirname "$git_dir")" + + case "$git_dir" in + *.git) ;; + *) + echo 'Error: public repos must be named *.git' >&2 + return 1 ;; + esac + + case "$dirname" in + $HOME/public_git) return 0 ;; + $HOME/public_git/*) + # Ensure that no parent directory is named *.git + # Also enforce a maximum depth of 4. + # Valid: public_git/a/b/c/d.git + # Invalid: public_git/a/b/c/d/e.git + local n relative="${git_dir#$HOME/public_git/}" + for n in 1 2 3 4; do + local topmost="${relative%%/*}" + case "$topmost" in + "$relative") return 0;; + *.git) return 1;; + esac + relative=${relative#$topmost/} + done + echo 'Error: directories nest too deep' >&2 + return 1 + ;; + *) return 1 ;; + esac +} + +check_if_ssh_user_owns_repository() +{ + git --git-dir "$git_dir" config --get-all samizdat.anonymous-ssh-owner | grep -xqF "$SSH_CLIENT_FINGERPRINT" +} +ssh_user_owns_repository() +{ + if [ -z "$SSH_USER_OWNS_REPOSITORY" ]; then + check_if_ssh_user_owns_repository + SSH_USER_OWNS_REPOSITORY=$? + fi + return $SSH_USER_OWNS_REPOSITORY +} + +is_public_repository() +{ + case "$git_dir" in + */../*) false;; + "$HOME"/public_git/*) true;; + *) false;; + esac +} + +authorized() +{ + # TODO: check SSH_CLIENT_FINGERPRINT against a blacklist + ssh_user_owns_repository && return + is_public_repository && return + test "$(git --git-dir "$1" config --bool --get samizdat.allow-anonymous-access)" = true 2>/dev/null && return 0 + # TODO: check SSH_CLIENT_FINGERPRINT against a whitelist +} + +maybe_initialize_heads() +{ + [ "$GIT_NAMESPACE" ] || die 'Programmer error' + heads=$git_dir/refs/namespaces/$GIT_NAMESPACE/refs/heads + mkdir -p "$heads" + found_file=$(find "$heads" -type f -print -quit) + [ "$found_file" ] && return + [ -e "$git_dir/refs/heads/master" ] && cp "$git_dir/refs/heads/master" "$heads" + # TODO: copy actual file 'HEAD' and whatever it references +} + + +if [ "$1" = "authorize-full-access" ]; then + case "$SSH_ORIGINAL_COMMAND" in + git-receive-pack\ *) + git_cmd=git-receive-pack + git_dir="${SSH_ORIGINAL_COMMAND#git-receive-pack }" + dequote + homedir_expand + initialize_git "$git_dir" + exec "$git_cmd" "$git_dir" + ;; + "") + shell=$(getent passwd $USER|cut -d: -f7) + argv0=-${shell##*/} + if which chpst >/dev/null 2>&1; then + exec chpst -b "$argv0" "$shell" + else + exec "$shell" + fi + ;; + *) + exec /bin/sh -c "$SSH_ORIGINAL_COMMAND" + ;; + esac + exit +fi + +eval "$(samizdat-ssh-uid)" || die eval + +if [ $# -gt 0 ] +then + exec "$@" + exit +fi + +# TODO: call password_authentication on all authorization failures + +#echo "SSH_ORIGINAL_COMMAND=$SSH_ORIGINAL_COMMAND" >&2 +case "$SSH_ORIGINAL_COMMAND" in + git-upload-pack\ *|git-receive-pack\ *) + # set three variables + # 1. git_cmd + # 2. git_dir + # 3. git_ns (optional) + + git_cmd=${SSH_ORIGINAL_COMMAND%%\ *} + git_dir=${SSH_ORIGINAL_COMMAND#*\ } + + dequote + homedir_expand + + case "$git_dir" in + $HOME/git_namespace/*/public_git/*) + git_ns_subdir=${git_dir#$HOME/git_namespace/} + git_ns=${git_ns_subdir%%/*} + git_dir=$HOME/${git_ns_subdir#*/} + ;; + esac + + ;; + rsync\ --server\ --sender\ -de.LsfxC\ .\ public_git/|rsync\ --server\ --sender\ -de.LsfxC\ .\ public_git/|rsync\ --server\ --sender\ -de.Lsf\ .\ public_git/) + #echo "$SSH_ORIGINAL_COMMAND" >&2 + [ -d "$HOME"/public_git ] || { password_authentication; exit 1; } + exec $SSH_ORIGINAL_COMMAND + #exec rrsync -ro "$HOME"/public_git + exit 1 + ;; + rsync\ --server\ --sender\ *) + #echo "$SSH_ORIGINAL_COMMAND" >&2 + [ -d "$HOME"/public_rsync ] || { password_authentication; exit 1; } + exec rrsync -ro "$HOME"/public_rsync + exit 1 + ;; + rsync\ --server\ *) + [ -d "$HOME"/incoming_rsync -a "${SSH_CLIENT_FINGERPRINT}" ] || { password_authentication; exit 1; } + destdir=$HOME/incoming_rsync/$SSH_CLIENT_FINGERPRINT/ + mkdir -p "$destdir" && exec rrsync "$destdir" + exit 1 + ;; + *) + #password_authentication + $default_command + exit + ;; +esac + +if [ "$git_cmd" = 'git-upload-pack' ]; then + case "$git_dir" in + $HOME/public_git/*|public_git/*) + is_gitdir "$git_dir" || git_dir="$git_dir/.git" + if ! is_gitdir "$git_dir"; then + # git rev-parse --resolve-git-dir "${git_dir%/.git}" # show git's error message + deny + fi + if [ "$git_ns" -a -e "$git_dir/refs/namespaces/$git_ns" ]; then + export GIT_NAMESPACE="$git_ns" + # maybe_initialize_heads + fi + exec "$git_cmd" "$git_dir" + ;; + esac + +elif [ "$git_cmd" = 'git-receive-pack' ]; then + + if [ ! -d "$git_dir" ]; then + if valid_new_public_repo "$git_dir"; then + initialize_git "$git_dir" "$SSH_CLIENT_FINGERPRINT" + else + deny + fi + fi + +fi + +if authorized "$git_dir"; then + if [ "$git_cmd" = 'git-receive-pack' ]; then + if ! ssh_user_owns_repository + then + export GIT_NAMESPACE="$SSH_CLIENT_FINGERPRINT" + maybe_initialize_heads + printf '%s:%s\n' 'd@cryptonomic.net' "git_namespace/$GIT_NAMESPACE/${git_dir#${HOME}/}" >&2 + fi + fi + exec "$git_cmd" "$git_dir" +else + $default_command + exit + # echo 'Error: git access is unauthorized' >&2; exit 1 # unreached +fi diff --git a/src/samizdat-ssh-uid b/src/samizdat-ssh-uid new file mode 100755 index 0000000..2b4b311 --- /dev/null +++ b/src/samizdat-ssh-uid @@ -0,0 +1,39 @@ +#!/bin/dash + +die() { echo "$0: Error: $*" >&2; exit 1; } + +[ "$SSH_USER_AUTH" ] || die "not defined: \$SSH_USER_AUTH" +[ -f "$SSH_USER_AUTH" ] || die "file does not exist: \$SSH_USER_AUTH=${SSH_USER_AUTH}" + +PEMFILE="${SSH_USER_AUTH}.tmp" + +sed -ne 's/^publickey //p' < "${SSH_USER_AUTH}" > "${PEMFILE}" || die "could not rewrite SSH_USER_AUTH file" + +SSH_CLIENT_FINGERPRINT=$(ssh-keygen -r . -f "${PEMFILE}" | sed -ne 's/^. IN SSHFP [0-9]* 2 //p') && + [ "$SSH_CLIENT_FINGERPRINT" ] || die "could not determine ssh client fingerprint" + +read keytype keydata < "${PEMFILE}" || die "reading from PEMFILE=$PEMFILE" +case "$keytype" in + ssh-rsa|ssh-dss|ecdsa-sha2-nistp256|ssh-ed25519) + domain=$keytype.cryptonomic.net ;; + *) + die "Unsupported key type: $keytype" ;; +esac + +if [ "$1" = '--copy-pem' -a "$2" ] +then + if [ -d "$2" ] || mkdir "$2" + then + mv "${PEMFILE}" "$2"/${SSH_CLIENT_FINGERPRINT}.${keytype}.pem + fi +else + rm -f "${PEMFILE}" +fi + +env -i \ + SSH_CLIENT_FINGERPRINT="$SSH_CLIENT_FINGERPRINT" \ + SSH_CLIENT_KEYTYPE="$keytype" \ + SSH_CLIENT_DOMAIN="$domain" \ + SSH_CLIENT_PEMFILE="$PEMFILE" \ + SSH_CLIENT_KEYDATA="$keydata" + -- cgit v1.2.3