From ef4d609914e578993928b7f905bf875ffd8dfac3 Mon Sep 17 00:00:00 2001
From: Jan Krueger
Date: Wed, 14 May 2008 15:45:11 +0200
Subject: [PATCH] Add SSH key changing interface.
Add SSH key changing interface.
Motivated by the recent OpenSSL vulnerability in Debian, add a user-accessible
feature to change public SSH keys associated with an account. Authentication
happens by sending the user an authorization code by e-mail (which is stored
on the server side as a special comment in the user's sshkey file).
Signed-off-by: Jan Krueger
---
cgi/Git/RepoCGI.pm | 75 ++++++++++++++++++++++++++++++-
cgi/edituser.cgi | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++
cgi/reguser.cgi | 5 +--
indextext.html | 2 +-
4 files changed, 207 insertions(+), 5 deletions(-)
create mode 100755 cgi/edituser.cgi
diff --git a/cgi/Git/RepoCGI.pm b/cgi/Git/RepoCGI.pm
index 2d8cb94..4f77cd5 100644
--- a/cgi/Git/RepoCGI.pm
+++ b/cgi/Git/RepoCGI.pm
@@ -3,7 +3,6 @@ package Git::RepoCGI;
use strict;
use warnings;
-
### Administrativa
BEGIN {
@@ -18,6 +17,7 @@ BEGIN {
use CGI qw(:standard :escapeHTML -nosticky);
use CGI::Util qw(unescape);
use CGI::Carp qw(fatalsToBrowser);
+ use Digest::SHA1 qw(sha1_hex);
$ENV{PATH} = '/home/pasky/bin:'.$ENV{PATH};
}
@@ -608,9 +608,32 @@ sub _sshkey_path {
'/etc/sshkeys/'.$self->{name};
}
+sub _sshkey_load {
+ my $self = shift;
+ open F, "<".jailed_file($self->_sshkey_path) or die "sshkey load failed: $!";
+ my @keys;
+ my $auth;
+ while () {
+ chomp;
+ if (/^ssh-(?:dss|rsa) /) {
+ push @keys, $_;
+ } elsif (/^# REPOAUTH ([0-9a-f]+) (\d+)/) {
+ my $expire = $2;
+ $auth = $1 unless (time >= $expire);
+ }
+ }
+ close F;
+ my $keys = join('', @keys); chomp $keys;
+ ($keys, $auth);
+}
+
sub _sshkey_save {
my $self = shift;
open F, ">".jailed_file($self->_sshkey_path) or die "sshkey failed: $!";
+ if (defined($self->{auth}) && $self->{auth}) {
+ my $expire = time + 24 * 3600;
+ print F "# REPOAUTH $self->{auth} $expire\n";
+ }
print F $self->{keys}."\n";
close F;
chmod 0664, jailed_file($self->_sshkey_path);
@@ -636,6 +659,28 @@ sub ghost {
$self;
}
+# public constructor #1
+sub load {
+ my $class = shift;
+ my ($name) = @_;
+
+ open F, jailed_file("/etc/passwd") or die "user load failed: $!";
+ while () {
+ chomp;
+ @_ = split /:+/;
+ next unless (shift eq $name);
+
+ my $self = $class->_new($name);
+
+ (undef, $self->{uid}, undef, $self->{email}) = @_;
+ ($self->{keys}, $self->{auth}) = $self->_sshkey_load;
+
+ return $self;
+ }
+ close F;
+ undef;
+}
+
# $user may not be in sane state if this returns false!
sub cgi_fill {
my $self = shift;
@@ -650,6 +695,14 @@ sub cgi_fill {
valid_email($self->{email})
or $repo->err("Your email sure looks weird...?");
+ $self->keys_fill($repo);
+}
+
+sub keys_fill {
+ my $self = shift;
+ my ($repo) = @_;
+ my $cgi = $repo->cgi;
+
$self->{keys} = $cgi->param('keys');
length($self->{keys}) <= 4096
or $repo->err("The list of keys is more than 4kb. Do you really need that much?");
@@ -657,6 +710,26 @@ sub cgi_fill {
not $repo->err_check;
}
+sub keys_save {
+ my $self = shift;
+
+ $self->_sshkey_save;
+}
+
+sub gen_auth {
+ my $self = shift;
+
+ $self->{auth} = Digest::SHA1::sha1_hex(time . $$ . rand() . $self->{keys});
+ $self->_sshkey_save;
+ $self->{auth};
+}
+
+sub del_auth {
+ my $self = shift;
+
+ delete $self->{auth};
+}
+
sub conjure {
my $self = shift;
diff --git a/cgi/edituser.cgi b/cgi/edituser.cgi
new file mode 100755
index 0000000..45ab0bf
--- /dev/null
+++ b/cgi/edituser.cgi
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+# (c) Petr Baudis
+# (c) Jan Krueger
+# GPLv2
+
+use strict;
+use warnings;
+
+use lib qw(/home/repo/repomgr/cgi);
+use Git::RepoCGI;
+
+my $repo = Git::RepoCGI->new('User SSH Key Update');
+my $cgi = $repo->cgi;
+
+if ($cgi->param('mail')) {
+ print "
Go away, bot.
";
+ exit;
+}
+
+sub _auth_form {
+ my $name = shift;
+ my $submit = shift;
+ my $fields = shift;
+ $fields = '' if (!$fields);
+ my $auth = shift;
+ my $authtag = ($auth ? qq() :
+ qq(
Authorization code:
));
+ print <
+
+$authtag
+$fields
+EOT
+}
+
+if ($cgi->param('name')) {
+ # submitted, let's see
+ # FIXME: racy, do a lock
+ my $name = $repo->wparam('name');
+ !valid_user_name($name)
+ or !Git::RepoCGI::User::does_exist($name)
+ and $repo->err("Username is not registered.");
+
+ $repo->err_check and exit;
+
+ my $user = Git::RepoCGI::User->load($name) or
+ die "Failed loading user but this can't really happen here";
+
+ if (!$cgi->param('auth')) {
+ my $auth = $user->gen_auth;
+
+ # Send auth mail
+ open(MAIL, '|-', '/usr/bin/mail', '-s', '[repo.or.cz] Account update authorization', $user->{email}) or
+ die "Sorry, could not send authorization code: $!";
+ print MAIL <
+EOT
+ close MAIL;
+
+ print "
You should shortly receive an e-mail containing an authorization code. Please enter this code below to update your SSH keys.
+The code will expire in an hour or after you have used it.
";
+ _auth_form($name, "'Login'");
+ exit;
+ } else {
+ $user->{auth} or
+ die("There currently isn't any authorization code filed under your account. Please generate one.");
+
+ my $fields = '';
+ my $keys = $cgi->param('keys') || '';
+ if ($keys) {
+ $fields = "
Public SSH key(s):
\n";
+ }
+
+ my $auth = $repo->wparam('auth');
+ if ($auth ne $user->{auth}) {
+ print '
You can paste multiple keys in the box below, each on a separate line.
+Paste each key including the ssh-whatever prefix and email-like postfix.
\n";
+ _auth_form($name, "Update keys", $fields, $auth);
+ exit;
+ }
+
+}
+
+print <Here you can update the public SSH keys associated with your user account. These keys are required for you to push to projects.
+
SSH is used for pushing (the git+ssh protocol), your SSH key authenticates you -
+there is no password (though we recommend that your SSH key is password-protected; use ssh-agent to help your fingers).
+You can find your public key in ~/.ssh/id_rsa.pub or ~/.ssh/id_dsa.pub.
+If you do not have any yet, generate it using the ssh-keygen command.
+
+
Please enter your username below so we can send you an authorization code to the e-mail address you gave us when you registered the account.
+
+
+EOT
diff --git a/cgi/reguser.cgi b/cgi/reguser.cgi
index e649066..3ef39ed 100755
--- a/cgi/reguser.cgi
+++ b/cgi/reguser.cgi
@@ -46,11 +46,10 @@ You need to register a user so that it can be granted project(s) push access.ssh-agent to help your fingers).
You can find your public key in ~/.ssh/id_rsa.pub or ~/.ssh/id_dsa.pub.
If you do not have any yet, generate it using the ssh-keygen command.
-You can paste multiple keys in the box below, each on a separate line. Paste each key including the ssh-whatever prefix and email-like postfix.
-Changing the keys later is not implemented yet - if you need to do that, please contact the administrator.
+You can paste multiple keys in the box below, each on a separate line. Paste each key including the ssh-whatever prefix and email-like postfix.
We won't bother to verify your email contact,
but fill in something sensible in your own interest
-so that we can contact you or confirm your identity shall the need arise.
+so that we can contact you or confirm your identity shall the need arise. We also need to send you an e-mail if you want to update your SSH keys later.
By submitting this form, you are confirming that you will push only free software
and no content that would violate any law of Czech Republic. Have fun!