0

https://stem.torproject.org/faq.html#i-m-using-safe-cookie-authentication I am trying to automate the process. Problem is that nc/netcat,socat,telnet client response has to act based on the server nonce to generate the authentication token, and I don't know how to do that non interactively.

I have looked at https://unix.stackexchange.com/a/359665 but it is not what I want. I it passing the string before it is modified of course, that is why it is failing, file descriptor has helped so far but don't know how to progress now. It works with COOKIE and HASHEDPASSWORD, but not with SAFECOOKIE yet and that is what I am aiming for as COOKIE is being deprecated. The script will default to SAFECOOKIE, else COOKIE, else HASHEDPASSWORD. It will fail because SAFECOOKIE method is not working...

Modified from https://github.com/adrelanos/tor-ctrl/blob/master/usr/bin/tor-ctrl Here is what I have right now:

#!/usr/bin/env sh

## tor-ctrl is a commandline tool for executing commands on a Tor server via
## the controlport.  In order to get this to work, add "ControlPort 9051" and
## "CookieAuthentication 1" to your torrc and reload Tor.  Or if you want a
## fixed password, leave out "CookieAuthentication 1" and use the following
## line to create the appropriate HashedControlPassword entry for your torrc
## (you need to change yourpassword):
##
## echo "HashedControlPassword $(tor --hash-password yourpassword)"
##
## Many, many other things are possible, see
##              https://gitweb.torproject.org/torspec.git/tree/control-spec.txt
##
## Copyright (c) 2007 by Stefan Behte <stefan.behte@gmx.net> <http://ge.mine.nu/>
## Portion Copyright (C) 2013 - 2020 ENCRYPTED SUPPORT LP <adrelanos@riseup.net>
## Portion Copyright (C) 2021 nyxnor <nyxnor@protonmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <https://www.gnu.org/licenses/>.
##
## On Debian systems, the full text of the GNU General Public
## License version 3 can be found in the file
## `/usr/share/common-licenses/GPL-3'.
##
## tor-ctrl was originally written by Stefan Behte
## further developed by Patrick Schleizer and Nyxnor
##
## nyxnor major changes made were for
## - portability
##  - be POSIX
##  - use hexdump instead of xxd for normal COOKIE, xxd still needed for SAFECOOKIE
##  - autodetect CookieAuthFile by sending PROTOCOLINFO and receiving COOKIEFILE
##  - use any compatible networking tool available: nc socat telnet
##  - use any compatible hex converter tool available: xxd, hexdump, od
##  - accept [addr:]port format for ControlPort cli argument
## - stability
##  - test if ControlSocket file exists or if it is possible to connect to ControlPort
## - security
##  - regarding AUTH METHODS, if SAFECOOKIE is enabled and xxd is available, use it,
##    else fallback to COOKIE if available,
##    if only HASHEDPASSWORD is available and password is provided, use it else fail.

me="${0##*/}"

usage(){
  printf '%s\n' "usage: ${me} [-cfCaPps] [argument] [-v]

  -c [command]   command to execute
                 notice: always \"double quote\" your command

  -f [file]      file to execute commands from
                 notice: only one command per line

  -C [path]      path to tor's control socket
                 default: /run/tor/control
                 notice: do not forget to adjust your torrc

  -a [path]      path to tor's control_auth_cookie
                 default: found via PROTOCOLINFO command, COOKIEFILE=/path
                 notice: do not forget to adjust your torrc

  -P [port]      tor ControlPort
                 default: 9051

  -p [pwd]       use password [var] instead of Tor's control_auth_cookie
                 default: not used
                 notice: do not forget to adjust your torrc

  -s [time]      sleep [var] seconds after each command sent
                 default: 1 second
                 notice: for GETCONF, you can use smaller pause times
                 than for SETCONF; this is due to telnet's behavior.

  -v             verbose
                 default: not set
                 notice: the default output is the return code ;)
                 You probably want to set -v when running manually

  Examples:      tor-ctrl -c \"SETCONF bandwidthrate=1mb\"
                 tor-ctrl -v -s 0 -c \"GETINFO User\"
                 tor-ctrl -v -s 0 -P 9051 -p foobar -c \"GETCONF bandwidthrate\"

You should have a look at
https://gitweb.torproject.org/torspec.git/tree/control-spec.txt"
  exit 1
}

checkprogs(){
  prog_net="nc socat telnet"
  while :; do
    for p in ${prog_net}; do
      command -v "${p}" >/dev/null && socket_prog="$(command -v "${p}")" && break
    done
    break
  done
  if [ -z "${socket_prog}" ]; then
    printf '%s\n' "Install one of the following programs to connect to sockets: ${prog_net}"
    exit 1
  fi

  case "${socket_type}" in
    unix)
      if [ ! -f "${tor_control_unix}" ]; then
        printf '%s\n' "Couldn't find ControlSocket ${tor_control_unix}"
        exit 1
      fi
      case "${socket_prog##*/}" in
        nc|netcat) socket_prog="${socket_prog} -U ${tor_control_unix}";;
        socat) socket_prog="${socket_prog} - UNIX-CONNECT:${tor_control_unix}";;
        telnet) printf "Telnet doesn't support unix domain sockets"; exit 1;;
      esac
    ;;
    tcp)
      case "${socket_prog##*/}" in
        nc|netcat)
          ${socket_prog} -z "${tor_control_tcp_ip}" "${tor_control_tcp_port}" || { printf '%s\n' "Connetion refused to ControlPort ${tor_control_tcp_ip}:${tor_control_tcp_port}"; exit 1; }
          socket_prog="${socket_prog} ${tor_control_tcp_ip} ${tor_control_tcp_port}"
        ;;
        socat)
          ${socket_prog} /dev/null TCP:"${tor_control_tcp_ip}:${tor_control_tcp_port}" || { printf '%s\n' "Connetion refused to ControlPort ${tor_control_tcp_ip}:${tor_control_tcp_port}"; exit 1; }
          socket_prog="${socket_prog} - TCP:${tor_control_tcp_ip}:${tor_control_tcp_port}"
        ;;
        telnet)
          printf "\x1dclose\x0d" | ${socket_prog} "${tor_control_tcp_ip}" "${tor_control_tcp_port}" || { printf '%s\n' "Connetion refused to ControlPort ${tor_control_tcp_ip}:${tor_control_tcp_port}"; exit 1; }
          socket_prog="${socket_prog} ${tor_control_tcp_ip} ${tor_control_tcp_port} 2>/dev/null"
        ;;
        # bash)
        #   bash -c "{ printf '%s\n%s\n' \"PROTOCOLINFO\" \"QUIT\" >&3; cat <&3 ; } 3<> /dev/tcp/${tor_control_tcp_ip}/${tor_control_tcp_port}"
        # ;;
      esac
    ;;
  esac
}

login(){
  auth="$({ sendcmd "PROTOCOLINFO"; sendcmd "QUIT"; } 3>&1 | ${socket_prog} | grep "AUTH")"
  auth_methods="$(printf '%s\n' "${auth}" | sed "s/250-AUTH METHODS=//" | cut -d " " -f1 | tr "," "\n")"
  hashedpassword_enabled="$(printf '%s\n' "${auth_methods}" | grep "^HASHEDPASSWORD$")"
  tor_cookie="$(printf '%s\n' "${auth}" | sed "s/.*COOKIEFILE=//" | tr -d "\"" | tr -d "\\r")"
  if [ -n "${tor_cookie}" ] && [ -f "${tor_cookie}" ]; then
    cookie_string="$(xxd -u -p -c 32 "${tor_cookie}")"
    safecookie_enabled="$(printf '%s\n' "${auth_methods}" | grep "^SAFECOOKIE$")"
    cookie_enabled="$(printf '%s\n' "${auth_methods}" | grep "^COOKIE$")"
  fi

  if [ -n "${safecookie_enabled}" ] && test -f "${tor_cookie}" && command -v xxd >/dev/null && test -c /dev/urandom; then
    client_nonce="$(xxd -u -p -c 32 -l 32 /dev/urandom)"
    authchallenge_string="AUTHCHALLENGE SAFECOOKIE ${client_nonce}"
  elif [ -n "${cookie_enabled}" ] && test -f "${tor_cookie}"; then
    ## xxd is only needed when authenticating with SAFECOOKIE
    while :; do
      command -v xxd >/dev/null && hex_prog="xxd" && break
      command -v hexdump >/dev/null && hex_prog="hexdump" && break
      command -v od >/dev/null && hex_prog="od" && break
      break
    done
    case "${hex_prog}" in
      xxd) auth_string="AUTHENTICATE $(xxd -u -p -c 32 "${tor_cookie}")";;
      hexdump) auth_string="AUTHENTICATE $(hexdump -e '32/1 "%02x""\n"' "${tor_cookie}")";;
      od) auth_string="AUTHENTICATE $(od -t x1 "${tor_cookie}" | head -n 2 | cut -d " " -f2- | tr -d "\n" | tr -d " ")";;
      *) printf "Missing program to dump hex. Install either 'hexdump' or 'xxd' or 'od'."
    esac
  elif [ -n "${hashedpassword_enabled}" ] && [ -z "${tor_cookie}" ] && [ -n "${tor_password}" ]; then
    auth_string="AUTHENTICATE \"${tor_password}\""
  fi

  if printf %s"${auth_methods}" | grep -q "AUTH METHODS=NULL"; then
    sendcmd "QUIT"
    printf "Configure an authentication method to connect to tor's controller on your torrc, chose either or both options: HashedControlPassword or CookieAuthentication"
    exit 1
  fi
}

sendcmd(){
  printf '%s\n' "${1}" 1>&3
}

finish(){
  sendcmd "${cmd_cli}"
  sendcmd "QUIT"
  str="$(cat <&3 2>/dev/null)"
  [ "${verbose}" -ge 1 ] && printf '%s\n' "${str}"
  printf '%s\n' "${str}" | { [ "$(grep -c "^250 ")" != 3 ] && exit 1; }
}

filepipe(){
  while read -r line; do
    sendcmd "${line}"
  done < "${1}"
  sendcmd "QUIT"
}

be_verbose(){
  str="$(cat)"
  [ "${verbose}" -ge 1 ] && printf '%s\n' "${str}"
  printf '%s\n' "${str}" | { [ "$(grep -c "^250 ")" != 3 ] && exit 1; }
}

while getopts ":a:c:C:s:p:P:f:vh" Option; do
  case ${Option} in
    c) cmd_cli="${OPTARG}";;
    #f) cmd_file="${OPTARG}";;
    a) tor_cookie="${OPTARG}";;
    p) tor_password="${OPTARG}";;
    C) tor_control_unix="${OPTARG}";;
    P) tor_control_tcp_port="${OPTARG}";;
    s) sleep_n="${OPTARG}";;
    v) verbose=1;;
    h) usage;;
    *) usage;;
  esac
done


## if ControlSocket is not given (unix domain socket)
## fallback to ControlPort 127.0.0.1:9051 (tcp socket)
case "${tor_control_unix}" in
  "") socket_type="tcp";;
  *) socket_type="unix";;
esac

: "${sleep_n:=1}"
: "${verbose:=0}"
: "${tor_control_tcp_port:=9051}"
tor_control_tcp_ip="${tor_control_tcp_port%%:*}"
tor_control_tcp_port="${tor_control_tcp_port##*:}"
[ "${tor_control_tcp_ip}" = "${tor_control_tcp_port}" ] && tor_control_tcp_ip="127.0.0.1"


## main

# if [ -e "${cmd_file}" ]; then
#   checkprogs
#   login
#   { sendcmd "${auth_string}"; filepipe "${cmd_cli}"; } | ${socket_prog} | be_verbose
# fi

if [ -n "${cmd_cli}" ]; then
  checkprogs
  login
  if [ -n "${safecookie_enabled}" ] && test -f "${tor_cookie}" && command -v xxd >/dev/null && test -c /dev/urandom; then
    {
      ## TODO server_nonce is not working, how to solve a challenge and continue answering the server?
      set -x
      server_nonce="$(sendcmd "${authchallenge_string}" 3>&1 | sed "s/.* SERVERNONCE=//" | tr -d "\\r")"
      # shellcheck disable=SC2030
      auth_string="AUTHENTICATE $(printf '%s%s%s\n' "${cookie_string}" "${client_nonce}" "${server_nonce}" | xxd -r -p | openssl dgst -sha256 -binary -hmac "Tor safe cookie authentication controller-to-server hash" | xxd -p -u -c 32)"
      sendcmd "${auth_string}"
      finish
    } 3>&1 | ${socket_prog}
  else
    {
      # shellcheck disable=SC2030,2031
      sendcmd "${auth_string}"
      finish
    } 3>&1 | ${socket_prog}
  fi
else
  usage
fi

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.