X-Git-Url: http://git.plutz.net/?p=cgilite;a=blobdiff_plain;f=session.sh;h=ee5c4993a72df047fe0fc67b3c384f2b5733be48;hp=3f3839ae8b167975a5e5231582509c5484d1f025;hb=HEAD;hpb=41bcab02ff4b11d22a2624e7afabc50e9c33ae2f diff --git a/session.sh b/session.sh index 3f3839a..c3a44e8 100755 --- a/session.sh +++ b/session.sh @@ -1,11 +1,50 @@ #!/bin/sh +# Copyright 2018 - 2022 Paul Hänsch +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + [ -n "$include_session" ] && return 0 include_session="$0" +export _DATE="$(date +%s)" +SESSION_TIMEOUT="${SESSION_TIMEOUT:-7200}" + +if ! which uuencode >/dev/null; then + uuencode() { busybox uuencode "$@"; } +fi +if ! which sha256sum >/dev/null; then + sha256sum() { busybox sha256sum "$@"; } +fi + +if which openssl >/dev/null; then + session_mac(){ { [ $# -gt 0 ] && printf %s "$*" || cat; } | openssl dgst -sha1 -hmac "$(server_key)" -binary |slopecode; } +else + # Gonzo MAC if openssl is unavailable + session_mac(){ + { server_key | dd status=none bs=256 count=1 skip=1 + { server_key | dd status=none bs=256 count=1 + [ $# -gt 0 ] && printf %s "$*" || cat + } \ + | sha256sum -; + } \ + | sha256sum | cut -d\ -f1 + } +fi + server_key(){ IDFILE="${IDFILE:-${_DATA:-.}/serverkey}" - if ! grep -m1 -xE '.{512}' "$IDFILE"; then + if [ "$(stat -c %s "$IDFILE")" -ne 512 ] || ! cat "$IDFILE"; then dd count=1 bs=512 if=/dev/urandom \ | tee "$IDFILE" fi 2>&- @@ -15,19 +54,20 @@ slopecode(){ # 6-Bit Code that retains sort order of input data, while beeing safe to use # in ascii transmissions, unix file names, HTTP URLs, and HTML attributes - uuencode -m - | sed ' + { [ $# -gt 0 ] && printf %s "$*" || cat; } \ + | uuencode -m - | sed ' 1d;$d; y;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/;0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz; ' } randomid(){ - dd bs=12 count=1 if=/dev/urandom \ - | slopecode 2>&- + dd bs=12 count=1 if=/dev/urandom 2>&- \ + | slopecode } timeid(){ - d=$(($(date +%s) % 4294967296)) + d=$(($_DATE % 4294967296)) { printf "$( printf \\%o \ $((d / 16777216 % 256)) \ @@ -35,34 +75,78 @@ timeid(){ $((d / 256 % 256)) \ $((d % 256)) )" - dd bs=8 count=1 if=/dev/urandom - } | slopecode 2>&- + dd bs=8 count=1 if=/dev/urandom 2>&- + } | slopecode } -checkid(){ grep -m 1 -xE '[0-9a-zA-Z:_]{16}'; } +transid(){ + # transaction ID to modify a given file + local file="$1" + session_mac "$(stat -c %F%i%n%N%s%Y "$file" 2>&-)" "$SESSION_ID" +} + +checkid(){ { [ $# -gt 0 ] && printf %s "$*" || cat; } | grep -m 1 -xE '[0-9a-zA-Z:=]{16}'; } update_session(){ - local session sid time sig serverkey checksig + local session sid time sig checksig + unset SESSION_KEY SESSION_ID - IFS=- read -r sid time sig <<-END - $(COOKIE session) + read -r sid time sig <<-END + $(POST session_key || COOKIE session) END - serverkey="$(server_key)" - checksig="$(printf %s "$sid" "$time" "$serverkey" | sha256sum)" - checksig="${checksig%% *}" - d=$(date +%s) + checksig="$(session_mac "$sid" "$time")" - if [ "$checksig" != "$sig" -o "$time" -lt "$d" ] 2>&-; then - sid="$(randomid)" + if [ "$checksig" = "$sig" \ + -a "$time" -ge "$_DATE" \ + -a "$(checkid "$sid")" ] 2>&- + then + time=$(( $_DATE + $SESSION_TIMEOUT )) + sig="$(session_mac "$sid" "$time")" + + SESSION_KEY="${sid} ${time} ${sig}" + SESSION_ID="${sid}" + return 0 + else + return 1 + fi + +} + +new_session(){ + local sid time sig + + debug "Setting up new session" + sid="$(randomid)" + time=$(( $_DATE + $SESSION_TIMEOUT )) + sig="$(session_mac "$sid" "$time")" + + SESSION_KEY="${sid} ${time} ${sig}" + SESSION_ID="${sid}" +} + +SESSION_BIND() { + # Set tamper-proof authenticated cookie + local key="$1" value="$2" + SET_COOKIE session "$key"="${value} $(session_mac "$value" "$SESSION_ID")" Path="/${_BASE#/}" SameSite=Strict HttpOnly +} + +SESSION_VAR() { + # read authenticated cookie + # fail if value has been tampered with + local key="$1" value sig + value="$(COOKIE "$key")" + sig="${value##* }" value="${value% *}" + if [ "$sig" = "$(session_mac "$value" "$SESSION_ID")" ]; then + printf %s\\n "$value" + else + return 1 fi +} - time=$(( $(date +%s) + 7200 )) - sig="$(printf %s "$sid" "$time" "$serverkey" |sha256sum)" - sig="${sig%% *}" - printf %s\\n "${sid}-${time}-${sig}" +SESSION_COOKIE() { + [ "$1" = new ] && new_session + SET_COOKIE 0 session="$SESSION_KEY" Path="/${_BASE#/}" SameSite=Strict HttpOnly } -SESSION_ID="$(update_session)" -SET_COOKIE 0 "session=$SESSION_ID" HttpOnly -SESSION_ID="${SESSION_ID%%-*}" +update_session || new_session