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 '

Invalid authorization code, please re-enter or generate a new one.

'; + _auth_form($name, "'Login'", $fields); + exit; + } + + # Auth valid, keys given -> save + if ($keys) { + $user->keys_fill($repo); + $user->del_auth; + $user->keys_save; + print "

Your SSH keys have been updated.

"; + exit; + } + + # Otherwise pre-fill keys + $keys = $user->{keys}; + $fields = "

Public SSH key(s):

\n"; + + print "

Authorization code validated (for now).

+

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.

+ +
+

Login:

+

Anti-captcha (leave empty!):

+

+
+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!

diff --git a/indextext.html b/indextext.html index 55c5cdb..367ce22 100644 --- a/indextext.html +++ b/indextext.html @@ -9,7 +9,7 @@ and we will provide pull and gitweb access for the project. The service is maintained by Petr Baudis, please contact him with any requests, proposals or issues.

-

Register project | Register user

+

Register project | Register user | Update user SSH keys

How to grab a project? -- 2.11.4.GIT