aboutsummarylogtreecommitdiffstats
path: root/doasedit
blob: 85550ab1ad6994970050e32535a9b76d0aa032a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/bin/bash

# This script is not provided with the assumption that it is absolutely secure - instead, its primary function is to give users the convenience of using their configuration for their editor while editing root-owned files
# Certain situations where security issues might arise, such as when copying a non-world-readable file to /tmp, are disallowed for this reason, but I can make no promises about the security of using this software
# If, on the other hand, you have a suggestion to improve the security of the script, please contact me at magnus@iastate.edu or on matrix @magnustesshu:matrix.org and tell me how to improve it

# Since I am lazy and am using this to write small scripts under /usr/local/bin sometimes, I also added a feature for myself to install with permissions already set if you are writing a new file and start it with a shebang.

EDITOR=${EDITOR:-vi}
doasediting_file="/tmp/doasediting"

ERR() {
    echo -e 'doasedit: '"$1";
    exit 1
}

# Set shell options to fail if any variable expansion or final expression in a pipeline fails
set -eu

[[ $# != "1" ]] && ERR 'usage:\n\tdoasedit /file/owned/by/root\n\tdoasedit /new/file\t(starting a new file with "#!" will make it executable)' # Argument was passed
# Ensure this script is run as a user who has doas priviledges, editing a regular or nonexistent file
[[ "$(id -u)" == "0" ]] && ERR "Do not run doasedit as root!"
/sbin/doas -C /etc/doas.conf doasedit \"$1\" >/dev/null || ERR "You are not a doer" # To allow doasedit, you must be allowed to run `doas doasedit`
[[ -d "$1" ]] && ERR "'$1' is a directory" # File is a directory
# Ensure the file is in a directory we can read
[[ -d "`dirname $1`" ]] || ERR "'$1' is not in a directory we can read"
[[ -r "`dirname $1`" ]] || ERR "'$1' is a directory you do not have permission to read"

# In the positive case, the file exists, and we check that anyone can read it and that it is owned by root. In the negative case, the file does not exist
if [[ -e "$1" ]] ; then
    fileperms=$(stat -L -c "%a" "$1")
    anyreadperm=${fileperms:3:1} # With sticky bit
    [[ ${#fileperms} == "3" ]] && anyreadperm=${fileperms:2:1} # without sticky bit
    [[ $anyreadperm -ge "4" ]] || ERR "file '$1' is not world-readable (since security is hard, this is a sacrifice doasedit makes for ease of implementation)"
    [[ -r "$1" ]] || ERR "file '$1' is world-readable but is not readable by your user, what are you doing?"
    [[ "$(stat -L -c "%u" "$1")" == "0" ]] || ERR "Can only edit root-owned files with this script, as copying the file back will make root the owner"

    # Preserves file permissions, amazingly
    cp "$1" $doasediting_file || ERR "Cannot copy to $doasediting_file"
else
    # Default permissions are 644
    rm -f $doasediting_file || ERR "Cannot remove $doasediting_file"
    touch $doasediting_file || ERR "Cannot touch $doasediting_file"
fi

$EDITOR $doasediting_file || ERR "The editor failed to exit successfully"

# If the original file existed, don't update it if we did not make any modification to the temporary copy
[[ -e "$1" ]] && cmp -s $doasediting_file "$1" && exit 0
# If the original file did not exist, print a warning
[[ -e "$1" ]] || echo "doasedit: File '$1' will be created, if this is not desireable press ctrl+C"

[[ "/etc/doas.conf" == "$1" ]] && echo "$ doas -C $doasediting_file doasedit \"$1\"" && doas -C $doasediting_file doasedit \"$1\" || ERR "Replacing '/etc/doas.conf' would mean you no longer have permissions to edit it any further.\nIf you do not know what you're doing, you had a syntax error, so see the above output and try again."

doas cp $doasediting_file "$1" || ERR "You put your password in wrong. Manually run 'doas cp $doasediting_file $1'"

# Change permissions if the file was empty and starts with a shebang
[[ $fileperms != "" ]] || [[ `head -c 2 $doasediting_file` != "#!" ]] || { echo "doasedit: automatically running \`doas chmod +x '$1'\`" && doas chmod +x "$1" ; } || ERR "Cannot change permissions of '$1'"