]> git.plutz.net Git - confetti/blob - cgilite/session.sh
1f4699e441357303b4dd4b6e4daeff3af1bb47c8
[confetti] / cgilite / session.sh
1 #!/bin/sh
2
3 [ -n "$include_session" ] && return 0
4 include_session="$0"
5
6 _DATE="$(date +%s)"
7 SESSION_TIMEOUT="${SESSION_TIMEOUT:-7200}"
8
9 if ! which uuencode >/dev/null; then
10   uuencode() { busybox uuencode "$@"; }
11 fi
12 if ! which sha256sum >/dev/null; then
13   sha256sum() { busybox sha256sum "$@"; }
14 fi
15
16 if which openssl >/dev/null; then
17   session_mac(){ { [ $# -gt 0 ] && printf %s "$*" || cat; } | openssl dgst -sha1 -hmac "$(server_key)" -binary |slopecode; }
18 else
19   # Gonzo MAC if openssl is unavailable
20   session_mac(){
21     { server_key | dd status=none bs=256 count=1 skip=1
22       { server_key | dd status=none bs=256 count=1
23         [ $# -gt 0 ] && printf %s "$*" || cat
24       } \
25       | sha256sum -;
26     } \
27     | sha256sum | cut -d\  -f1
28   }
29 fi
30
31 server_key(){
32   IDFILE="${IDFILE:-${_DATA:-.}/serverkey}"
33   if [ "$(stat -c %s "$IDFILE")" -ne 512 ] || ! cat "$IDFILE"; then
34     dd count=1 bs=512 if=/dev/urandom \
35     | tee "$IDFILE"
36   fi 2>&-
37 }
38
39 slopecode(){
40   # 6-Bit Code that retains sort order of input data, while beeing safe to use
41   # in ascii transmissions, unix file names, HTTP URLs, and HTML attributes
42
43   { [ $# -gt 0 ] && printf %s "$*" || cat; } \
44   | uuencode -m - | sed '
45     1d;$d; 
46     y;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/;0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;
47   '
48 }
49
50 randomid(){
51   dd bs=12 count=1 if=/dev/urandom 2>&- \
52   | slopecode
53 }
54
55 timeid(){
56   d=$(($_DATE % 4294967296))
57   { printf "$(
58       printf \\%o \
59         $((d / 16777216 % 256)) \
60         $((d / 65536 % 256)) \
61         $((d / 256 % 256)) \
62         $((d % 256))
63     )"
64     dd bs=8 count=1 if=/dev/urandom 2>&-
65   } | slopecode
66 }
67
68 transid(){
69   # transaction ID to modify a given file
70   local file="$1"
71   session_mac "$(stat -c %F%i%n%N%s%Y "$file" 2>&-)" "$SESSION_ID"
72 }
73
74 checkid(){ { [ $# -gt 0 ] && printf %s "$*" || cat; } | grep -m 1 -xE '[0-9a-zA-Z:=]{16}'; }
75
76 update_session(){
77   local session sid time sig checksig
78   unset SESSION_KEY SESSION_ID
79
80   read -r sid time sig <<-END
81         $(POST session_key || COOKIE session)
82         END
83   
84   checksig="$(session_mac "$sid" "$time")"
85   
86   if [ "$checksig" = "$sig" \
87        -a "$time" -ge "$_DATE" \
88        -a "$(checkid "$sid")" ] 2>&-
89   then
90     time=$(( $_DATE + $SESSION_TIMEOUT ))
91     sig="$(session_mac "$sid" "$time")"
92
93     SESSION_KEY="${sid} ${time} ${sig}"
94     SESSION_ID="${sid}"
95     return 0
96   else
97     return 1
98   fi
99
100 }
101
102 new_session(){
103   local sid time sig
104
105   debug "Setting up new session"
106   sid="$(randomid)"
107   time=$(( $_DATE + $SESSION_TIMEOUT ))
108   sig="$(session_mac "$sid" "$time")"
109
110   SESSION_KEY="${sid} ${time} ${sig}"
111   SESSION_ID="${sid}"
112 }
113
114 SESSION_BIND() {
115   # Set tamper-proof authenticated cookie
116   local key="$1" value="$2"
117   SET_COOKIE session "$key"="${value} $(session_mac "$value" "$SESSION_ID")" Path="/${_BASE#/}" SameSite=Strict HttpOnly
118 }
119
120 SESSION_VAR() {
121   # read authenticated cookie
122   # fail if value has been tampered with
123   local key="$1" value sig
124   value="$(COOKIE "$key")"
125   sig="${value##* }" value="${value% *}"
126   if [ "$sig" = "$(session_mac "$value" "$SESSION_ID")" ]; then
127     printf %s\\n "$value"
128   else
129     return 1
130   fi
131 }
132
133 SESSION_COOKIE() {
134   [ "$1" = new ] && new_session
135   SET_COOKIE 0 session="$SESSION_KEY" Path="/${_BASE#/}" SameSite=Strict HttpOnly
136 }
137
138 update_session || new_session