]> git.plutz.net Git - confetti/blob - session.sh
Squashed 'cgilite/' changes from 970afda..397847d
[confetti] / session.sh
1 #!/bin/sh
2
3 # Copyright 2018 - 2022 Paul Hänsch
4
5 # Permission to use, copy, modify, and/or distribute this software for any
6 # purpose with or without fee is hereby granted, provided that the above
7 # copyright notice and this permission notice appear in all copies.
8
9 # THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12 # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15 # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17 [ -n "$include_session" ] && return 0
18 include_session="$0"
19
20 export _DATE="$(date +%s)"
21 SESSION_TIMEOUT="${SESSION_TIMEOUT:-7200}"
22
23 if ! which uuencode >/dev/null; then
24   uuencode() { busybox uuencode "$@"; }
25 fi
26 if ! which sha256sum >/dev/null; then
27   sha256sum() { busybox sha256sum "$@"; }
28 fi
29
30 if which openssl >/dev/null; then
31   session_mac(){ { [ $# -gt 0 ] && printf %s "$*" || cat; } | openssl dgst -sha1 -hmac "$(server_key)" -binary |slopecode; }
32 else
33   # Gonzo MAC if openssl is unavailable
34   session_mac(){
35     { server_key | dd status=none bs=256 count=1 skip=1
36       { server_key | dd status=none bs=256 count=1
37         [ $# -gt 0 ] && printf %s "$*" || cat
38       } \
39       | sha256sum -;
40     } \
41     | sha256sum | cut -d\  -f1
42   }
43 fi
44
45 server_key(){
46   IDFILE="${IDFILE:-${_DATA:-.}/serverkey}"
47   if [ "$(stat -c %s "$IDFILE")" -ne 512 ] || ! cat "$IDFILE"; then
48     dd count=1 bs=512 if=/dev/urandom \
49     | tee "$IDFILE"
50   fi 2>&-
51 }
52
53 slopecode(){
54   # 6-Bit Code that retains sort order of input data, while beeing safe to use
55   # in ascii transmissions, unix file names, HTTP URLs, and HTML attributes
56
57   { [ $# -gt 0 ] && printf %s "$*" || cat; } \
58   | uuencode -m - | sed '
59     1d;$d; 
60     y;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/;0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;
61   '
62 }
63
64 randomid(){
65   dd bs=12 count=1 if=/dev/urandom 2>&- \
66   | slopecode
67 }
68
69 timeid(){
70   d=$(($_DATE % 4294967296))
71   { printf "$(
72       printf \\%o \
73         $((d / 16777216 % 256)) \
74         $((d / 65536 % 256)) \
75         $((d / 256 % 256)) \
76         $((d % 256))
77     )"
78     dd bs=8 count=1 if=/dev/urandom 2>&-
79   } | slopecode
80 }
81
82 transid(){
83   # transaction ID to modify a given file
84   local file="$1"
85   session_mac "$(stat -c %F%i%n%N%s%Y "$file" 2>&-)" "$SESSION_ID"
86 }
87
88 checkid(){ { [ $# -gt 0 ] && printf %s "$*" || cat; } | grep -m 1 -xE '[0-9a-zA-Z:=]{16}'; }
89
90 update_session(){
91   local session sid time sig checksig
92   unset SESSION_KEY SESSION_ID
93
94   read -r sid time sig <<-END
95         $(POST session_key || COOKIE session)
96         END
97   
98   checksig="$(session_mac "$sid" "$time")"
99   
100   if [ "$checksig" = "$sig" \
101        -a "$time" -ge "$_DATE" \
102        -a "$(checkid "$sid")" ] 2>&-
103   then
104     time=$(( $_DATE + $SESSION_TIMEOUT ))
105     sig="$(session_mac "$sid" "$time")"
106
107     SESSION_KEY="${sid} ${time} ${sig}"
108     SESSION_ID="${sid}"
109     return 0
110   else
111     return 1
112   fi
113
114 }
115
116 new_session(){
117   local sid time sig
118
119   debug "Setting up new session"
120   sid="$(randomid)"
121   time=$(( $_DATE + $SESSION_TIMEOUT ))
122   sig="$(session_mac "$sid" "$time")"
123
124   SESSION_KEY="${sid} ${time} ${sig}"
125   SESSION_ID="${sid}"
126 }
127
128 SESSION_BIND() {
129   # Set tamper-proof authenticated cookie
130   local key="$1" value="$2"
131   SET_COOKIE session "$key"="${value} $(session_mac "$value" "$SESSION_ID")" Path="/${_BASE#/}" SameSite=Strict HttpOnly
132 }
133
134 SESSION_VAR() {
135   # read authenticated cookie
136   # fail if value has been tampered with
137   local key="$1" value sig
138   value="$(COOKIE "$key")"
139   sig="${value##* }" value="${value% *}"
140   if [ "$sig" = "$(session_mac "$value" "$SESSION_ID")" ]; then
141     printf %s\\n "$value"
142   else
143     return 1
144   fi
145 }
146
147 SESSION_COOKIE() {
148   [ "$1" = new ] && new_session
149   SET_COOKIE 0 session="$SESSION_KEY" Path="/${_BASE#/}" SameSite=Strict HttpOnly
150 }
151
152 update_session || new_session