[pve-devel] [PATCH access-control 1/2] add PVE::U2F, libu2f-server bindings
Wolfgang Bumiller
w.bumiller at proxmox.com
Thu May 24 15:28:48 CEST 2018
Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
---
.gitignore | 3 ++
Makefile | 25 ++++++++-
PVE/U2F.pm | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++
U2F.xs | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 361 insertions(+), 1 deletion(-)
create mode 100644 PVE/U2F.pm
create mode 100644 U2F.xs
diff --git a/.gitignore b/.gitignore
index e1fc9d6..9ef3233 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
build
+ppport.h
+U2F.so
+U2F.xsc
*.deb
*.1.pod
*.1.gz
diff --git a/Makefile b/Makefile
index bc1ca81..ba46235 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,17 @@ GITVERSION:=$(shell cat .git/refs/heads/master)
DEB=${PACKAGE}_${VERSION}-${PKGREL}_${ARCH}.deb
+PERL_ARCHLIB != perl -MConfig -e 'print $$Config{archlib};'
+PERL_INSTALLVENDORARCH != perl -MConfig -e 'print $$Config{installvendorarch};'
+PERL_APIVER != perl -MConfig -e 'print $$Config{debian_abi}//$$Config{version};'
+PERL_CC != perl -MConfig -e 'print $$Config{cc};'
+PERLSODIR=$(PERL_INSTALLVENDORARCH)/auto
+CFLAGS := -shared -fPIC -O2 -Werror -Wtype-limits -Wall -Wl,-z,relro \
+ -D_FORTIFY_SOURCE=2 -I$(PERL_ARCHLIB)/CORE -DXS_VERSION=\"1.0\"
+
+CFLAGS += `pkg-config --cflags u2f-server`
+LIBS += `pkg-config --libs u2f-server`
+
# this requires package pve-doc-generator
export NOVIEW=1
include /usr/share/pve-doc-generator/pve-doc-generator.mk
@@ -34,8 +45,18 @@ pveum.bash-completion: PVE/CLI/pveum.pm
perl -I. -T -e "use PVE::CLI::pveum; PVE::CLI::pveum->generate_bash_completions();" >$@.tmp
mv $@.tmp $@
+U2F.c: U2F.xs
+ xsubpp U2F.xs > U2F.xsc
+ mv U2F.xsc U2F.c
+
+ppport.h:
+ perl -MDevel::PPPort -e 'Devel::PPPort::WriteFile();'
+
+U2F.so: U2F.c ppport.h
+ $(PERL_CC) $(CFLAGS) -o U2F.so U2F.c $(LIBS)
+
.PHONY: install
-install: pveum.1 oathkeygen pveum.bash-completion
+install: pveum.1 oathkeygen pveum.bash-completion U2F.so
install -d ${DESTDIR}${BINDIR}
install -d ${DESTDIR}${SBINDIR}
install -m 0755 pveum ${DESTDIR}${SBINDIR}
@@ -45,6 +66,8 @@ install: pveum.1 oathkeygen pveum.bash-completion
install -d ${DESTDIR}/${DOCDIR}
install -m 0644 pveum.1 ${DESTDIR}/${MAN1DIR}
gzip -9 -n ${DESTDIR}/${MAN1DIR}/pveum.1
+ install -D -m 0644 PVE/U2F.pm ${DESTDIR}${PERLDIR}/PVE/U2F.pm
+ install -D -m 0644 -s U2F.so ${DESTDIR}${PERLSODIR}/PVE/U2F/U2F.so
install -m 0644 -D pveum.bash-completion ${DESTDIR}${BASHCOMPLDIR}/pveum
.PHONY: test
diff --git a/PVE/U2F.pm b/PVE/U2F.pm
new file mode 100644
index 0000000..acc9348
--- /dev/null
+++ b/PVE/U2F.pm
@@ -0,0 +1,155 @@
+package PVE::U2F;
+
+use 5.024000;
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+# This allows declaration use PVE::U2F::XS ':all';
+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
+# will save memory.
+our %EXPORT_TAGS = ( 'all' => [] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = ();
+our $VERSION = '1.0';
+
+require XSLoader;
+XSLoader::load('PVE::U2F', $VERSION);
+
+#### Context creation
+
+my $global_init = 0;
+sub new($) {
+ my ($class) = @_;
+ if (!$global_init) {
+ $global_init = 1;
+ do_global_init();
+ }
+ if (my $lib = new_impl()) {
+ return bless { ctx => $lib }, $class;
+ }
+ return undef;
+}
+
+sub DESTROY {
+ my ($self) = @_;
+ done_impl($self->{ctx});
+}
+
+#### Error handling
+
+my @errcodes = (
+qw(memory json base64 crypto origin challenge signature format)
+);
+sub checkrc($) {
+ my ($rc) = @_;
+ return if $rc == 0;
+ die "u2fs: $errcodes[-$rc-1] error\n" if $rc < 0 && $rc >= -8;
+ die "u2fs: unknown error\n";
+}
+
+#### Context initialization
+
+sub origin($) { return $_[0]->{origin}; }
+sub set_origin($$) {
+ my ($self, $origin) = @_;
+ checkrc(set_origin_impl($self->{ctx}, $origin));
+ return $self->{origin} = $origin;
+}
+
+sub appid($) { return $_[0]->{appid}; }
+sub set_appid($$) {
+ my ($self, $appid) = @_;
+ checkrc(set_appid_impl($self->{ctx}, $appid));
+ return $self->{appid} = $appid;
+}
+
+sub challenge($) { return $_[0]->{challenge}; }
+sub set_challenge($$) {
+ my ($self, $challenge) = @_;
+ checkrc(set_challenge_impl($self->{ctx}, $challenge));
+ return $self->{challenge} = $challenge;
+}
+
+sub keyHandle($) { return $_[0]->{keyHandle}; }
+sub set_keyHandle($$) {
+ my ($self, $keyHandle) = @_;
+ checkrc(set_keyHandle_impl($self->{ctx}, $keyHandle));
+ return $self->{keyHandle} = $keyHandle;
+}
+
+sub publicKey($) { return $_[0]->{publicKey}; }
+sub set_publicKey($$) {
+ my ($self, $publicKey) = @_;
+ checkrc(set_publicKey_impl($self->{ctx}, $publicKey));
+ return $self->{publicKey} = $publicKey;
+}
+
+#### Registration
+
+sub registration_challenge($) {
+ my ($self) = @_;
+ checkrc(registration_challenge_impl($self->{ctx}, my $challenge));
+ return $challenge;
+}
+
+sub registration_verify($$) {
+ my ($self, $response) = @_;
+ checkrc(registration_verify_impl($self->{ctx}, $response, my $kh, my $pk));
+ return ($kh, $pk);
+}
+
+#### Authentication
+
+sub auth_challenge($) {
+ my ($self) = @_;
+ checkrc(auth_challenge_impl($self->{ctx}, my $challenge));
+ return $challenge;
+}
+
+sub auth_verify($$) {
+ my ($self, $response) = @_;
+ checkrc(auth_verify_impl($self->{ctx}, $response,
+ my $verified,
+ my $counter,
+ my $presence));
+ checkrc($verified);
+ return wantarray ? ($counter, $presence) : 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+PVE::U2F - Perl bindings for libu2f-server
+
+=head1 SYNOPSIS
+
+ use PVE::U2F;
+
+=head1 DESCRIPTION
+
+Perl bindings for libu2f-server
+
+=head2 EXPORT
+
+None by default.
+
+=head1 SEE ALSO
+
+TODO
+
+=head1 AUTHOR
+
+Proxmox Server Solutions GmbH <support at proxmox.com>
+
+=cut
diff --git a/U2F.xs b/U2F.xs
new file mode 100644
index 0000000..2e167d2
--- /dev/null
+++ b/U2F.xs
@@ -0,0 +1,179 @@
+#define PERL_NO_GET_CONTEXT
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include "ppport.h"
+
+#include <u2f-server.h>
+
+MODULE = PVE::U2F PACKAGE = PVE::U2F
+
+#// Context creation and destruction
+
+void
+do_global_init()
+ CODE:
+ u2fs_global_init(0);
+
+void
+do_global_done()
+ CODE:
+ u2fs_global_done();
+
+SV*
+new_impl()
+ CODE:
+ u2fs_ctx_t *ctx = NULL;
+ if (u2fs_init(&ctx) != U2FS_OK) {
+ RETVAL = &PL_sv_undef;
+ } else {
+ RETVAL = newSVpv((char*)&ctx, sizeof(ctx));
+ }
+ OUTPUT:
+ RETVAL
+
+void
+done_impl(ctx)
+ SV *ctx
+ CODE:
+ if (ctx == &PL_sv_undef) {
+ croak("u2fs xs: double free");
+ } else {
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ u2fs_done(*pctx);
+ sv_setsv(ctx, &PL_sv_undef);
+ }
+
+#// Context initialization before registration/authentication
+
+int
+set_origin_impl(ctx, origin)
+ SV *ctx
+ char *origin
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ RETVAL = u2fs_set_origin(*pctx, origin);
+ OUTPUT:
+ RETVAL
+
+int
+set_appid_impl(ctx, appid)
+ SV *ctx
+ char *appid
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ RETVAL = u2fs_set_appid(*pctx, appid);
+ OUTPUT:
+ RETVAL
+
+int
+set_challenge_impl(ctx, challenge)
+ SV *ctx
+ char *challenge
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ RETVAL = u2fs_set_challenge(*pctx, challenge);
+ OUTPUT:
+ RETVAL
+
+int
+set_keyHandle_impl(ctx, keyHandle)
+ SV *ctx
+ char *keyHandle
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ RETVAL = u2fs_set_keyHandle(*pctx, keyHandle);
+ OUTPUT:
+ RETVAL
+
+int
+set_publicKey_impl(ctx, publicKey)
+ SV *ctx
+ unsigned char *publicKey
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ RETVAL = u2fs_set_publicKey(*pctx, publicKey);
+ OUTPUT:
+ RETVAL
+
+#// Registration functions
+
+int
+registration_challenge_impl(ctx, outref=&PL_sv_undef)
+ SV *ctx
+ SV *outref
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ char *output = NULL;
+ u2fs_rc rc = u2fs_registration_challenge(*pctx, &output);
+ if (rc == U2FS_OK) {
+ sv_setpv(outref, output);
+ }
+ RETVAL = rc;
+ OUTPUT:
+ RETVAL
+
+int
+registration_verify_impl(ctx, response, kh=&PL_sv_undef, pk=&PL_sv_undef)
+ SV *ctx
+ char *response
+ SV *kh
+ SV *pk
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ u2fs_reg_res_t *result = NULL;
+ u2fs_rc rc = u2fs_registration_verify(*pctx, response, &result);
+ if (rc == U2FS_OK) {
+ const char *keyHandle = u2fs_get_registration_keyHandle(result);
+ const char *publicKey = u2fs_get_registration_publicKey(result);
+ sv_setpv(kh, keyHandle);
+ sv_setpv(pk, publicKey);
+ u2fs_free_reg_res(result);
+ }
+ RETVAL = rc;
+ OUTPUT:
+ RETVAL
+
+#// Authentication functions
+int
+auth_challenge_impl(ctx, outref=&PL_sv_undef)
+ SV *ctx
+ SV *outref
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ char *output = NULL;
+ u2fs_rc rc = u2fs_authentication_challenge(*pctx, &output);
+ if (rc == U2FS_OK) {
+ sv_setpv(outref, output);
+ }
+ RETVAL = rc;
+ OUTPUT:
+ RETVAL
+
+int
+auth_verify_impl(ctx, response, verified=&PL_sv_undef, counter=&PL_sv_undef, presence=&PL_sv_undef)
+ SV *ctx
+ char *response
+ SV *verified
+ SV *counter
+ SV *presence
+ CODE:
+ u2fs_ctx_t **pctx = (u2fs_ctx_t**)SvPV_nolen(ctx);
+ u2fs_auth_res_t *result = NULL;
+ u2fs_rc rc = u2fs_authentication_verify(*pctx, response, &result);
+ if (rc == U2FS_OK) {
+ u2fs_rc a_verified = 0;
+ uint32_t a_count = 0;
+ uint8_t a_presence = 0;
+ rc = u2fs_get_authentication_result(result, &a_verified, &a_count, &a_presence);
+ if (rc == U2FS_OK) {
+ sv_setiv(verified, a_verified);
+ sv_setuv(counter, a_count);
+ sv_setuv(presence, a_presence);
+ }
+ u2fs_free_auth_res(result);
+ }
+ RETVAL = rc;
+ OUTPUT:
+ RETVAL
--
2.11.0
More information about the pve-devel
mailing list