aboutsummaryrefslogtreecommitdiff
path: root/www
diff options
context:
space:
mode:
Diffstat (limited to 'www')
-rw-r--r--www/authenticate.html44
-rw-r--r--www/authenticate.js33
-rwxr-xr-xwww/logged_in_p.pl48
-rwxr-xr-xwww/login.pl78
-rw-r--r--www/provision.html25
-rw-r--r--www/provision.js65
-rwxr-xr-xwww/sign.pl92
7 files changed, 385 insertions, 0 deletions
diff --git a/www/authenticate.html b/www/authenticate.html
new file mode 100644
index 0000000..32bd2ed
--- /dev/null
+++ b/www/authenticate.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>BrowserID IMAP Authentication</title>
+
+ <script type="text/javascript" src="https://browserid.org/include.js"></script>
+ <script type="text/javascript" src="https://browserid.org/authentication_api.js"></script>
+
+<!--
+ <script type="text/javascript" src="mozilla/include.js"></script>
+ <script type="text/javascript" src="mozilla/authentication_api.js"></script>
+-->
+
+<!--
+ <script type="text/javascript" src="https://dev.diresworb.org/include.js"></script>
+ <script type="text/javascript" src="https://dev.diresworb.org/authentication_api.js"></script>
+-->
+
+ <script type="text/javascript" src="jquery/jquery.js"></script>
+ <script type="text/javascript" src="authenticate.js"></script>
+</head>
+<body>
+ <h1>BrowserID IMAP Login</h1>
+
+ <form action="#" method="POST" id="auth-form">
+ <table>
+ <tbody>
+ <tr>
+ <td>E-Mail: </td>
+ <td><input type="text" id="email" name="email" disabled="disabled"></input></td>
+ </tr>
+ <tr>
+ <td>Password: </td>
+ <td><input type="password" id="password" name="password"></input></td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="button-box">
+ <button class="cancel">Cancel</button>
+ <input type="submit" value="Log in"></input>
+ </div>
+ </form>
+</body>
+</html>
diff --git a/www/authenticate.js b/www/authenticate.js
new file mode 100644
index 0000000..2d72480
--- /dev/null
+++ b/www/authenticate.js
@@ -0,0 +1,33 @@
+jQuery(function($) {
+ var email;
+
+ navigator.id.beginAuthentication(function(email_) {
+ email = email_;
+ $('#email').val(email);
+ });
+
+ var onAuthentication = function() {
+ var password = $('#password').val();
+ $.ajax({
+ type: 'POST',
+ url: '/browserid/login.pl',
+ dataType: 'json',
+ data: { email: email, password: password },
+ success: function(sig, status, xhr) {
+ console.log("Login successful!");
+ navigator.id.completeAuthentication();
+ },
+ error: function(reason, status, xhr) {
+ navigator.id.raiseAuthenticationFailure(reason.responseText);
+ }
+ });
+ return false;
+ };
+
+ var onCancel = function() {
+ navigator.id.cancelAuthentication();
+ };
+
+ $('#auth-form').submit(onAuthentication);
+ $('.cancel').click(onCancel);
+});
diff --git a/www/logged_in_p.pl b/www/logged_in_p.pl
new file mode 100755
index 0000000..a79d790
--- /dev/null
+++ b/www/logged_in_p.pl
@@ -0,0 +1,48 @@
+#! /usr/bin/env perl
+# Copyright 2012, Matthias Andreas Benkard <code@mail.matthias.benkard.de>.
+
+
+use common::sense;
+use Modern::Perl;
+
+use JSON;
+
+use File::Slurp;
+
+use CGI;
+use CGI::Fast;
+use CGI::Session;
+
+use Mail::ExpandAliases;
+
+
+while (my $cgi = new CGI::Fast) {
+ print $cgi->header(-content_type => 'application/json; charset=UTF-8');
+
+ my $cookie = $cgi->cookie('mulkid_session');
+ unless ($cookie) {
+ say encode_json({logged_in_p => 0});
+ exit(0);
+ }
+
+ my $session = new CGI::Session("driver:File", $cookie, {Directory=>"/tmp"});
+ unless ($session) {
+ say encode_json({logged_in_p => 0});
+ exit(0);
+ }
+
+ my $aliases = Mail::ExpandAliases->new("/etc/aliases");
+ my $email = $cgi->param('email') or die "No email address supplied";
+ my $session_user = $session->param('user');
+
+ my $alias;
+ if ($email =~ /^(.*?)@/) { $alias = $1; }
+ my $email_users = $aliases->expand($alias) or die "User not found";
+
+ if ($session_user ~~ @$email_users) {
+ say encode_json({logged_in_p => 1});
+ } else {
+ say encode_json({logged_in_p => 0});
+ }
+}
+
diff --git a/www/login.pl b/www/login.pl
new file mode 100755
index 0000000..630d5df
--- /dev/null
+++ b/www/login.pl
@@ -0,0 +1,78 @@
+#! /usr/bin/env perl
+# Copyright 2012, Matthias Andreas Benkard <code@mail.matthias.benkard.de>.
+
+use common::sense;
+#use Modern::Perl 2011;
+use Modern::Perl;
+
+use Mail::ExpandAliases;
+
+use JSON;
+
+use CGI;
+use CGI::Fast;
+use CGI::Session;
+
+use Mail::IMAPTalk ;
+#use IO::Socket::SSL;
+
+
+sub check_password($$) {
+ my ($user, $password) = @_;
+
+ #my $socket = IO::Socket::SSL->new('imap.googlemail.com:imaps');
+ my $imap = Mail::IMAPTalk->new(
+ # Socket => $socket,
+ Server => 'localhost',
+ Username => $user,
+ Password => $password,
+ Uid => 1
+ );
+ if ($imap) {
+ $imap->logout;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+while (my $cgi = new CGI::Fast) {
+ my $cookie = $cgi->cookie('mulkid_session');
+ my $session;
+ if ($cookie) {
+ $session = new CGI::Session("driver:File", $cookie, {Directory=>"/tmp"});
+ print $cgi->header(-content_type => 'application/json; charset=UTF-8');
+ } else {
+ $session = new CGI::Session("driver:File", undef, {Directory=>"/tmp"});
+ my $cookie = $cgi->cookie(-name => 'mulkid_session',
+ -value => $session->id,
+ -expires => '+1d',
+ -secure => 1,
+ -httponly => 1,
+ #-domain => '.mulk.eu'
+ );
+ print $cgi->header(-content_type => 'application/json; charset=UTF-8',
+ -cookie => $cookie);
+ }
+
+ my $aliases = Mail::ExpandAliases->new("/etc/aliases");
+
+ my $email = $cgi->param('email') or die "No email address provided";
+ my $password = $cgi->param('password') or die "Empty password";
+
+ my $alias;
+ if ($email =~ /^(.*?)@/) { $alias = $1; }
+ my $users = $aliases->expand($alias);
+
+ for my $user (@$users) {
+ #say STDERR "Trying user: $user";
+ if (check_password($user, $password)) {
+ $session->param('user', $user);
+ say encode_json({user => $user});
+ exit 0;
+ }
+ }
+
+ die "Could not authenticate with mail server.";
+}
diff --git a/www/provision.html b/www/provision.html
new file mode 100644
index 0000000..8f7c7aa
--- /dev/null
+++ b/www/provision.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title></title>
+
+ <script type="text/javascript" src="https://browserid.org/include.js"></script>
+ <script type="text/javascript" src="https://browserid.org/provisioning_api.js"></script>
+
+<!--
+ <script type="text/javascript" src="mozilla/include.js"></script>
+ <script type="text/javascript" src="mozilla/provisioning_api.js"></script>
+-->
+
+<!--
+ <script type="text/javascript" src="https://dev.diresworb.org/include.js"></script>
+ <script type="text/javascript" src="https://dev.diresworb.org/provisioning_api.js"></script>
+-->
+
+ <script type="text/javascript" src="jquery/jquery.js"></script>
+ <script type="text/javascript" src="provision.js"></script>
+
+</head>
+<body></body>
+</html>
+
diff --git a/www/provision.js b/www/provision.js
new file mode 100644
index 0000000..5441bce
--- /dev/null
+++ b/www/provision.js
@@ -0,0 +1,65 @@
+var sign;
+
+jQuery(function($) {
+ // NB. By browsing secondary server source code, I have
+ // discovered that 'user is not authenticated as target user' is
+ // the only acceptable error message here.
+ //
+ // See: static/shared/user.js, primaryUserAuthenticationInfo
+ var ifLoggedIn = function(email, thunk) {
+ $.ajax({
+ type: 'POST',
+ crossDomain: true,
+ url: '/browserid/logged_in_p.pl',
+ dataType: 'json',
+ data: { email: email },
+ success: function(data, status, xhr) {
+ console.log('Logged in: ' + data.logged_in_p);
+ if (data.logged_in_p === 0) {
+ console.log('* Not logged in.');
+ return navigator.id.raiseProvisioningFailure('user is not authenticated as target user');
+ } else {
+ thunk();
+ }
+ },
+ error: function(data, status, xhr) {
+ console.log('Logged in: no.');
+ return navigator.id.raiseProvisioningFailure('user is not authenticated as target user');
+ }
+ });
+ };
+
+ sign = function(email, pubkey, cert_duration) {
+ console.log('email: ' + email);
+ console.log('pubkey: '); console.log(pubkey);
+ console.log('duration: ' + cert_duration);
+ $.ajax({
+ type: 'POST',
+ crossDomain: true,
+ url: '/browserid/sign.pl',
+ dataType: 'json',
+ data: { email: email, pubkey: JSON.stringify(pubkey), duration: cert_duration },
+ success: function(data, status, xhr) {
+ console.log('Success!');
+ console.log(data);
+ navigator.id.registerCertificate(data.signature);
+ },
+ error: function(data, status, xhr) {
+ return navigator.id.raiseProvisioningFailure(data.responseText);
+ }
+ });
+ };
+
+ navigator.id.beginProvisioning(function(email, cert_duration) {
+ console.log('email: ' + email);
+ console.log('cert_duration: ' + cert_duration);
+ ifLoggedIn(email, function() {
+ navigator.id.genKeyPair(function(pubkey) {
+ if (typeof(pubkey) === 'string') {
+ pubkey = JSON.parse(pubkey);
+ }
+ sign(email, pubkey, cert_duration);
+ });
+ });
+ });
+});
diff --git a/www/sign.pl b/www/sign.pl
new file mode 100755
index 0000000..c7e51ad
--- /dev/null
+++ b/www/sign.pl
@@ -0,0 +1,92 @@
+#! /usr/bin/env perl
+# Copyright 2012, Matthias Andreas Benkard <code@mail.matthias.benkard.de>.
+
+
+use common::sense;
+use Modern::Perl;
+
+use JSON;
+
+use Crypt::OpenSSL::RSA;
+
+use File::Slurp;
+
+use CGI;
+use CGI::Fast;
+use CGI::Session;
+
+use Mail::ExpandAliases;
+
+use MIME::Base64 qw(encode_base64 decode_base64);
+
+use Time::HiRes qw(time);
+
+
+sub decode_base64_url($) {
+ # From: https://github.com/ptarjan/base64url/blob/master/perl.pl
+ (my $s = shift) =~ tr{-_}{+/};
+ $s .= '=' x (4 - length($s));
+ return decode_base64($s);
+}
+
+sub encode_base64_url($) {
+ my ($s) = shift;
+ $s = encode_base64($s);
+ $s =~ tr{+/}{-_};
+ $s =~ s/=*$//;
+ $s =~ s/\n//g;
+ return $s;
+}
+
+
+sub sign($$$$) {
+ # NB. Treating the jwcrypto code as the spec here.
+ my ($key, $client_pubkey, $email, $duration) = @_;
+
+ my $issued_at = int(1000*time);
+
+ my $cert = {
+ iss => "mulk.eu",
+ exp => $issued_at + 1000*$duration,
+ iat => $issued_at,
+ "public-key" => $client_pubkey,
+ principal => { email => $email }
+ };
+
+ my $header = {typ => "JWT", alg => "RS256"};
+ my $header_bytes = encode_base64_url(encode_json($header));
+ my $cert_bytes = encode_base64_url(encode_json($cert));
+ my $string_to_sign = $header_bytes . "." . $cert_bytes;
+
+ my $sig = $key->sign($string_to_sign);
+ my $sig_bytes = encode_base64_url($sig);
+
+ return $header_bytes . "." . $cert_bytes . "." . $sig_bytes;
+}
+
+
+while (my $cgi = new CGI::Fast) {
+ my $cookie = $cgi->cookie('mulkid_session') or die "No session cookie";
+ my $session = new CGI::Session("driver:File", $cookie, {Directory=>"/tmp"}) or die "Invalid session cookie";
+ print $cgi->header(-content_type => 'application/json; charset=UTF-8');
+
+ my $key = Crypt::OpenSSL::RSA->new_private_key(scalar read_file('/etc/mulkid/rsa2048.pem'));
+ $key->use_pkcs1_padding();
+ $key->use_sha256_hash();
+
+ my $aliases = Mail::ExpandAliases->new("/etc/aliases");
+ my $user_pubkey = $cgi->param('pubkey') or die "Nothing to sign";
+ my $duration = $cgi->param('duration') || 24*3600;
+ my $email = $cgi->param('email') or die "No email address supplied";
+ my $session_user = $session->param('user');
+
+ my $alias;
+ if ($email =~ /^(.*?)@/) { $alias = $1; }
+ my $email_users = $aliases->expand($alias) or die "User not found";
+
+ die "User is not authorized to use this email address"
+ unless ($session_user ~~ @$email_users);
+
+ my $sig = sign $key, decode_json($user_pubkey), $email, $duration;
+ say encode_json({signature => $sig});
+}