diff -Nru msva-perl-0.9.1/Changelog msva-perl-0.9.2/Changelog --- msva-perl-0.9.1/Changelog 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/Changelog 2013-09-09 07:22:59.000000000 +0100 @@ -1,3 +1,14 @@ +msva-perl (0.9.2) upstream; + + * tweak POD to declare charset + * openpgp2x509: a bit more clean up and features (needs more work) + * handle multiple keyserver entries in gpg.conf gracefully + (closes MS #6252) + * also accept DOS-style CR/LF line endings in PEM-encoded X.509 certs + * msva-query-agent: produce newline-terminated output. + + -- Daniel Kahn Gillmor Mon, 09 Sep 2013 01:16:47 -0400 + msva-perl (0.9.1) upstream; * Bug Fix Release: diff -Nru msva-perl-0.9.1/Crypt/Monkeysphere/Keyserver.pm msva-perl-0.9.2/Crypt/Monkeysphere/Keyserver.pm --- msva-perl-0.9.1/Crypt/Monkeysphere/Keyserver.pm 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/Crypt/Monkeysphere/Keyserver.pm 2013-09-09 07:22:59.000000000 +0100 @@ -82,11 +82,20 @@ if (-f $gpgconf) { if (-r $gpgconf) { my %gpgconfig = Config::General::ParseConfig($gpgconf); - if ($gpgconfig{keyserver} =~ /^(((hkps?|hkpms|finger|ldap):\/\/)?$RE{net}{domain})$/) { - $self->log('debug', "Using keyserver %s from the GnuPG configuration file (%s)\n", $1, $gpgconf); - return $1; + if (! defined $gpgconfig{keyserver}) { + $self->log('debug', "No keyserver line found in GnuPG configuration file (%s)\n", $gpgconf); } else { - $self->log('error', "Not a valid keyserver (from gpg config %s):\n %s\n", $gpgconf, $gpgconfig{keyserver}); + if (ref($gpgconfig{keyserver}) eq 'ARRAY') { + # use the last keyserver entry if there is more than one. + $self->log('debug', "more than one keyserver line found in GnuPG configuration file (%s), using last one found\n", $gpgconf); + $gpgconfig{keyserver} = pop(@{$gpgconfig{keyserver}}); + } + if ($gpgconfig{keyserver} =~ /^(((hkps?|hkpms|finger|ldap):\/\/)?$RE{net}{domain})$/) { + $self->log('debug', "Using keyserver %s from the GnuPG configuration file (%s)\n", $1, $gpgconf); + return $1; + } else { + $self->log('error', "Not a valid keyserver (from gpg config %s):\n %s\n", $gpgconf, $gpgconfig{keyserver}); + } } } else { $self->log('error', "The GnuPG configuration file (%s) is not readable\n", $gpgconf); diff -Nru msva-perl-0.9.1/Crypt/Monkeysphere/MSVA.pm msva-perl-0.9.2/Crypt/Monkeysphere/MSVA.pm --- msva-perl-0.9.1/Crypt/Monkeysphere/MSVA.pm 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/Crypt/Monkeysphere/MSVA.pm 2013-09-09 07:22:59.000000000 +0100 @@ -45,7 +45,7 @@ # we need the version of GnuPG::Interface that knows about pubkey_data, etc: use GnuPG::Interface 0.43; - $VERSION = '0.9.1'; + $VERSION = '0.9.2'; my $gnupg = GnuPG::Interface::->new(); $gnupg->options->quiet(1); @@ -461,7 +461,7 @@ sub pem2der { my $pem = shift; - my @lines = split(/\n/, $pem); + my @lines = split(/\r?\n/, $pem); my @goodlines = (); my $ready = 0; foreach my $line (@lines) { diff -Nru msva-perl-0.9.1/debian/changelog msva-perl-0.9.2/debian/changelog --- msva-perl-0.9.1/debian/changelog 2013-05-21 17:58:33.000000000 +0100 +++ msva-perl-0.9.2/debian/changelog 2013-11-28 16:11:31.000000000 +0000 @@ -1,3 +1,41 @@ +msva-perl (0.9.2-1ubuntu1) trusty; urgency=low + + * Merge from Debian unstable. Remaining changes: + - Provide an upstart user session job. It works like this: + - Start monkeysphere-validation-agent before starting the session (on + starting xsession-init) + - Because msva-perl stays in the foreground, we can't export its + environment variable (which it prints on standard output). + - It also has a mode in which it can take a child to run. The agent keeps + running as long as the child is running. + - Provide a child which runs `initctl set-env' to set the environment + variable. + - It turns out that upstart continues the job started as soon as it has + executed the main script. This means that the session is started before + the `initctl set-env' has had a chance to run. So make the child script + emit an event after running set-env which a *second*, empty, job + (msva-block) waits for and blocks the session start. + + -- Roy Jamison Thu, 28 Nov 2013 16:10:16 +0000 + +msva-perl (0.9.2-1) unstable; urgency=low + + * New upstream version + - includes POD failure fix from gregor, thanks! + * updated to dh 9 + * bumped Standards-Version to 3.9.4 (no changes needed) + + -- Daniel Kahn Gillmor Fri, 13 Sep 2013 11:00:30 -0400 + +msva-perl (0.9.1-1.1) unstable; urgency=low + + * Non-maintainer upload. + * Fix "FTBFS with perl 5.18: POD failure": + add patch to add encoding to POD. + (Closes: #720430) + + -- gregor herrmann Fri, 30 Aug 2013 17:38:58 +0200 + msva-perl (0.9.1-1ubuntu1) saucy; urgency=low * Provide an upstart user session job. It works like this: diff -Nru msva-perl-0.9.1/debian/compat msva-perl-0.9.2/debian/compat --- msva-perl-0.9.1/debian/compat 2012-09-09 20:36:08.000000000 +0100 +++ msva-perl-0.9.2/debian/compat 2013-09-13 23:10:58.000000000 +0100 @@ -1 +1 @@ -7 +9 diff -Nru msva-perl-0.9.1/debian/control msva-perl-0.9.2/debian/control --- msva-perl-0.9.1/debian/control 2012-09-09 20:36:08.000000000 +0100 +++ msva-perl-0.9.2/debian/control 2013-11-28 16:14:35.000000000 +0000 @@ -1,12 +1,13 @@ Source: msva-perl Section: net Priority: extra -Maintainer: Daniel Kahn Gillmor +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Daniel Kahn Gillmor Uploaders: Micah Anderson Build-Depends: - debhelper (>= 7.0), + debhelper (>= 9), perl -Standards-Version: 3.9.3 +Standards-Version: 3.9.4 Homepage: http://web.monkeysphere.info/ Vcs-Git: git://git.monkeysphere.info/msva-perl diff -Nru msva-perl-0.9.1/msva-perl msva-perl-0.9.2/msva-perl --- msva-perl-0.9.1/msva-perl 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/msva-perl 2013-09-09 07:22:59.000000000 +0100 @@ -29,6 +29,8 @@ msva=>$server); __END__ +=encoding utf8 + =head1 NAME msva-perl - Perl implementation of a Monkeysphere Validation Agent diff -Nru msva-perl-0.9.1/msva-query-agent msva-perl-0.9.2/msva-query-agent --- msva-perl-0.9.1/msva-query-agent 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/msva-query-agent 2013-09-09 07:22:59.000000000 +0100 @@ -43,7 +43,7 @@ my ($status,$ret) = $client->agent_info(); $client->log('verbose', "status: %s\n", $status); if (defined $ret) { - printf("%s", $ret->{server}); + printf("%s\n", $ret->{server}); exit 0; } exit 1; @@ -80,12 +80,14 @@ exit 1; } } else { - printf("%s", $status); + printf("%s\n", $status); exit 100; } __END__ +=encoding utf8 + =head1 NAME msva-query-agent - query a Monkeysphere Validation Agent diff -Nru msva-perl-0.9.1/openpgp2x509 msva-perl-0.9.2/openpgp2x509 --- msva-perl-0.9.1/openpgp2x509 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/openpgp2x509 2013-09-09 07:22:59.000000000 +0100 @@ -1,17 +1,29 @@ #!/usr/bin/perl # Author: Daniel Kahn Gillmor -# Copyright: 2011 +# Copyright: 2011, 2013 # License: GPL-3+ +# Usage (two examples): + +# openpgp2x509 'Daniel Kahn Gillmor ' +# openpgp2x509 ssh://lair.fifthhorseman.net + +# Each invocation will produce a series of PEM-encoded X.509 +# certificates on stdout corresponding to keys that are well-bound to +# the specified OpenPGP User ID. + +# This tool should detect (based on the form of the User ID) what kind +# of X.509 certificate to produce + +# It only emits certificates for OpenPGP keys that are marked with the +# "Authentication" usage flag. FIXME: make the usage flag selection +# adjustable by an environment variable or something. + # WARNING: This is very rough code! the interface WILL change # dramatically. The only thing I can commit to keeping stable are the # OIDs. -# Use this code to take an OpenPGP certificate (pubkey) and emit a -# corresponding OpenPGP-validated X.509 certificate. - -# Usage: openpgp2x509 ssh://lair.fifthhorseman.net use strict; use warnings; @@ -25,15 +37,18 @@ $cert->configure('encode' => { 'time' => 'raw' } ); my $pgpe = Crypt::X509::_init('PGPExtension'); $pgpe->configure('encode' => { 'time' => 'raw' } ); +my $san = Crypt::X509::_init('SubjectAltName'); +$san->configure('encode' => { 'time' => 'raw' } ); my $rsapubkeyinfo = Crypt::X509::_init('RSAPubKeyInfo'); my $dntypes = { 'CN' => '2.5.4.3', # common name - 'emailAddress' => '1.2.840.113549.1.9.1', # e-mail address + 'emailAddress' => '1.2.840.113549.1.9.1', # e-mail address -- DEPRECATED. should use subjectAltName instead. 'C' => '2.5.4.6', # country 'ST' => '2.5.4.8', # state 'L' => '2.5.4.7', # locality 'O' => '2.5.4.10', # organization 'OU' => '2.5.4.11', # organization unit (often used as a comment) + 'PSEUDO' => '2.5.4.65', # pseudonym (used for the parenthetical "comment" in the conventional OpenPGP User ID) }; my $algos = { @@ -115,7 +130,10 @@ # I think it means the NULL type. my $noparams = sprintf('%c%c', 5, 0); -my $extensions = { 'PGPExtension' => '1.3.6.1.4.1.3401.8.1.1' }; +my $extensions = { + 'PGPExtension' => '1.3.6.1.4.1.3401.8.1.1', + 'subjectAltName' => '2.5.29.17', # https://tools.ietf.org/html/rfc5280#section-4.2.1.6 + }; my $gnupg = GnuPG::Interface::->new(); $gnupg->options->quiet(1); @@ -159,8 +177,10 @@ my $protocol; my $emailaddress; my $humanname; + my $comment; my $subject; my $ret = []; + my @subjectAltNames; if ($userid =~ /^\s+/) { err("We will not process User IDs with leading whitespace\n"); @@ -176,25 +196,41 @@ } # FIXME: do we want to rule out any other forms of User ID? - - if ($userid =~ /^(.*)\s+<([^><@\s]+\@$RE{net}{domain})>$/ ) { + if ($userid =~ /^([^()]*)\s+(\((.*)\)\s+)?<([^><@\s]+\@$RE{net}{domain})>$/ ) { # this is a typical/expected OpenPGP User ID. + $humanname = $1; - $emailaddress = $2; + $comment = $3; + $emailaddress = $4; + + # We're stripping arbitrary amounts of whitespace between the + # name, the comment, and the e-mail address here. if that + # whitespace is anything but " " then the OpenPGP User ID will not + # be reconstructible from the string. + my $reconstructeduid; + if (defined($comment)) { + $reconstructeduid = sprintf('%s (%s) <%s>', $humanname, $comment, $emailaddress); + } else { + $reconstructeduid = sprintf('%s <%s>', $humanname, $emailaddress); + } + if ($userid ne $reconstructeduid) { + err("This OpenPGP User ID could not be reconstructed from the X.509 certificate we would generate. Maybe a whitespace issue?\n"); + return $ret; + } + $subject = [ [ { 'type' => $dntypes->{'CN'}, 'value' => { - 'printableString' => $humanname, - }, - } ], - [ { - 'type' => $dntypes->{'emailAddress'}, - 'value' => { - 'ia5String' => $emailaddress, + 'utf8String' => $humanname, }, } ], ]; + push(@{ $subject }, [ { 'type' => $dntypes->{'PSEUDO'}, + 'value' => { 'utf8String' => $comment } } ] ) + if defined($comment); + + push(@subjectAltNames, { 'rfc822Name' => $emailaddress }); } elsif ($userid =~ /^(https|ssh|smtps?|ike|postgresql|imaps?|submission):\/\/($RE{net}{domain})$/) { $protocol = $1; $hostname = $2; @@ -204,10 +240,22 @@ 'printableString' => $hostname }, } ] ]; + push(@subjectAltNames, { 'dNSName' => $hostname }); } else { - # what should we do here? Maybe we just assume this is a bare Human Name? + # Maybe we just assume this is a bare Human Name? + + # what if it's a human name plus a comment? should we treat the + # comment like a pseudonym, as above? err("Assuming '%s' is a bare human name.\n", $userid); $humanname = $userid; + $subject = [ + [ { + 'type' => $dntypes->{'CN'}, + 'value' => { + 'printableString' => $humanname, + }, + } ], + ]; } foreach my $gpgkey ($gnupg->get_public_keys('='.$userid)) { @@ -235,7 +283,21 @@ err("key 0x%s is algorithm %d (not RSA) -- we currently only handle RSA\n", $subkey->fingerprint->as_hex_string, $subkey->algo_num); next; } - # FIXME: reject/skip over revoked/expired keys. + # FIXME: reject/skip over revoked keys. + if (defined($subkey->{expiration_date}) && + $subkey->{expiration_date} <= time()) { + err("key 0x%s is expired -- skipping\n", $subkey->fingerprint->as_hex_string); + next; + } + if ($subkey->{usage_flags} =~ /D/) { + err("key 0x%s is disabled -- skipping\n", $subkey->fingerprint->as_hex_string); + next; + } + if ($subkey->{usage_flags} !~ /a/) { + err("key 0x%s is not authentication-capable -- skipping\n", $subkey->fingerprint->as_hex_string); + next + } + err("making certificate for key 0x%s\n", $subkey->fingerprint->as_hex_string); my $pubkey = { 'modulus' => @{$subkey->pubkey_data}[0], 'exponent' => @{$subkey->pubkey_data}[1], @@ -266,12 +328,20 @@ push(@extensions, { 'extnID' => $extensions->{'PGPExtension'}, 'extnValue' => $pgpeval }); + if (@subjectAltNames) { + my $saneval = $san->encode(\@subjectAltNames); + print $san->{error} + if (!defined($saneval)); + push(@extensions, { 'extnID' => $extensions->{'subjectAltName'}, + 'extnValue' => $saneval + }); + } # FIXME: base some keyUsage extensions on the type of User ID # and on the usage flags of the key in question. # if 'a' is present - # if protocol =~ /^http|ssh|smtps?|postgresql|imaps?|submission$/ then set TLS server eKU + ??? + # if protocol =~ /^https|smtps?|postgresql|imaps?|submission$/ then set TLS server eKU + ??? # if protocol eq 'ike' then ??? (ask micah) # if protocol =~ /^smtps?$/ then set TLS client + ??? # if defined($humanname) then set TLS client + ??? @@ -292,8 +362,10 @@ # FIXME: add subjectAltName that matches the type of information # we believe we're working with (see the cert-id draft). - # FIXME: if @sans is present, add them as subjectAltNames (do we - # want to do this? maybe this should be optional). + # if @sans is present, should we add them as subjectAltNames? i + # don't think so. this certificate should be just for the User + # ID requested. The user can always make another certificate + # for the other user IDs and use that one. my $newcert = { @@ -336,7 +408,7 @@ my $dd = $cert->encode($newcert); - push(@{$ret}, $dd); + push(@{$ret}, { 'der' => $dd, 'fpr' => $subkey->fingerprint->as_hex_string}); } } return $ret; @@ -344,5 +416,24 @@ foreach $cert ( @{ makeX509CertForUserID($ARGV[0]) } ) { - printf("-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", encode_base64($cert)); + if (defined($ENV{OPENPGP2X509_EMIT_PKCS12})) { + # FIXME: figure out how to do this with certtool instead of openssl; + # the PKCS12 files i've tried to generate from certtool --to-p12 + # can't be loaded by iceweasel for some reason. + + # FIXME: don't do this horrific shell nastiness. be nicer! + $ENV{CERTOUTPUT} = sprintf("-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", + encode_base64($cert->{'der'})); + $ENV{FPR} = $cert->{'fpr'}; + $ENV{OPENPGP_UID} = $ARGV[0]; + + # Note that while pkcs12(1ssl) claims that the order doesn't + # matter, in fact, this doesn't work if you emit the certificate + # before you emit the key. + system('(gpg --export-options export-reset-subkey-passwd --export-secret-subkeys "0x$FPR"\! |'. + 'openpgp2ssh "$FPR" && printf "%s" "$CERTOUTPUT") |'. + 'openssl pkcs12 -export -name "$OPENPGP_UID"'); + } else { + printf("-----BEGIN CERTIFICATE-----\n%s-----END CERTIFICATE-----\n", encode_base64($cert->{'der'})); + } } diff -Nru msva-perl-0.9.1/tests/basic msva-perl-0.9.2/tests/basic --- msva-perl-0.9.1/tests/basic 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/tests/basic 2013-09-09 07:22:59.000000000 +0100 @@ -38,6 +38,7 @@ WORKDIR=$(mktemp -d) mkdir -m 0700 "${WORKDIR}/"{pkc,sec,gnupg} +touch "${WORKDIR}/gnupg/gpg.conf" export GNUPGHOME="${WORKDIR}/gnupg" if gpg --quick-random --version ; then diff -Nru msva-perl-0.9.1/unit-tests/10.keyserver/10.gnupghome.t msva-perl-0.9.2/unit-tests/10.keyserver/10.gnupghome.t --- msva-perl-0.9.1/unit-tests/10.keyserver/10.gnupghome.t 2012-09-09 20:35:13.000000000 +0100 +++ msva-perl-0.9.2/unit-tests/10.keyserver/10.gnupghome.t 2013-09-09 07:22:59.000000000 +0100 @@ -8,7 +8,7 @@ use warnings; my $fpr='762B57BB784206AD'; -plan tests =>5; +plan tests =>9; { @@ -37,3 +37,25 @@ isa_ok($ks,'Crypt::Monkeysphere::Keyserver'); is($ks->{keyserver},$testks); + +open GPGCONF, '>', "$tempdir/gpg.conf"; +print GPGCONF "keyserver $testks\n"; +print GPGCONF "keyserver $testks.example\n"; +close GPGCONF; + +$ks=new Crypt::Monkeysphere::Keyserver(gnupg=>$gnupg, + loglevel=>'debug'); + +isa_ok($ks,'Crypt::Monkeysphere::Keyserver'); + +is($ks->{keyserver},"$testks.example"); + +open GPGCONF, '>', "$tempdir/gpg.conf"; +close GPGCONF; + +$ks=new Crypt::Monkeysphere::Keyserver(gnupg=>$gnupg, + loglevel=>'debug'); + +isa_ok($ks,'Crypt::Monkeysphere::Keyserver'); + +is($ks->{keyserver},$Crypt::Monkeysphere::Keyserver::default_keyserver);