-
Notifications
You must be signed in to change notification settings - Fork 1
/
passprint
executable file
·235 lines (211 loc) · 7.06 KB
/
passprint
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/usr/bin/env bash
#
# NAME
#
# passprint - convert a password store managed by pass into
# a printable PDF.
#
# SYNOPSIS
#
# passprint [-au] [<output>.pdf]
# passprint -o
#
# DESCRIPTION
#
# passprint will convert a password store managed by pass [1] into
# a printable [2] PDF. All passwords in your password store are
# expected to have a prefix string that will match a fixed regular
# expression. This way, one can strip out the prefix string before
# printing the passwords on a paper, thus adding an additional layer
# of security. Passwords not having a recognized prefix string will
# not be printed. In addition to this, every line in a password entry
# beginning with a pipe '|' will also be printed. This feature can be
# used to print additional information like username, email, etc.
#
# CONFIGURATION
#
# To use passprint, first create a gpg2-encrypted configuration file
# '.passprint.conf.gpg' inside the $PASSWORD_STORE_DIR (by default
# ~/.password-store). This configuration file should be a shell
# script that can be 'evaled' in Bash, and _must_ contain the variable
# $PASSPRINT_PREFIX, which is an extended regular expression that will
# match the prefix string of the passwords. For example, if your
# passwords always begin with 'hello' or 'world', you should add:
#
# PASSPRINT_PREFIX="^(hello|world)"
#
# The configuration file should also ideally have the
# $PASSPRINT_EXCLUDE Bash array containing a list of entries that you
# want to avoid printing. For example,
#
# PASSPRINT_EXCLUDE=("*/other/*" "*reddit*")
#
# The contents of this array should be glob patterns that can be
# passed to the -path option of find(1).
#
# DEPENDENCIES
#
# pdflatex(1), md5sum(1), mktemp(1), and pass(1)
#
# [1]: https://www.passwordstore.org
# [2]: https://www.schneier.com/blog/archives/2005/06/write_down_your.html
#
: "${PASSWORD_STORE_DIR:=${HOME}/.password-store}"
: "${PASSPRINT_CONF:=${PASSWORD_STORE_DIR}/.passprint.conf.gpg}"
prefix_check=1
print_all=0
print_totp=0
if command -v gpg2 &>/dev/null
then
# If there's a executable called gpg2, it probably means that there
# are two versions of GPG; we need GPG v2.
GPG="gpg2"
else
GPG="gpg"
fi
if [[ -f "$PASSPRINT_CONF" ]]
then
eval "$("$GPG" --batch \
--decrypt \
--quiet \
--use-agent \
--yes "$PASSPRINT_CONF" 2>/dev/null)" &>/dev/null
else
echo >&2 "${0##*/}: '${PASSPRINT_CONF}' not found."
exit 1
fi
# By default refuse to print all entries.
: "${PASSPRINT_PREFIX:=^$}"
# By default exclude all entries.
: "${PASSPRINT_EXCLUDE:=*}"
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir" >/dev/null 2>&1' EXIT
trap 'exit 2' HUP INT QUIT TERM
# Arguments for the find command that will be run inside
# $PASSWORD_STORE_DIR.
find_args=( -type f -name '*.gpg' -not -name "${PASSPRINT_CONF##*/}" )
for entry in "${PASSPRINT_EXCLUDE[@]}"
do
find_args+=( "!" "-path" "$entry" )
done
mklatex() {
CPATH= pushd "$PASSWORD_STORE_DIR" &>/dev/null
cat <<EOF
\documentclass[twocolumn,a4paper,10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[margin=0.25in]{geometry}
\usepackage{fancyvrb,fvextra}
\begin{document}\small
\begin{Verbatim}[
breakanywhere,
breaklines,
breaksymbolindentleftnchars=2,
breaksymbolindentrightnchars=2,
breaksymbolsepleftnchars=1,
breaksymbolseprightnchars=1
]
EOF
while IFS= read -r path
do
local entry=$(echo "$path" | sed 's|^\./||;s|.gpg$||')
"$GPG" --batch --decrypt --quiet --use-agent --yes "$path" 2>/dev/null |
awk -v entry="$entry" \
-v prefix="$PASSPRINT_PREFIX" \
-v prefix_check="$prefix_check" \
-v print_all="$print_all" '
BEGIN {
valid = 1
}
function warn(string) {
printf("passprint: %s\n", string) >"/dev/stderr"
}
(NR == 1) {
if ($0 !~ prefix && prefix_check) {
warn(sprintf("password for \"%s\" does not have a valid prefix.", entry))
warn(sprintf("refusing to print \"%s\".", entry))
valid = 0
exit
} else {
if (prefix_check == 1)
sub(prefix, "")
sub("[[:space:]]+$", "")
printf("%s\n", entry)
printf(" %s\n", $0)
}
}
{
if (($0 ~ /(^\||.*otpauth:.*)/ || print_all) && NR != 1) {
sub("^\\|[[:space:]]{,2}", "")
if (print_all)
sub("[[:space:]]{,2}", "")
sub("[[:space:]]+$", "")
printf(" %s\n", $0)
}
}
END {
if (valid) {
printf("\n")
printf("% --------------------------------------------------------")
printf("\n")
}
}
'
done < <(find "${find_args[@]}" | sort)
# Use latest commit date or LaTeX's \today for the timestamp.
local date=$(git log -1 --format='%cd [%h]' 2>/dev/null || echo "\today")
# A random string to uniquely `tag' PDF files.
local randstr=$(head /dev/urandom | md5sum | cut -d ' ' -f 1)
cat <<EOF
** $randstr
** $date
\end{Verbatim}
\end{document}
EOF
popd &>/dev/null
}
mkpdf() {
# For reasons I don't understand (nor have the patience to), piping
# to pdflatex doesn't work well.
mklatex >"${tmpdir}/texput.tex"
if xelatex -interaction=nonstopmode \
-output-directory "$tmpdir" \
"${tmpdir}/texput.tex" &>/dev/null
then
mv "${tmpdir}/texput.pdf" -- "$1" && echo >&2 "${0##*/}: output written to '${1}'"
else
echo >&2 "${0##*/}: error compiling LaTeX source"
cat "${tmpdir}/texput.log"
exit 1
fi
}
totp_codes() {
CPATH= pushd "$PASSWORD_STORE_DIR" &>/dev/null
while IFS= read -r path
do
local entry=$(echo "$path" | sed 's|^\./||;s|.gpg$||')
"$GPG" --batch --decrypt --quiet --use-agent --yes "$path" 2>/dev/null | grep -s 'otpauth:\/\/'
done < <(find "${find_args[@]}" | sort)
popd &>/dev/null
}
while getopts ":aou" opt
do
case "$opt" in
a) print_all=1 ;;
o) print_totp=1 ;;
u) prefix_check=0 ;;
\?) echo >&2 "${0##*/}: -$OPTARG is not a valid option"
exit 1 ;;
esac
done
shift $(( OPTIND - 1 ))
if [[ "$print_totp" == 1 ]]
then
echo >&2 "${0##*/}: print TOTP codes to stdout"
totp_codes
else
[[ -n "$*" ]] || set -- "$(date +%Y-%m-%d).pdf"
[[ "$print_all" == 1 ]] && echo >&2 "${0##*/}: WARNING: printing everything!"
[[ "$prefix_check" == 0 ]] && echo >&2 "${0##*/}: WARNING: prefix checking is off; unsafe!"
mkpdf "$1"
fi