5 use lib
$ENV{GL_LIBDIR
};
10 Usage for this command is not that simple. Please read the full documentation
11 in doc/sskm.mkd or online at http://gitolite.com/gitolite/contrib/sskm.html.
14 usage
() if @ARGV and $ARGV[0] eq '-h';
16 my $rb = $rc{GL_REPO_BASE
};
17 my $ab = $rc{GL_ADMIN_BASE
};
21 # save arguments for later
22 my $operation = shift || 'list';
23 my $keyid = shift || '';
24 # keyid must fit a very specific pattern
25 $keyid and $keyid !~ /^@[-0-9a-z_]+$/i and die "invalid keyid $keyid\n";
27 # get the actual userid and keytype
28 my $gl_user = $ENV{GL_USER
};
30 $keytype = $1 if $gl_user =~ s/^zzz-marked-for-(...)-//;
31 print STDERR
"hello $gl_user, you are currently using "
34 ?
"a key in the 'marked for $keytype' state\n"
35 : "a normal (\"active\") key\n"
39 # first collect the keys
41 my ( @pubkeys, @marked_for_add, @marked_for_del );
42 # get the list of pubkey files for this user, including pubkeys marked for
45 for my $pubkey (`find . -type f -name "*.pub" | sort`) {
47 $pubkey =~ s
(^./)(); # artifact of the find command
50 $user =~ s
(.*/)(); # foo/bar
/baz
.pub
-> baz
.pub
51 $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz
53 next unless $user eq $gl_user or $user =~ /^zzz-marked-for-...-$gl_user/;
55 if ( $user =~ m
(^zzz
-marked
-for-add
-) ) {
56 push @marked_for_add, $pubkey;
57 } elsif ( $user =~ m
(^zzz
-marked
-for-del
-) ) {
58 push @marked_for_del, $pubkey;
60 push @pubkeys, $pubkey;
65 # list mode; just do it and exit
67 my ( $message, @list ) = @_;
69 print "== $message ==\n";
72 my $fp = fingerprint
($_);
73 s/zzz-marked(\/|-for-...-)//g
;
74 print $count++ . ": $fp : $_\n";
77 if ( $operation eq 'list' ) {
78 print "you have the following keys:\n";
79 print_keylist
( "active keys", @pubkeys );
80 print_keylist
( "keys marked for addition/replacement", @marked_for_add );
81 print_keylist
( "keys marked for deletion", @marked_for_del );
87 # please see docs for details on how a user interacts with this
89 if ( $keytype eq '' ) {
90 # user logging in with a normal key
91 die "valid operations: add, del, undo-add, confirm-del\n" unless $operation =~ /^(add|del|confirm-del|undo-add)$/;
92 if ( $operation eq 'add' ) {
93 print STDERR
"please supply the new key on STDIN. (I recommend you
94 don't try to do this interactively, but use a pipe)\n";
95 kf_add
( $gl_user, $keyid, safe_stdin
() );
96 } elsif ( $operation eq 'del' ) {
97 kf_del
( $gl_user, $keyid );
98 } elsif ( $operation eq 'confirm-del' ) {
99 die "you dont have any keys marked for deletion\n" unless @marked_for_del;
100 kf_confirm_del
( $gl_user, $keyid );
101 } elsif ( $operation eq 'undo-add' ) {
102 die "you dont have any keys marked for addition\n" unless @marked_for_add;
103 kf_undo_add
( $gl_user, $keyid );
105 } elsif ( $keytype eq 'del' ) {
106 # user is using a key that was marked for deletion. The only possible use
107 # for this is that she changed her mind for some reason (maybe she marked
108 # the wrong key for deletion) or is not able to get her client-side sshd
109 # to stop using this key
110 die "valid operations: undo-del\n" unless $operation eq 'undo-del';
113 kf_undo_del
( $gl_user, $keyid );
114 } elsif ( $keytype eq 'add' ) {
115 die "valid operations: confirm-add\n" unless $operation eq 'confirm-add';
116 # user is trying to validate a key that has been previously marked for
117 # addition. This isn't interactive, but it *could* be... if someone asked
118 kf_confirm_add
( $gl_user, $keyid );
125 # make a temp clone and switch to it
127 BEGIN { $TEMPDIR = `mktemp -d -t tmp.XXXXXXXXXX`; }
128 END { `/bin/rm -rf $TEMPDIR`; }
132 hushed_git
( "clone", "$rb/gitolite-admin.git", "$TEMPDIR" );
134 my $hostname = `hostname`; chomp($hostname);
135 hushed_git
( "config", "--get", "user.email" ) and hushed_git
( "config", "user.email", $ENV{USER
} . "@" . $hostname );
136 hushed_git
( "config", "--get", "user.name" ) and hushed_git
( "config", "user.name", "$ENV{USER} on $hostname" );
140 my ($fp, $output) = ssh_fingerprint_file
(shift);
141 # Do not print the output of $output to an untrusted destination.
142 die "does not seem to be a valid pubkey\n" unless $fp;
147 # read one line from STDIN
149 my $ret = read STDIN
, $data, 4096;
150 # current pubkeys are approx 400 bytes so we go a little overboard
151 die "could not read pubkey data" . ( defined($ret) ?
"" : ": $!" ) . "\n" unless $ret;
152 die "pubkey data seems to have more than one line\n" if $data =~ /\n./;
157 local (*STDOUT
) = \
*STDOUT
;
158 local (*STDERR
) = \
*STDERR
;
159 open( STDOUT
, ">", "/dev/null" );
160 open( STDERR
, ">", "/dev/null" );
165 # there can be only one
166 my ( $keyid, $die_if_empty, @a ) = @_;
170 more than one key satisfies this condition, and I can't deal with that!
174 print STDERR
"\t" . join( "\n\t", @a ), "\n\n";
178 die "no keys with " . ( $keyid || "empty" ) . " keyid found\n" if $die_if_empty and not @a;
184 my ( $gl_user, $keyid, $keymaterial ) = @_;
186 # add a new "marked for addition" key for $gl_user.
191 _print
( "zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub", $keymaterial );
192 hushed_git
( "add", "." ) and die "git add failed\n";
193 my $fp = fingerprint
("zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub");
194 hushed_git
( "commit", "-m", "sskm: add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
195 system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
199 my ( $gl_user, $keyid ) = @_;
200 # find entries in both @pubkeys and @marked_for_add whose basename matches $gl_user$keyid
201 my @pk = highlander
( $keyid, 0, grep { m
(^(.*/)?
$gl_user$keyid.pub
$) } @pubkeys );
202 my @mfa = highlander
( $keyid, 1, grep { m
(^zzz
-marked
/zzz
-marked
-for-add
-$gl_user$keyid.pub
$) } @marked_for_add );
207 my $fp = fingerprint
( $mfa[0] );
209 hushed_git
( "mv", "-f", $mfa[0], $pk[0] );
210 hushed_git
( "commit", "-m", "sskm: confirm-add (replace) $pk[0] ($fp)" ) and die "git commit failed\n";
212 hushed_git
( "mv", "-f", $mfa[0], "$gl_user$keyid.pub" );
213 hushed_git
( "commit", "-m", "sskm: confirm-add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
215 system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
219 # XXX some code at start is shared with kf_confirm_add
220 my ( $gl_user, $keyid ) = @_;
221 my @mfa = highlander
( $keyid, 1, grep { m
(^zzz
-marked
/zzz
-marked
-for-add
-$gl_user$keyid.pub
$) } @marked_for_add );
226 my $fp = fingerprint
( $mfa[0] );
227 hushed_git
( "rm", $mfa[0] );
228 hushed_git
( "commit", "-m", "sskm: undo-add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
229 system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
233 my ( $gl_user, $keyid ) = @_;
239 my @pk = highlander
( $keyid, 1, grep { m
(^(.*/)?
$gl_user$keyid.pub
$) } @pubkeys );
241 my $fp = fingerprint
( $pk[0] );
242 hushed_git
( "mv", $pk[0], "zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub" ) and die "git mv failed\n";
243 hushed_git
( "commit", "-m", "sskm: del $pk[0] ($fp)" ) and die "git commit failed\n";
244 system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
248 my ( $gl_user, $keyid ) = @_;
249 my @mfd = highlander
( $keyid, 1, grep { m
(^zzz
-marked
/zzz
-marked
-for-del
-$gl_user$keyid.pub
$) } @marked_for_del );
254 my $fp = fingerprint
( $mfd[0] );
255 hushed_git
( "rm", $mfd[0] );
256 hushed_git
( "commit", "-m", "sskm: confirm-del $gl_user$keyid ($fp)" ) and die "git commit failed\n";
257 system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
261 my ( $gl_user, $keyid ) = @_;
263 my @mfd = highlander
( $keyid, 1, grep { m
(^zzz
-marked
/zzz
-marked
-for-del
-$gl_user$keyid.pub
$) } @marked_for_del );
266 You're undeleting a key that is currently marked for deletion.
267 Hit ENTER to undelete this key
268 Hit Ctrl-C to cancel the undelete
269 Please see documentation for caveats on the undelete process as well as how to
272 <>; # yeay... always wanted to do that -- throw away user input!
277 my $fp = fingerprint
( $mfd[0] );
278 hushed_git
( "mv", "-f", $mfd[0], "$gl_user$keyid.pub" );
279 hushed_git
( "commit", "-m", "sskm: undo-del $gl_user$keyid ($fp)" ) and die "git commit failed\n";
280 system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";