[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