Blame


1 884d5b5c 2023-12-31 aa #!/usr/bin/perl
2 9f1a6925 2023-05-30 aa
3 884d5b5c 2023-12-31 aa use strict;
4 884d5b5c 2023-12-31 aa use warnings;
5 9f1a6925 2023-05-30 aa
6 884d5b5c 2023-12-31 aa use IPC::Open2;
7 884d5b5c 2023-12-31 aa use Getopt::Std;
8 884d5b5c 2023-12-31 aa use File::Basename;
9 884d5b5c 2023-12-31 aa use File::Path 'make_path';
10 9f1a6925 2023-05-30 aa
11 884d5b5c 2023-12-31 aa our ($opt_g, $opt_d, $opt_r);
12 884d5b5c 2023-12-31 aa my $gpg;
13 9f1a6925 2023-05-30 aa
14 884d5b5c 2023-12-31 aa # usage: print usage information to stderr and exit with error.
15 884d5b5c 2023-12-31 aa sub usage {
16 884d5b5c 2023-12-31 aa my $cmd = basename $0;
17 884d5b5c 2023-12-31 aa die
18 884d5b5c 2023-12-31 aa "usage: $cmd [-g command] [-d dir] add [-m] name\n" .
19 884d5b5c 2023-12-31 aa " $cmd [-d dir] rm name ...\n" .
20 884d5b5c 2023-12-31 aa " $cmd [-g command] [-d dir] show name\n" .
21 884d5b5c 2023-12-31 aa " $cmd [-d dir] mv from to\n" .
22 884d5b5c 2023-12-31 aa " $cmd [-d dir] ls\n";
23 884d5b5c 2023-12-31 aa }
24 9f1a6925 2023-05-30 aa
25 884d5b5c 2023-12-31 aa # getrecipient: return string to be used with gpg's -r option.
26 884d5b5c 2023-12-31 aa #
27 884d5b5c 2023-12-31 aa # Past versions of gpm (written in shell) required explicit recipient, set by
28 884d5b5c 2023-12-31 aa # option -r, or the GPM_RECIPIENT environment variable. This is completely
29 884d5b5c 2023-12-31 aa # unnecessary now, due to GPM_GPG and -g, but legacy syntax is still maintained.
30 884d5b5c 2023-12-31 aa sub getrecipient {
31 884d5b5c 2023-12-31 aa my $r = $opt_r;
32 884d5b5c 2023-12-31 aa defined $r or $r = $ENV{GPM_RECIPIENT};
33 884d5b5c 2023-12-31 aa return $r;
34 9f1a6925 2023-05-30 aa }
35 9f1a6925 2023-05-30 aa
36 884d5b5c 2023-12-31 aa # ckpath $path
37 884d5b5c 2023-12-31 aa #
38 884d5b5c 2023-12-31 aa # Return 1 if $path doesn't start with /, or contain .. files, and 0 otherwise.
39 884d5b5c 2023-12-31 aa sub ckpath {
40 884d5b5c 2023-12-31 aa my ($p) = @_;
41 884d5b5c 2023-12-31 aa return $p !~ m,^/, && $p ne ".." && $p !~ m,^\.\./, && $p !~ m,/\.\./,
42 884d5b5c 2023-12-31 aa && $p !~ m,/\.\.$,;
43 6201fa4d 2023-06-14 aa }
44 6201fa4d 2023-06-14 aa
45 884d5b5c 2023-12-31 aa # cklegacy $f
46 884d5b5c 2023-12-31 aa #
47 884d5b5c 2023-12-31 aa # Returns the correct path for $f (with or without the .gpg suffix), or dies
48 884d5b5c 2023-12-31 aa # if no such file exists.
49 884d5b5c 2023-12-31 aa sub cklegacy {
50 884d5b5c 2023-12-31 aa my ($f) = @_;
51 884d5b5c 2023-12-31 aa if (!-f $f) {
52 884d5b5c 2023-12-31 aa my $f2 = $f . ".gpg";
53 884d5b5c 2023-12-31 aa -f $f2 or die "neither $f, nor $f2 exist as regular files\n";
54 884d5b5c 2023-12-31 aa return $f2;
55 884d5b5c 2023-12-31 aa }
56 884d5b5c 2023-12-31 aa return $f;
57 9f1a6925 2023-05-30 aa }
58 9f1a6925 2023-05-30 aa
59 884d5b5c 2023-12-31 aa # prunetree $d: remove empty directories, starting from $d, and going up.
60 884d5b5c 2023-12-31 aa sub prunetree {
61 884d5b5c 2023-12-31 aa for (my ($d) = @_; $d ne "."; $d = dirname $d) {
62 884d5b5c 2023-12-31 aa rmdir $d or last;
63 884d5b5c 2023-12-31 aa }
64 9f1a6925 2023-05-30 aa }
65 9f1a6925 2023-05-30 aa
66 884d5b5c 2023-12-31 aa # add
67 884d5b5c 2023-12-31 aa #
68 884d5b5c 2023-12-31 aa # Encrypt the secret from stdin, and store the ciphertext in file specified
69 884d5b5c 2023-12-31 aa # on the command line.
70 884d5b5c 2023-12-31 aa sub add {
71 884d5b5c 2023-12-31 aa our $opt_m;
72 884d5b5c 2023-12-31 aa my $r = getrecipient;
73 884d5b5c 2023-12-31 aa my ($cmd, $sec);
74 9f1a6925 2023-05-30 aa
75 884d5b5c 2023-12-31 aa $cmd .= "$gpg -e";
76 884d5b5c 2023-12-31 aa $cmd .= " -r $r" if defined $r;
77 9f1a6925 2023-05-30 aa
78 884d5b5c 2023-12-31 aa getopts('m') or usage;
79 884d5b5c 2023-12-31 aa $#ARGV >= 0 or usage;
80 9f1a6925 2023-05-30 aa
81 884d5b5c 2023-12-31 aa my $outfile = $ARGV[0];
82 884d5b5c 2023-12-31 aa ckpath $outfile or die "bad path: $outfile\n";
83 9f1a6925 2023-05-30 aa
84 884d5b5c 2023-12-31 aa -e $outfile and die "$outfile already exists\n";
85 9f1a6925 2023-05-30 aa
86 884d5b5c 2023-12-31 aa if (-t STDIN && !$opt_m) {
87 884d5b5c 2023-12-31 aa system "stty -echo";
88 9f1a6925 2023-05-30 aa
89 884d5b5c 2023-12-31 aa print "Secret:";
90 884d5b5c 2023-12-31 aa $sec = <STDIN>;
91 884d5b5c 2023-12-31 aa print "\n";
92 884d5b5c 2023-12-31 aa print "Repeat:";
93 884d5b5c 2023-12-31 aa my $sec2 = <STDIN>;
94 884d5b5c 2023-12-31 aa print "\n";
95 884d5b5c 2023-12-31 aa die "Sorry\n" if $sec ne $sec2;
96 9f1a6925 2023-05-30 aa
97 884d5b5c 2023-12-31 aa system "stty echo";
98 884d5b5c 2023-12-31 aa } else { while (<STDIN>) { $sec .= $_; } }
99 884d5b5c 2023-12-31 aa
100 884d5b5c 2023-12-31 aa my $pid = open2(my $reader, my $writer, $cmd);
101 884d5b5c 2023-12-31 aa print $writer $sec;
102 884d5b5c 2023-12-31 aa close($writer);
103 884d5b5c 2023-12-31 aa my $out;
104 884d5b5c 2023-12-31 aa while (<$reader>) { $out .= $_; }
105 884d5b5c 2023-12-31 aa waitpid $pid, 0;
106 884d5b5c 2023-12-31 aa $? == 0 or exit 1;
107 884d5b5c 2023-12-31 aa
108 884d5b5c 2023-12-31 aa my $d = dirname $outfile;
109 884d5b5c 2023-12-31 aa make_path($d, {mode => 0700});
110 884d5b5c 2023-12-31 aa umask 0377;
111 884d5b5c 2023-12-31 aa unless (open FH, ">$outfile") {
112 884d5b5c 2023-12-31 aa prunetree($d);
113 884d5b5c 2023-12-31 aa die "couldn't open $outfile for writing: $!\n";
114 884d5b5c 2023-12-31 aa }
115 884d5b5c 2023-12-31 aa unless (print FH $out) {
116 884d5b5c 2023-12-31 aa prunetree($d);
117 884d5b5c 2023-12-31 aa die "couldn't write to $outfile: $!\n";
118 884d5b5c 2023-12-31 aa }
119 9f1a6925 2023-05-30 aa }
120 9f1a6925 2023-05-30 aa
121 884d5b5c 2023-12-31 aa # ls
122 884d5b5c 2023-12-31 aa #
123 884d5b5c 2023-12-31 aa # Produce a listing of files in the gpm directory, using the GPM_LSCMD
124 884d5b5c 2023-12-31 aa # environment variable if defined.
125 884d5b5c 2023-12-31 aa sub ls {
126 884d5b5c 2023-12-31 aa my $cmd = $ENV{GPM_LSCMD};
127 884d5b5c 2023-12-31 aa unless (defined $cmd) {
128 884d5b5c 2023-12-31 aa opendir my $dh, "." or die "couldn't open directory .: $!\n";
129 884d5b5c 2023-12-31 aa while (readdir $dh) {
130 884d5b5c 2023-12-31 aa if ($_ ne "." && $_ ne ".." && -d $_) {
131 884d5b5c 2023-12-31 aa $cmd = "find . -type f | sed 's,^\./,,'";
132 884d5b5c 2023-12-31 aa last;
133 884d5b5c 2023-12-31 aa }
134 884d5b5c 2023-12-31 aa }
135 884d5b5c 2023-12-31 aa $cmd = "ls" unless defined $cmd;
136 884d5b5c 2023-12-31 aa }
137 884d5b5c 2023-12-31 aa system($cmd);
138 884d5b5c 2023-12-31 aa $? == 0 or exit 1;
139 9f1a6925 2023-05-30 aa }
140 9f1a6925 2023-05-30 aa
141 884d5b5c 2023-12-31 aa # mv: safely rename $ARGV[0] to $ARGV[1].
142 884d5b5c 2023-12-31 aa sub mv {
143 884d5b5c 2023-12-31 aa $#ARGV >= 1 || usage;
144 884d5b5c 2023-12-31 aa my ($from, $to) = @ARGV;
145 884d5b5c 2023-12-31 aa ckpath $from or die "bad path $from\n";
146 884d5b5c 2023-12-31 aa ckpath $to or die "bad path $to\n";
147 884d5b5c 2023-12-31 aa -e $to and die "$to already existst\n";
148 884d5b5c 2023-12-31 aa $from = cklegacy $from;
149 884d5b5c 2023-12-31 aa
150 884d5b5c 2023-12-31 aa make_path(dirname $to, {mode => 0700});
151 884d5b5c 2023-12-31 aa rename $from, $to;
152 884d5b5c 2023-12-31 aa prunetree(dirname $from);
153 9f1a6925 2023-05-30 aa }
154 9f1a6925 2023-05-30 aa
155 884d5b5c 2023-12-31 aa # rm: unlink arguments, asking each time.
156 884d5b5c 2023-12-31 aa sub rm {
157 884d5b5c 2023-12-31 aa $#ARGV >= 0 || usage;
158 884d5b5c 2023-12-31 aa for (@ARGV) {
159 884d5b5c 2023-12-31 aa my $f = $_;
160 884d5b5c 2023-12-31 aa unless (ckpath $f) {
161 884d5b5c 2023-12-31 aa print STDERR "bad path $f\n";
162 884d5b5c 2023-12-31 aa next;
163 884d5b5c 2023-12-31 aa }
164 884d5b5c 2023-12-31 aa $f = cklegacy $f;
165 884d5b5c 2023-12-31 aa print "Really remove $f? ";
166 884d5b5c 2023-12-31 aa <STDIN> =~ m/^[Yy]/ and unlink $f;
167 884d5b5c 2023-12-31 aa prunetree(dirname $f);
168 884d5b5c 2023-12-31 aa }
169 884d5b5c 2023-12-31 aa }
170 9f1a6925 2023-05-30 aa
171 884d5b5c 2023-12-31 aa # show: decrypt file, and print plaintext to stdout.
172 884d5b5c 2023-12-31 aa sub show {
173 884d5b5c 2023-12-31 aa $#ARGV >= 0 or usage;
174 884d5b5c 2023-12-31 aa my $file = $ARGV[0];
175 884d5b5c 2023-12-31 aa ckpath $file or die "bad path $file\n";
176 884d5b5c 2023-12-31 aa $file = cklegacy $file;
177 884d5b5c 2023-12-31 aa
178 884d5b5c 2023-12-31 aa system("$gpg -d $file");
179 884d5b5c 2023-12-31 aa $? == 0 or exit 1;
180 884d5b5c 2023-12-31 aa }
181 884d5b5c 2023-12-31 aa
182 884d5b5c 2023-12-31 aa getopts('g:d:r') or usage;
183 884d5b5c 2023-12-31 aa
184 884d5b5c 2023-12-31 aa $#ARGV >= 0 or usage;
185 884d5b5c 2023-12-31 aa my $cmd = $ARGV[0];
186 884d5b5c 2023-12-31 aa shift @ARGV;
187 884d5b5c 2023-12-31 aa
188 884d5b5c 2023-12-31 aa $gpg = $opt_g;
189 884d5b5c 2023-12-31 aa $gpg = $ENV{GPM_GPG} unless defined $gpg;
190 884d5b5c 2023-12-31 aa $gpg = "gpg" unless defined $gpg;
191 884d5b5c 2023-12-31 aa
192 884d5b5c 2023-12-31 aa my $gpmd = $opt_d;
193 884d5b5c 2023-12-31 aa defined $gpmd or $gpmd = $ENV{GPM_DIR};
194 884d5b5c 2023-12-31 aa unless (defined $gpmd) {
195 884d5b5c 2023-12-31 aa if (defined $ENV{XDG_DATA_HOME}) {
196 884d5b5c 2023-12-31 aa $gpmd = $ENV{XDG_DATA_HOME} . "/gpm";
197 884d5b5c 2023-12-31 aa } elsif (defined $ENV{HOME}) {
198 884d5b5c 2023-12-31 aa $gpmd = $ENV{HOME} . "/.gpm";
199 884d5b5c 2023-12-31 aa } else {
200 884d5b5c 2023-12-31 aa die "couldn't determine the gpm directory\n";
201 884d5b5c 2023-12-31 aa }
202 884d5b5c 2023-12-31 aa }
203 884d5b5c 2023-12-31 aa
204 884d5b5c 2023-12-31 aa make_path($gpmd, {mode => 0700});
205 884d5b5c 2023-12-31 aa chdir $gpmd or die "couldn't change directory to $gpmd: $!\n";
206 884d5b5c 2023-12-31 aa
207 884d5b5c 2023-12-31 aa for ($cmd) {
208 884d5b5c 2023-12-31 aa if (/^a/) { add; last; }
209 884d5b5c 2023-12-31 aa if (/^l/) { ls; last; }
210 884d5b5c 2023-12-31 aa if (/^m/) { mv; last; }
211 884d5b5c 2023-12-31 aa if (/^r/) { rm; last; }
212 884d5b5c 2023-12-31 aa if (/^s/) { show; last; }
213 884d5b5c 2023-12-31 aa usage;
214 884d5b5c 2023-12-31 aa }