diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/dyndns-command.sh | 92 | ||||
-rwxr-xr-x | src/samizdat-ssh-command | 324 | ||||
-rwxr-xr-x | src/samizdat-ssh-uid | 39 |
3 files changed, 0 insertions, 455 deletions
diff --git a/src/dyndns-command.sh b/src/dyndns-command.sh deleted file mode 100755 index 375d50b..0000000 --- a/src/dyndns-command.sh +++ /dev/null | |||
@@ -1,92 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | die() { printf '%s\n' "$*" >&2; exit 1; } | ||
4 | |||
5 | sql_string() | ||
6 | { | ||
7 | printf '%s' "'${1//\'/\'\'}'" | ||
8 | } | ||
9 | |||
10 | powerdns_sqlite_add_replace_record() | ||
11 | { | ||
12 | local sql_new_domain="$(sql_string "$1.$3")" | ||
13 | local sql_ip_address="$(sql_string "$2")" | ||
14 | local sql_domain="$(sql_string "$3")" | ||
15 | local record_type="$(sql_string "$4")" | ||
16 | |||
17 | DBDIR=/etc/powerdns | ||
18 | DBNAME=powerdns.sqlite3 | ||
19 | DB=$DBDIR/$DBNAME | ||
20 | |||
21 | test -r $DB && test -w $DB || die "Wrong permissions on $DB" | ||
22 | test -r $DBDIR && test -w $DBDIR || die "Wrong permissions on $DBDIR" | ||
23 | |||
24 | sqlite3 $DB <<END | ||
25 | ${SQL_ECHO:+.echo on} | ||
26 | BEGIN; | ||
27 | DELETE FROM records WHERE type=$record_type AND name=$sql_new_domain; | ||
28 | |||
29 | INSERT INTO records | ||
30 | |||
31 | (domain_id, | ||
32 | name, | ||
33 | type, | ||
34 | content, | ||
35 | ttl, | ||
36 | prio) | ||
37 | |||
38 | SELECT | ||
39 | id, | ||
40 | $sql_new_domain, | ||
41 | $record_type, | ||
42 | $sql_ip_address, | ||
43 | 3600, | ||
44 | 0 | ||
45 | FROM domains | ||
46 | WHERE name=$sql_domain; | ||
47 | COMMIT; | ||
48 | END | ||
49 | } | ||
50 | |||
51 | add() | ||
52 | { | ||
53 | local record_type | ||
54 | case "$2" in | ||
55 | *.*.*.*) record_type=A ;; | ||
56 | *:*) record_type=AAAA ;; | ||
57 | *) exit 1 ;; | ||
58 | esac | ||
59 | |||
60 | powerdns_sqlite_add_replace_record "$1" "$2" "$domain" "$record_type" \ | ||
61 | && printf '%s %s\n' "$1.$domain $2" | ||
62 | } | ||
63 | |||
64 | main() | ||
65 | { | ||
66 | add "$subdomain" "$ip_address" | ||
67 | |||
68 | set -- $SSH_ORIGINAL_COMMAND | ||
69 | while [ $# -ge 2 ]; do | ||
70 | d=$1 | ||
71 | ip=$2 | ||
72 | shift 2 | ||
73 | |||
74 | case "$d" in | ||
75 | *.*) continue;; | ||
76 | esac | ||
77 | |||
78 | add "$d.$subdomain" "$ip" | ||
79 | done | ||
80 | } | ||
81 | |||
82 | PEM_DEST=$HOME/public_rsync | ||
83 | |||
84 | PATH=$HOME/bin:$PATH | ||
85 | |||
86 | eval "$(samizdat-ssh-uid --copy-pem "$PEM_DEST")" | ||
87 | |||
88 | domain=${SSH_CLIENT_DOMAIN} | ||
89 | subdomain=${SSH_CLIENT_FINGERPRINT} | ||
90 | ip_address=${SSH_CLIENT%% *} | ||
91 | |||
92 | main "$@" | ||
diff --git a/src/samizdat-ssh-command b/src/samizdat-ssh-command deleted file mode 100755 index a03b554..0000000 --- a/src/samizdat-ssh-command +++ /dev/null | |||
@@ -1,324 +0,0 @@ | |||
1 | #!/bin/dash | ||
2 | default_command="$HOME/samizdat-default-command" | ||
3 | if ! [ "$default_command" ] || ! [ -e "$default_command" ] | ||
4 | then | ||
5 | default_command=password_authentication | ||
6 | fi | ||
7 | |||
8 | authorize() | ||
9 | { | ||
10 | local authtype authdata forced_command comment authorized_keys_line | ||
11 | [ "$SSH_USER_AUTH" -a -f "$SSH_USER_AUTH" ] || return | ||
12 | read authtype authdata < "$SSH_USER_AUTH" || return | ||
13 | [ "$authtype" = publickey ] || return | ||
14 | |||
15 | forced_command="command=\"${0} authorize-full-access\",no-port-forwarding" | ||
16 | comment="samizdat: password-authenticated from ${SSH_CONNECTION%% *}" | ||
17 | |||
18 | authorized_keys_line="$forced_command $authdata $comment" | ||
19 | sentinel='Samizdat - YES WE CAN' | ||
20 | |||
21 | su - "$USER" -c 'mkdir -p "$HOME"/.ssh; touch "$HOME"/.ssh/authorized_keys' && | ||
22 | add_before_sentinel "$sentinel" \ | ||
23 | "$authorized_keys_line" \ | ||
24 | "$HOME"/.ssh/authorized_keys | ||
25 | } | ||
26 | |||
27 | add_before_sentinel() | ||
28 | { | ||
29 | local sentinel="$1" add_me="$2" target="$3" | ||
30 | sed -i.samizdat~ \ | ||
31 | -e "/$sentinel/i $add_me" \ | ||
32 | "$target" | ||
33 | } | ||
34 | |||
35 | password_authentication() | ||
36 | { | ||
37 | [ "$USER" ] || { echo 'Error: no $USER' >&2; exit 1; } | ||
38 | [ "$SSH_CLIENT_FINGERPRINT" ] || { echo 'Error: no $SSH_CLIENT_FINGERPRINT' >&2; exit 1; } | ||
39 | |||
40 | tty=$(tty) && [ "$tty" != 'not a tty' ] || tty= | ||
41 | |||
42 | if [ "$SSH_ORIGINAL_COMMAND" ]; then | ||
43 | msg='You are not authorized to execute the command: %s\n' | ||
44 | if ! [ "$tty" ]; then | ||
45 | msg="$msg"'To authorize your public key via password, reconnect without a command, or with a terminal attached.\n' | ||
46 | msg="$msg"'To attach a terminal, use the "-t" option to ssh.\n' | ||
47 | else | ||
48 | msg="$msg"'To authorize your public key and execute the command, enter your password.\n' | ||
49 | fi | ||
50 | printf "\n$msg\n" "$SSH_ORIGINAL_COMMAND" >&2 | ||
51 | if ! [ "$tty" ]; then | ||
52 | exit 1 | ||
53 | fi | ||
54 | else | ||
55 | msg='You are not authorized to log in.\n' | ||
56 | msg="$msg"'To authorize your public key and log in, enter your password.\n' | ||
57 | printf "\n$msg\n" >&2 | ||
58 | fi | ||
59 | authorize || exit $? | ||
60 | # TODO: blacklist after too many authentication failures | ||
61 | if [ "$SSH_ORIGINAL_COMMAND" ]; then | ||
62 | exec "$SSH_ORIGINAL_COMMAND" | ||
63 | else | ||
64 | exec $(getent passwd "$USER"|cut -d: -f7) -i | ||
65 | fi | ||
66 | exit $? # exec failed | ||
67 | } | ||
68 | |||
69 | die() { echo "Error: $*" >&2; exit 1; } | ||
70 | |||
71 | dequote() | ||
72 | { | ||
73 | # Sorry about the slashes. The perl would be: s{ \\(.) | '([^']+)' }{ $1$2 }gx | ||
74 | git_dir=$(echo -n "$git_dir" | sed -e "s/\\\\\\(.\\)\\|'\\([^']\\+\\)'/\\1\\2/g") | ||
75 | } | ||
76 | |||
77 | homedir_expand() | ||
78 | { | ||
79 | git_dir=$(homedir_expand_arg "$git_dir") && | ||
80 | [ "$git_dir" ] || die "Could not expand home directory. HOME=$HOME, USER=$USER, id=$(id)" | ||
81 | } | ||
82 | |||
83 | homedir_expand_arg() | ||
84 | { | ||
85 | [ "$HOME" ] || die '$HOME is not set.' | ||
86 | case "$1" in | ||
87 | \~) echo "$HOME";; | ||
88 | \~/*) echo "${HOME}${1#\~}";; | ||
89 | \~*) | ||
90 | local u | ||
91 | u=${1#\~} | ||
92 | u=${u%%/*} | ||
93 | u=$(getent passwd "$u"|cut -f6 -d:) && [ "$u" ] || return 1 | ||
94 | echo "$u/${1#*/}";; | ||
95 | /*) echo "$1";; | ||
96 | *) echo "$HOME/$1";; | ||
97 | esac | ||
98 | } | ||
99 | |||
100 | initialize_git() | ||
101 | { | ||
102 | local git_dir="$1" anonymous="$2" | ||
103 | if ! [ -e "$git_dir" ]; then | ||
104 | mkdir -p "$(dirname "$git_dir")" | ||
105 | git init --bare "$git_dir" >&2 | ||
106 | if [ "$anonymous" ]; then | ||
107 | git --git-dir "$git_dir" config samizdat.allow-anonymous-access true | ||
108 | git --git-dir "$git_dir" config samizdat.anonymous-ssh-owner "$anonymous" | ||
109 | fi | ||
110 | fi | ||
111 | } | ||
112 | |||
113 | is_gitdir() { git rev-parse --resolve-git-dir "$1" >/dev/null 2>&1; } | ||
114 | |||
115 | deny() { echo 'Error: permission denied.' >&2; exit 1; } | ||
116 | |||
117 | valid_new_public_repo() | ||
118 | { | ||
119 | local git_dir="$1" | ||
120 | [ ! -e "$git_dir" ] || return | ||
121 | [ "$HOME" -a -d "$HOME"/public_git ] || return | ||
122 | local dirname="$(dirname "$git_dir")" | ||
123 | |||
124 | case "$git_dir" in | ||
125 | *.git) ;; | ||
126 | *) | ||
127 | echo 'Error: public repos must be named *.git' >&2 | ||
128 | return 1 ;; | ||
129 | esac | ||
130 | |||
131 | case "$dirname" in | ||
132 | $HOME/public_git) return 0 ;; | ||
133 | $HOME/public_git/*) | ||
134 | # Ensure that no parent directory is named *.git | ||
135 | # Also enforce a maximum depth of 4. | ||
136 | # Valid: public_git/a/b/c/d.git | ||
137 | # Invalid: public_git/a/b/c/d/e.git | ||
138 | local n relative="${git_dir#$HOME/public_git/}" | ||
139 | for n in 1 2 3 4; do | ||
140 | local topmost="${relative%%/*}" | ||
141 | case "$topmost" in | ||
142 | "$relative") return 0;; | ||
143 | *.git) return 1;; | ||
144 | esac | ||
145 | relative=${relative#$topmost/} | ||
146 | done | ||
147 | echo 'Error: directories nest too deep' >&2 | ||
148 | return 1 | ||
149 | ;; | ||
150 | *) return 1 ;; | ||
151 | esac | ||
152 | } | ||
153 | |||
154 | check_if_ssh_user_owns_repository() | ||
155 | { | ||
156 | git --git-dir "$git_dir" config --get-all samizdat.anonymous-ssh-owner | grep -xqF "$SSH_CLIENT_FINGERPRINT" | ||
157 | } | ||
158 | ssh_user_owns_repository() | ||
159 | { | ||
160 | if [ -z "$SSH_USER_OWNS_REPOSITORY" ]; then | ||
161 | check_if_ssh_user_owns_repository | ||
162 | SSH_USER_OWNS_REPOSITORY=$? | ||
163 | fi | ||
164 | return $SSH_USER_OWNS_REPOSITORY | ||
165 | } | ||
166 | |||
167 | is_public_repository() | ||
168 | { | ||
169 | case "$git_dir" in | ||
170 | */../*) false;; | ||
171 | "$HOME"/public_git/*) true;; | ||
172 | *) false;; | ||
173 | esac | ||
174 | } | ||
175 | |||
176 | authorized() | ||
177 | { | ||
178 | # TODO: check SSH_CLIENT_FINGERPRINT against a blacklist | ||
179 | ssh_user_owns_repository && return | ||
180 | is_public_repository && return | ||
181 | test "$(git --git-dir "$1" config --bool --get samizdat.allow-anonymous-access)" = true 2>/dev/null && return 0 | ||
182 | # TODO: check SSH_CLIENT_FINGERPRINT against a whitelist | ||
183 | } | ||
184 | |||
185 | maybe_initialize_heads() | ||
186 | { | ||
187 | [ "$GIT_NAMESPACE" ] || die 'Programmer error' | ||
188 | heads=$git_dir/refs/namespaces/$GIT_NAMESPACE/refs/heads | ||
189 | mkdir -p "$heads" | ||
190 | found_file=$(find "$heads" -type f -print -quit) | ||
191 | [ "$found_file" ] && return | ||
192 | [ -e "$git_dir/refs/heads/master" ] && cp "$git_dir/refs/heads/master" "$heads" | ||
193 | # TODO: copy actual file 'HEAD' and whatever it references | ||
194 | } | ||
195 | |||
196 | |||
197 | if [ "$1" = "authorize-full-access" ]; then | ||
198 | case "$SSH_ORIGINAL_COMMAND" in | ||
199 | git-receive-pack\ *) | ||
200 | git_cmd=git-receive-pack | ||
201 | git_dir="${SSH_ORIGINAL_COMMAND#git-receive-pack }" | ||
202 | dequote | ||
203 | homedir_expand | ||
204 | initialize_git "$git_dir" | ||
205 | exec "$git_cmd" "$git_dir" | ||
206 | ;; | ||
207 | "") | ||
208 | shell=$(getent passwd $USER|cut -d: -f7) | ||
209 | argv0=-${shell##*/} | ||
210 | if which chpst >/dev/null 2>&1; then | ||
211 | exec chpst -b "$argv0" "$shell" | ||
212 | else | ||
213 | exec "$shell" | ||
214 | fi | ||
215 | ;; | ||
216 | *) | ||
217 | exec /bin/sh -c "$SSH_ORIGINAL_COMMAND" | ||
218 | ;; | ||
219 | esac | ||
220 | exit | ||
221 | fi | ||
222 | |||
223 | eval "$(samizdat-ssh-uid)" || die eval | ||
224 | |||
225 | if [ $# -gt 0 ] | ||
226 | then | ||
227 | exec "$@" | ||
228 | exit | ||
229 | fi | ||
230 | |||
231 | # TODO: call password_authentication on all authorization failures | ||
232 | |||
233 | #echo "SSH_ORIGINAL_COMMAND=$SSH_ORIGINAL_COMMAND" >&2 | ||
234 | case "$SSH_ORIGINAL_COMMAND" in | ||
235 | git-upload-pack\ *|git-receive-pack\ *) | ||
236 | # set three variables | ||
237 | # 1. git_cmd | ||
238 | # 2. git_dir | ||
239 | # 3. git_ns (optional) | ||
240 | |||
241 | git_cmd=${SSH_ORIGINAL_COMMAND%%\ *} | ||
242 | git_dir=${SSH_ORIGINAL_COMMAND#*\ } | ||
243 | |||
244 | dequote | ||
245 | homedir_expand | ||
246 | |||
247 | case "$git_dir" in | ||
248 | $HOME/git_namespace/*/public_git/*) | ||
249 | git_ns_subdir=${git_dir#$HOME/git_namespace/} | ||
250 | git_ns=${git_ns_subdir%%/*} | ||
251 | git_dir=$HOME/${git_ns_subdir#*/} | ||
252 | ;; | ||
253 | esac | ||
254 | |||
255 | ;; | ||
256 | rsync\ --server\ --sender\ -de.LsfxC\ .\ public_git/|rsync\ --server\ --sender\ -de.LsfxC\ .\ public_git/|rsync\ --server\ --sender\ -de.Lsf\ .\ public_git/) | ||
257 | #echo "$SSH_ORIGINAL_COMMAND" >&2 | ||
258 | [ -d "$HOME"/public_git ] || { password_authentication; exit 1; } | ||
259 | exec $SSH_ORIGINAL_COMMAND | ||
260 | #exec rrsync -ro "$HOME"/public_git | ||
261 | exit 1 | ||
262 | ;; | ||
263 | rsync\ --server\ --sender\ *) | ||
264 | #echo "$SSH_ORIGINAL_COMMAND" >&2 | ||
265 | [ -d "$HOME"/public_rsync ] || { password_authentication; exit 1; } | ||
266 | exec rrsync -ro "$HOME"/public_rsync | ||
267 | exit 1 | ||
268 | ;; | ||
269 | rsync\ --server\ *) | ||
270 | [ -d "$HOME"/incoming_rsync -a "${SSH_CLIENT_FINGERPRINT}" ] || { password_authentication; exit 1; } | ||
271 | destdir=$HOME/incoming_rsync/$SSH_CLIENT_FINGERPRINT/ | ||
272 | mkdir -p "$destdir" && exec rrsync "$destdir" | ||
273 | exit 1 | ||
274 | ;; | ||
275 | *) | ||
276 | #password_authentication | ||
277 | $default_command | ||
278 | exit | ||
279 | ;; | ||
280 | esac | ||
281 | |||
282 | if [ "$git_cmd" = 'git-upload-pack' ]; then | ||
283 | case "$git_dir" in | ||
284 | $HOME/public_git/*|public_git/*) | ||
285 | is_gitdir "$git_dir" || git_dir="$git_dir/.git" | ||
286 | if ! is_gitdir "$git_dir"; then | ||
287 | # git rev-parse --resolve-git-dir "${git_dir%/.git}" # show git's error message | ||
288 | deny | ||
289 | fi | ||
290 | if [ "$git_ns" -a -e "$git_dir/refs/namespaces/$git_ns" ]; then | ||
291 | export GIT_NAMESPACE="$git_ns" | ||
292 | # maybe_initialize_heads | ||
293 | fi | ||
294 | exec "$git_cmd" "$git_dir" | ||
295 | ;; | ||
296 | esac | ||
297 | |||
298 | elif [ "$git_cmd" = 'git-receive-pack' ]; then | ||
299 | |||
300 | if [ ! -d "$git_dir" ]; then | ||
301 | if valid_new_public_repo "$git_dir"; then | ||
302 | initialize_git "$git_dir" "$SSH_CLIENT_FINGERPRINT" | ||
303 | else | ||
304 | deny | ||
305 | fi | ||
306 | fi | ||
307 | |||
308 | fi | ||
309 | |||
310 | if authorized "$git_dir"; then | ||
311 | if [ "$git_cmd" = 'git-receive-pack' ]; then | ||
312 | if ! ssh_user_owns_repository | ||
313 | then | ||
314 | export GIT_NAMESPACE="$SSH_CLIENT_FINGERPRINT" | ||
315 | maybe_initialize_heads | ||
316 | printf '%s:%s\n' 'd@cryptonomic.net' "git_namespace/$GIT_NAMESPACE/${git_dir#${HOME}/}" >&2 | ||
317 | fi | ||
318 | fi | ||
319 | exec "$git_cmd" "$git_dir" | ||
320 | else | ||
321 | $default_command | ||
322 | exit | ||
323 | # echo 'Error: git access is unauthorized' >&2; exit 1 # unreached | ||
324 | fi | ||
diff --git a/src/samizdat-ssh-uid b/src/samizdat-ssh-uid deleted file mode 100755 index 2b4b311..0000000 --- a/src/samizdat-ssh-uid +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | #!/bin/dash | ||
2 | |||
3 | die() { echo "$0: Error: $*" >&2; exit 1; } | ||
4 | |||
5 | [ "$SSH_USER_AUTH" ] || die "not defined: \$SSH_USER_AUTH" | ||
6 | [ -f "$SSH_USER_AUTH" ] || die "file does not exist: \$SSH_USER_AUTH=${SSH_USER_AUTH}" | ||
7 | |||
8 | PEMFILE="${SSH_USER_AUTH}.tmp" | ||
9 | |||
10 | sed -ne 's/^publickey //p' < "${SSH_USER_AUTH}" > "${PEMFILE}" || die "could not rewrite SSH_USER_AUTH file" | ||
11 | |||
12 | SSH_CLIENT_FINGERPRINT=$(ssh-keygen -r . -f "${PEMFILE}" | sed -ne 's/^. IN SSHFP [0-9]* 2 //p') && | ||
13 | [ "$SSH_CLIENT_FINGERPRINT" ] || die "could not determine ssh client fingerprint" | ||
14 | |||
15 | read keytype keydata < "${PEMFILE}" || die "reading from PEMFILE=$PEMFILE" | ||
16 | case "$keytype" in | ||
17 | ssh-rsa|ssh-dss|ecdsa-sha2-nistp256|ssh-ed25519) | ||
18 | domain=$keytype.cryptonomic.net ;; | ||
19 | *) | ||
20 | die "Unsupported key type: $keytype" ;; | ||
21 | esac | ||
22 | |||
23 | if [ "$1" = '--copy-pem' -a "$2" ] | ||
24 | then | ||
25 | if [ -d "$2" ] || mkdir "$2" | ||
26 | then | ||
27 | mv "${PEMFILE}" "$2"/${SSH_CLIENT_FINGERPRINT}.${keytype}.pem | ||
28 | fi | ||
29 | else | ||
30 | rm -f "${PEMFILE}" | ||
31 | fi | ||
32 | |||
33 | env -i \ | ||
34 | SSH_CLIENT_FINGERPRINT="$SSH_CLIENT_FINGERPRINT" \ | ||
35 | SSH_CLIENT_KEYTYPE="$keytype" \ | ||
36 | SSH_CLIENT_DOMAIN="$domain" \ | ||
37 | SSH_CLIENT_PEMFILE="$PEMFILE" \ | ||
38 | SSH_CLIENT_KEYDATA="$keydata" | ||
39 | |||