summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Allbery <eagle@eyrie.org>2016-01-17 19:43:10 -0800
committerRuss Allbery <eagle@eyrie.org>2016-01-17 19:43:10 -0800
commit4b3f858ef567c0d12511e7fea2a56f08f2729635 (patch)
treee1cad1c445669045b47264c8957878352c7adc03
parent7856dc7cc5e16140c0084474fe54338f293bf77e (diff)
parent76f93739a8a933d98b87db9496861dae7de0ae1a (diff)
Imported Upstream version 1.3upstream/1.3
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml16
-rw-r--r--LICENSE27
-rw-r--r--Makefile.am128
-rw-r--r--Makefile.in251
-rw-r--r--NEWS93
-rw-r--r--README40
-rw-r--r--TODO373
-rw-r--r--aclocal.m462
-rwxr-xr-xautogen2
-rwxr-xr-xbuild-aux/ar-lib2
-rwxr-xr-xbuild-aux/compile2
-rwxr-xr-xbuild-aux/depcomp2
-rwxr-xr-xbuild-aux/install-sh373
-rwxr-xr-xbuild-aux/missing2
-rw-r--r--client/wallet-rekey.14
-rw-r--r--client/wallet.125
-rw-r--r--client/wallet.pod23
-rw-r--r--config.h.in15
-rwxr-xr-xconfigure424
-rw-r--r--configure.ac15
-rwxr-xr-xcontrib/commerzbank/wallet-history475
-rwxr-xr-xcontrib/wallet-contacts74
-rw-r--r--contrib/wallet-rekey-periodic.84
-rwxr-xr-xcontrib/wallet-summary2
-rw-r--r--contrib/wallet-summary.84
-rw-r--r--contrib/wallet-unknown-hosts.84
-rw-r--r--docs/design-acl41
-rw-r--r--docs/objects-and-schemes24
-rw-r--r--docs/stanford-naming97
-rw-r--r--m4/clang.m426
-rw-r--r--m4/krb5.m440
-rw-r--r--perl/Build.PL19
-rwxr-xr-xperl/create-ddl3
-rw-r--r--perl/lib/Wallet/ACL.pm126
-rw-r--r--perl/lib/Wallet/ACL/Base.pm28
-rw-r--r--perl/lib/Wallet/ACL/External.pm192
-rw-r--r--perl/lib/Wallet/ACL/Krb5.pm14
-rw-r--r--perl/lib/Wallet/ACL/Krb5/Regex.pm12
-rw-r--r--perl/lib/Wallet/ACL/LDAP/Attribute.pm16
-rw-r--r--perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm123
-rw-r--r--perl/lib/Wallet/ACL/Nested.pm186
-rw-r--r--perl/lib/Wallet/ACL/NetDB.pm14
-rw-r--r--perl/lib/Wallet/ACL/NetDB/Root.pm15
-rw-r--r--perl/lib/Wallet/Admin.pm32
-rw-r--r--perl/lib/Wallet/Config.pm206
-rw-r--r--perl/lib/Wallet/Database.pm16
-rw-r--r--perl/lib/Wallet/Kadmin.pm16
-rw-r--r--perl/lib/Wallet/Kadmin/AD.pm472
-rw-r--r--perl/lib/Wallet/Kadmin/Heimdal.pm18
-rw-r--r--perl/lib/Wallet/Kadmin/MIT.pm23
-rw-r--r--perl/lib/Wallet/Object/Base.pm21
-rw-r--r--perl/lib/Wallet/Object/Duo.pm164
-rw-r--r--perl/lib/Wallet/Object/Duo/LDAPProxy.pm202
-rw-r--r--perl/lib/Wallet/Object/Duo/PAM.pm205
-rw-r--r--perl/lib/Wallet/Object/Duo/RDP.pm204
-rw-r--r--perl/lib/Wallet/Object/Duo/RadiusProxy.pm204
-rw-r--r--perl/lib/Wallet/Object/File.pm16
-rw-r--r--perl/lib/Wallet/Object/Keytab.pm75
-rw-r--r--perl/lib/Wallet/Object/Password.pm224
-rw-r--r--perl/lib/Wallet/Object/WAKeyring.pm16
-rw-r--r--perl/lib/Wallet/Policy/Stanford.pm149
-rw-r--r--perl/lib/Wallet/Report.pm243
-rw-r--r--perl/lib/Wallet/Schema.pm14
-rw-r--r--perl/lib/Wallet/Schema/Result/Acl.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/AclEntry.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/AclHistory.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/AclScheme.pm7
-rw-r--r--perl/lib/Wallet/Schema/Result/Duo.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/Enctype.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/Flag.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/KeytabEnctype.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/KeytabSync.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/Object.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/ObjectHistory.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/SyncTarget.pm2
-rw-r--r--perl/lib/Wallet/Schema/Result/Type.pm2
-rw-r--r--perl/lib/Wallet/Server.pm68
-rw-r--r--perl/sql/Wallet-Schema-0.10-MySQL.sql23
-rw-r--r--perl/sql/Wallet-Schema-0.10-PostgreSQL.sql23
-rw-r--r--perl/sql/Wallet-Schema-0.10-SQLite.sql22
-rw-r--r--perl/sql/wallet-1.3-update-duo.sql9
-rwxr-xr-xperl/t/data/acl-command47
-rwxr-xr-xperl/t/general/acl.t155
-rwxr-xr-xperl/t/general/report.t51
-rwxr-xr-xperl/t/general/server.t10
-rwxr-xr-xperl/t/object/base.t5
-rw-r--r--perl/t/object/duo-ldap.t21
-rw-r--r--perl/t/object/duo-pam.t20
-rw-r--r--perl/t/object/duo-radius.t21
-rw-r--r--perl/t/object/duo-rdp.t20
-rwxr-xr-xperl/t/object/keytab.t26
-rw-r--r--perl/t/object/password.t125
-rwxr-xr-xperl/t/policy/stanford.t329
-rwxr-xr-xperl/t/verifier/external.t35
-rwxr-xr-xperl/t/verifier/ldap-attr.t39
-rwxr-xr-xperl/t/verifier/nested.t84
-rw-r--r--portable/asprintf.c6
-rw-r--r--portable/krb5-extra.c3
-rw-r--r--portable/krb5.h4
-rw-r--r--portable/macros.h3
-rw-r--r--portable/mkstemp.c5
-rw-r--r--portable/reallocarray.c8
-rw-r--r--portable/setenv.c24
-rw-r--r--portable/snprintf.c2
-rw-r--r--portable/strlcat.c51
-rw-r--r--portable/strlcpy.c49
-rw-r--r--portable/system.h4
-rwxr-xr-xserver/keytab-backend5
-rw-r--r--server/keytab-backend.84
-rwxr-xr-xserver/wallet-admin9
-rw-r--r--server/wallet-admin.84
-rwxr-xr-xserver/wallet-backend43
-rw-r--r--server/wallet-backend.828
-rwxr-xr-xserver/wallet-report66
-rw-r--r--server/wallet-report.824
-rw-r--r--tests/TESTS3
-rw-r--r--tests/data/perl.conf5
-rwxr-xr-xtests/perl/module-version-t183
-rw-r--r--tests/portable/asprintf-t.c1
-rw-r--r--tests/portable/snprintf-t.c4
-rw-r--r--tests/portable/strlcat-t.c78
-rw-r--r--tests/portable/strlcat.c2
-rw-r--r--tests/portable/strlcpy-t.c70
-rw-r--r--tests/portable/strlcpy.c2
-rw-r--r--tests/runtests.c204
-rw-r--r--tests/tap/basic.c12
-rw-r--r--tests/tap/basic.h8
-rw-r--r--tests/tap/kerberos.c31
-rw-r--r--tests/tap/kerberos.h8
-rw-r--r--tests/tap/macros.h8
-rw-r--r--tests/tap/messages.c4
-rw-r--r--tests/tap/perl/Test/RRA.pm104
-rw-r--r--tests/tap/perl/Test/RRA/Automake.pm166
-rw-r--r--tests/tap/perl/Test/RRA/Config.pm138
-rw-r--r--tests/tap/perl/Test/RRA/ModuleVersion.pm295
-rw-r--r--tests/tap/process.c9
-rw-r--r--tests/tap/string.h2
-rw-r--r--tests/util/messages-t.c5
-rwxr-xr-xtests/util/xmalloc-t32
-rw-r--r--tests/util/xmalloc.c7
-rw-r--r--util/macros.h4
-rw-r--r--util/messages.c2
-rw-r--r--util/messages.h19
-rw-r--r--util/xmalloc.c5
-rw-r--r--util/xmalloc.h2
146 files changed, 6031 insertions, 2827 deletions
diff --git a/.gitignore b/.gitignore
index 86f4a1c..67dd38b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
/Makefile.in
/aclocal.m4
+/autom4te.cache/
/build-aux/
/client/wallet
/client/wallet-rekey
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..3f60b23
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,16 @@
+sudo: required
+dist: trusty
+
+language: c
+compiler:
+ - gcc
+
+before_install:
+ - sudo apt-get update -qq
+ - sudo apt-get install -y libauthen-sasl-perl libcrypt-generatepassword-perl libdatetime-perl libdatetime-format-sqlite-perl libdbd-sqlite3-perl libdbi-perl libdbix-class-perl libheimdal-kadm5-perl libjson-perl libkrb5-dev libnet-dns-perl libnet-ldap-perl libnet-remctl-perl libperl6-slurp-perl libremctl-dev libsql-translator-perl libtest-minimumversion-perl libtest-pod-perl libtest-strict-perl libtimedate-perl libwebauth-perl perl sqlite3
+env: AUTHOR_TESTING=1
+script: ./autogen && ./configure && make warnings && make check
+
+branches:
+ only:
+ - master
diff --git a/LICENSE b/LICENSE
index 8fdc976..d8b59a1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -10,23 +10,25 @@ Copyright: 2006-2010, 2012-2013
License: Expat
Files: *
-Copyright: 2000-2002, 2004-2014 Russ Allbery <eagle@eyrie.org>
- 2001-2014 The Board of Trustees of the Leland Stanford Junior University
+Copyright: 2000-2002, 2004-2016 Russ Allbery <eagle@eyrie.org>
+ 2001-2015 The Board of Trustees of the Leland Stanford Junior University
+ 2015 Dropbox, Inc.
License: Expat
Files: Makefile.in
-Copyright: 1994-2013 Free Software Foundation, Inc.
+Copyright: 1994-2014 Free Software Foundation, Inc.
2006-2008, 2010, 2013-2014
The Board of Trustees of the Leland Stanford Junior University
+ 2016 Russ Allbery <eagle@eyrie.org>
License: FSF-unlimited and Expat
Files: aclocal.m4
-Copyright: 1996-2013 Free Software Foundation, Inc.
+Copyright: 1996-2015 Free Software Foundation, Inc.
License: FSF-unlimited
Files: build-aux/ar-lib build-aux/compile build-aux/depcomp
build-aux/missing
-Copyright: 1996-2013 Free Software Foundation, Inc.
+Copyright: 1996-2014 Free Software Foundation, Inc.
License: GPL-2+ with Autoconf exception or Expat
Files: build-aux/install-sh
@@ -62,7 +64,7 @@ Files: client/wallet-rekey.1 client/wallet-rekey.pod client/wallet.1
docs/stanford-naming perl/t/data/README tests/HOWTO tests/config/README
Copyright: 2006-2014
The Board of Trustees of the Leland Stanford Junior University
- 2010 Russ Allbery <eagle@eyrie.org>
+ 2010, 2016 Russ Allbery <eagle@eyrie.org>
License: all-permissive
Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty provided the copyright notice and
@@ -75,10 +77,12 @@ License: FSF-configure
This script is free software; the Free Software Foundation gives unlimited
permission to copy, distribute and modify it.
-Files: m4/gssapi.m4 m4/krb5-config.m4 m4/krb5.m4 m4/lib-depends.m4
- m4/lib-pathname.m4 m4/remctl.m4 m4/snprintf.m4 m4/vamacros.m4
+Files: m4/clang.m4 m4/gssapi.m4 m4/krb5-config.m4 m4/krb5.m4
+ m4/lib-depends.m4 m4/lib-pathname.m4 m4/remctl.m4 m4/snprintf.m4
+ m4/vamacros.m4
Copyright: 2005-2014
The Board of Trustees of the Leland Stanford Junior University
+ 2015 Russ Allbery <eagle@eyrie.org>
License: unlimited
This file is free software; the authors give unlimited permission to copy
and/or distribute it, with or without modifications, as long as this
@@ -87,10 +91,8 @@ License: unlimited
Files: portable/asprintf.c portable/dummy.c portable/krb5-extra.c
portable/krb5.h portable/macros.h portable/mkstemp.c
portable/reallocarray.c portable/setenv.c portable/stdbool.h
- portable/strlcat.c portable/strlcpy.c portable/system.h portable/uio.h
- tests/portable/asprintf-t.c tests/portable/mkstemp-t.c
- tests/portable/setenv-t.c tests/portable/strlcat-t.c
- tests/portable/strlcpy-t.c util/macros.h
+ portable/system.h portable/uio.h tests/portable/asprintf-t.c
+ tests/portable/mkstemp-t.c tests/portable/setenv-t.c util/macros.h
Copyright: no copyright notice, see License
License: rra-public-domain
The authors hereby relinquish any claim to any copyright that they may
@@ -116,6 +118,7 @@ Copyright: 1991, 1994-2003 The Internet Software Consortium and Rich Salz
2004-2006 Internet Systems Consortium, Inc.
2008-2010, 2012-2014
The Board of Trustees of the Leland Stanford Junior University
+ 2015 Russ Allbery <eagle@eyrie.org>
License: ISC
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile.am b/Makefile.am
index ccbaed0..6cabc93 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,7 @@
# Automake makefile for wallet.
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2006, 2007, 2008, 2010, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -20,17 +21,18 @@ WALLET_PERL_FLAGS ?=
# builddir != srcdir builds.
PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
perl/lib/Wallet/ACL.pm perl/lib/Wallet/ACL/Base.pm \
- perl/lib/Wallet/ACL/Krb5.pm perl/lib/Wallet/ACL/Krb5/Regex.pm \
- perl/lib/Wallet/ACL/LDAP/Attribute.pm perl/lib/Wallet/ACL/NetDB.pm \
+ perl/lib/Wallet/ACL/External.pm perl/lib/Wallet/ACL/Krb5.pm \
+ perl/lib/Wallet/ACL/Krb5/Regex.pm \
+ perl/lib/Wallet/ACL/LDAP/Attribute.pm \
+ perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm \
+ perl/lib/Wallet/ACL/NetDB.pm perl/lib/Wallet/ACL/Nested.pm \
perl/lib/Wallet/ACL/NetDB/Root.pm perl/lib/Wallet/Admin.pm \
perl/lib/Wallet/Config.pm perl/lib/Wallet/Database.pm \
- perl/lib/Wallet/Kadmin.pm perl/lib/Wallet/Kadmin/Heimdal.pm \
- perl/lib/Wallet/Kadmin/MIT.pm perl/lib/Wallet/Object/Base.pm \
- perl/lib/Wallet/Object/Duo.pm \
- perl/lib/Wallet/Object/Duo/LDAPProxy.pm \
- perl/lib/Wallet/Object/Duo/PAM.pm perl/lib/Wallet/Object/Duo/RDP.pm \
- perl/lib/Wallet/Object/Duo/RadiusProxy.pm \
+ perl/lib/Wallet/Kadmin.pm perl/lib/Wallet/Kadmin/AD.pm \
+ perl/lib/Wallet/Kadmin/Heimdal.pm perl/lib/Wallet/Kadmin/MIT.pm \
+ perl/lib/Wallet/Object/Base.pm perl/lib/Wallet/Object/Duo.pm \
perl/lib/Wallet/Object/File.pm perl/lib/Wallet/Object/Keytab.pm \
+ perl/lib/Wallet/Object/Password.pm \
perl/lib/Wallet/Object/WAKeyring.pm \
perl/lib/Wallet/Policy/Stanford.pm perl/lib/Wallet/Report.pm \
perl/lib/Wallet/Schema.pm perl/lib/Wallet/Server.pm \
@@ -65,8 +67,9 @@ PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
perl/sql/Wallet-Schema-0.09-0.10-SQLite.sql \
perl/sql/Wallet-Schema-0.10-MySQL.sql \
perl/sql/Wallet-Schema-0.10-PostgreSQL.sql \
- perl/sql/Wallet-Schema-0.10-SQLite.sql perl/t/data/README \
- perl/t/data/duo/integration.json \
+ perl/sql/Wallet-Schema-0.10-SQLite.sql \
+ perl/sql/wallet-1.3-update-duo.sql perl/t/data/README \
+ perl/t/data/acl-command perl/t/data/duo/integration.json \
perl/t/data/duo/integration-ldap.json \
perl/t/data/duo/integration-radius.json \
perl/t/data/duo/integration-rdp.json perl/t/data/duo/keys.json \
@@ -79,48 +82,52 @@ PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
perl/t/object/duo.t perl/t/object/duo-ldap.t \
perl/t/object/duo-pam.t perl/t/object/duo-radius.t \
perl/t/object/duo-rdp.t perl/t/object/file.t perl/t/object/keytab.t \
- perl/t/object/wa-keyring.t perl/t/policy/stanford.t \
- perl/t/style/minimum-version.t perl/t/style/strict.t \
- perl/t/util/kadmin.t perl/t/verifier/basic.t \
- perl/t/verifier/ldap-attr.t perl/t/verifier/netdb.t
+ perl/t/object/password.t perl/t/object/wa-keyring.t \
+ perl/t/policy/stanford.t perl/t/style/minimum-version.t \
+ perl/t/style/strict.t perl/t/util/kadmin.t perl/t/verifier/basic.t \
+ perl/t/verifier/external.t perl/t/verifier/ldap-attr.t \
+ perl/t/verifier/nested.t perl/t/verifier/netdb.t
# Directories that have to be created in builddir != srcdir builds before
# copying PERL_FILES over.
PERL_DIRECTORIES = perl perl/lib perl/lib/Wallet perl/lib/Wallet/ACL \
perl/lib/Wallet/ACL/Krb5 perl/lib/Wallet/ACL/LDAP \
- perl/lib/Wallet/ACL/NetDB perl/lib/Wallet/Kadmin \
- perl/lib/Wallet/Object perl/lib/Wallet/Object/Duo \
+ perl/lib/Wallet/ACL/LDAP/Attribute perl/lib/Wallet/ACL/NetDB \
+ perl/lib/Wallet/Kadmin perl/lib/Wallet/Object \
perl/lib/Wallet/Policy perl/lib/Wallet/Schema \
perl/lib/Wallet/Schema/Result perl/sql perl/t perl/t/data \
perl/t/data/duo perl/t/docs perl/t/general perl/t/lib perl/t/object \
perl/t/policy perl/t/style perl/t/util perl/t/verifier
ACLOCAL_AMFLAGS = -I m4
-EXTRA_DIST = .gitignore LICENSE autogen client/wallet.pod \
- client/wallet-rekey.pod config/allow-extract config/keytab \
- config/keytab.acl config/wallet config/wallet-report.acl \
- docs/design contrib/README contrib/convert-srvtab-db \
- contrib/used-principals contrib/wallet-contacts \
- contrib/wallet-rekey-periodic contrib/wallet-rekey-periodic.8 \
- contrib/wallet-summary contrib/wallet-summary.8 \
- contrib/wallet-unknown-hosts contrib/wallet-unknown-hosts.8 \
- docs/design-acl docs/design-api docs/netdb-role-api docs/notes \
- docs/objects-and-schemes docs/setup docs/stanford-naming \
- examples/stanford.conf tests/HOWTO tests/TESTS tests/config/README \
- tests/data/allow-extract tests/data/basic.conf tests/data/cmd-fake \
- tests/data/cmd-wrapper tests/data/fake-data tests/data/fake-kadmin \
- tests/data/fake-keytab tests/data/fake-keytab-2 \
- tests/data/fake-keytab-foreign tests/data/fake-keytab-merge \
- tests/data/fake-keytab-old tests/data/fake-keytab-partial \
- tests/data/fake-keytab-partial-result tests/data/fake-keytab-rekey \
- tests/data/fake-keytab-unknown tests/data/fake-srvtab \
- tests/data/full.conf tests/data/perl.conf tests/data/wallet.conf \
- tests/docs/pod-spelling-t tests/docs/pod-t \
- tests/perl/minimum-version-t tests/perl/strict-t \
- tests/server/admin-t tests/server/backend-t tests/server/keytab-t \
- tests/server/report-t tests/tap/kerberos.sh tests/tap/libtap.sh \
- tests/tap/perl/Test/RRA.pm tests/tap/perl/Test/RRA/Automake.pm \
- tests/tap/perl/Test/RRA/Config.pm tests/tap/remctl.sh \
+EXTRA_DIST = .gitignore .travis.yml LICENSE autogen client/wallet.pod \
+ client/wallet-rekey.pod config/allow-extract config/keytab \
+ config/keytab.acl config/wallet config/wallet-report.acl \
+ docs/design contrib/README contrib/commerzbank/wallet-history \
+ contrib/convert-srvtab-db contrib/used-principals \
+ contrib/wallet-contacts contrib/wallet-rekey-periodic \
+ contrib/wallet-rekey-periodic.8 contrib/wallet-summary \
+ contrib/wallet-summary.8 contrib/wallet-unknown-hosts \
+ contrib/wallet-unknown-hosts.8 docs/design-acl docs/design-api \
+ docs/netdb-role-api docs/notes docs/objects-and-schemes docs/setup \
+ docs/stanford-naming examples/stanford.conf tests/HOWTO tests/TESTS \
+ tests/config/README tests/data/allow-extract tests/data/basic.conf \
+ tests/data/cmd-fake tests/data/cmd-wrapper tests/data/fake-data \
+ tests/data/fake-kadmin tests/data/fake-keytab \
+ tests/data/fake-keytab-2 tests/data/fake-keytab-foreign \
+ tests/data/fake-keytab-merge tests/data/fake-keytab-old \
+ tests/data/fake-keytab-partial \
+ tests/data/fake-keytab-partial-result tests/data/fake-keytab-rekey \
+ tests/data/fake-keytab-unknown tests/data/fake-srvtab \
+ tests/data/full.conf tests/data/perl.conf tests/data/wallet.conf \
+ tests/docs/pod-spelling-t tests/docs/pod-t \
+ tests/perl/minimum-version-t tests/perl/module-version-t \
+ tests/perl/strict-t tests/server/admin-t tests/server/backend-t \
+ tests/server/keytab-t tests/server/report-t tests/tap/kerberos.sh \
+ tests/tap/libtap.sh tests/tap/perl/Test/RRA.pm \
+ tests/tap/perl/Test/RRA/Automake.pm \
+ tests/tap/perl/Test/RRA/Config.pm \
+ tests/tap/perl/Test/RRA/ModuleVersion.pm tests/tap/remctl.sh \
tests/util/xmalloc-t $(PERL_FILES)
# Supporting convenience libraries used by other targets.
@@ -173,22 +180,28 @@ dist_pkgdata_DATA = perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql \
perl/sql/Wallet-Schema-0.09-PostgreSQL.sql \
perl/sql/Wallet-Schema-0.09-SQLite.sql
-# A set of flags for warnings. Add -O because gcc won't find some warnings
+# A set of flags for warnings. Add -O because gcc won't find some warnings
# without optimization turned on. Desirable warnings that can't be turned
# on due to other problems:
#
-# -Wconversion http://bugs.debian.org/488884 (htons warnings)
+# -Wconversion http://bugs.debian.org/488884 (htons warnings)
#
-# Last checked against gcc 4.8.2 (2014-04-12). -D_FORTIFY_SOURCE=2 enables
+# Last checked against gcc 4.8.2 (2014-04-12). -D_FORTIFY_SOURCE=2 enables
# warn_unused_result attribute markings on glibc functions on Linux, which
# catches a few more issues.
-WARNINGS = -g -O -fstrict-overflow -fstrict-aliasing -D_FORTIFY_SOURCE=2 \
- -Wall -Wextra -Wendif-labels -Wformat=2 -Winit-self -Wswitch-enum \
- -Wstrict-overflow=5 -Wfloat-equal -Wdeclaration-after-statement \
- -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align \
- -Wwrite-strings -Wjump-misses-init -Wlogical-op -Wstrict-prototypes \
- -Wold-style-definition -Wmissing-prototypes -Wnormalized=nfc \
- -Wpacked -Wredundant-decls -Wnested-externs -Winline -Wvla -Werror
+if WARNINGS_GCC
+ WARNINGS = -g -O -fstrict-overflow -fstrict-aliasing -D_FORTIFY_SOURCE=2 \
+ -Wall -Wextra -Wendif-labels -Wformat=2 -Winit-self -Wswitch-enum \
+ -Wstrict-overflow=5 -Wmissing-format-attribute -Wfloat-equal \
+ -Wdeclaration-after-statement -Wshadow -Wpointer-arith \
+ -Wbad-function-cast -Wcast-align -Wwrite-strings -Wjump-misses-init \
+ -Wlogical-op -Wstrict-prototypes -Wold-style-definition \
+ -Wmissing-prototypes -Wnormalized=nfc -Wpacked -Wredundant-decls \
+ -Wnested-externs -Winline -Wvla -Werror
+endif
+if WARNINGS_CLANG
+ WARNINGS = -Weverything -Wno-padded
+endif
warnings:
$(MAKE) V=0 CFLAGS='$(WARNINGS)' KRB5_CPPFLAGS='$(KRB5_CPPFLAGS_GCC)'
@@ -247,8 +260,10 @@ clean-local:
cd perl && ./Build realclean ; \
fi
-# Remove the files that we copy over if and only if builddir != srcdir.
+# Remove the Autoconf cache. Remove the files that we copy over if and only
+# if builddir != srcdir.
distclean-local:
+ rm -rf autom4te.cache
set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \
for f in $(PERL_FILES) ; do \
rm -f "$(builddir)/$$f" ; \
@@ -258,8 +273,7 @@ distclean-local:
# The bits below are for the test suite, not for the main package.
check_PROGRAMS = tests/runtests tests/portable/asprintf-t \
tests/portable/mkstemp-t tests/portable/setenv-t \
- tests/portable/snprintf-t tests/portable/strlcat-t \
- tests/portable/strlcpy-t tests/util/messages-krb5-t \
+ tests/portable/snprintf-t tests/util/messages-krb5-t \
tests/util/messages-t tests/util/xmalloc
tests_runtests_CPPFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \
-DBUILD='"$(abs_top_builddir)/tests"'
@@ -283,12 +297,6 @@ tests_portable_setenv_t_LDADD = tests/tap/libtap.a portable/libportable.a
tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \
tests/portable/snprintf.c
tests_portable_snprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a
-tests_portable_strlcat_t_SOURCES = tests/portable/strlcat-t.c \
- tests/portable/strlcat.c
-tests_portable_strlcat_t_LDADD = tests/tap/libtap.a portable/libportable.a
-tests_portable_strlcpy_t_SOURCES = tests/portable/strlcpy-t.c \
- tests/portable/strlcpy.c
-tests_portable_strlcpy_t_LDADD = tests/tap/libtap.a portable/libportable.a
tests_util_messages_krb5_t_CPPFLAGS = $(KRB5_CPPFLAGS)
tests_util_messages_krb5_t_LDFLAGS = $(KRB5_LDFLAGS)
tests_util_messages_krb5_t_LDADD = tests/tap/libtap.a util/libutil.a \
diff --git a/Makefile.in b/Makefile.in
index 0fc38ed..360d0e7 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -17,6 +17,7 @@
# Automake makefile for wallet.
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2006, 2007, 2008, 2010, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -26,7 +27,17 @@
VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
@@ -93,40 +104,21 @@ check_PROGRAMS = tests/runtests$(EXEEXT) \
tests/portable/mkstemp-t$(EXEEXT) \
tests/portable/setenv-t$(EXEEXT) \
tests/portable/snprintf-t$(EXEEXT) \
- tests/portable/strlcat-t$(EXEEXT) \
- tests/portable/strlcpy-t$(EXEEXT) \
tests/util/messages-krb5-t$(EXEEXT) \
tests/util/messages-t$(EXEEXT) tests/util/xmalloc$(EXEEXT)
subdir = .
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
- $(top_srcdir)/configure $(am__configure_deps) \
- $(srcdir)/config.h.in $(top_srcdir)/tests/client/basic-t.in \
- $(top_srcdir)/tests/client/full-t.in \
- $(top_srcdir)/tests/client/prompt-t.in \
- $(top_srcdir)/tests/client/rekey-t.in \
- $(top_srcdir)/portable/setenv.c \
- $(top_srcdir)/portable/strlcpy.c \
- $(top_srcdir)/portable/snprintf.c \
- $(top_srcdir)/portable/reallocarray.c \
- $(top_srcdir)/portable/strlcat.c \
- $(top_srcdir)/portable/asprintf.c \
- $(top_srcdir)/portable/mkstemp.c $(dist_sbin_SCRIPTS) \
- $(top_srcdir)/build-aux/depcomp $(dist_man_MANS) \
- $(dist_pkgdata_DATA) NEWS README TODO build-aux/ar-lib \
- build-aux/compile build-aux/depcomp build-aux/install-sh \
- build-aux/missing $(top_srcdir)/build-aux/ar-lib \
- $(top_srcdir)/build-aux/compile \
- $(top_srcdir)/build-aux/install-sh \
- $(top_srcdir)/build-aux/missing
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/gssapi.m4 \
- $(top_srcdir)/m4/krb5-config.m4 $(top_srcdir)/m4/krb5.m4 \
- $(top_srcdir)/m4/lib-depends.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/clang.m4 \
+ $(top_srcdir)/m4/gssapi.m4 $(top_srcdir)/m4/krb5-config.m4 \
+ $(top_srcdir)/m4/krb5.m4 $(top_srcdir)/m4/lib-depends.m4 \
$(top_srcdir)/m4/lib-pathname.m4 $(top_srcdir)/m4/remctl.m4 \
$(top_srcdir)/m4/snprintf.m4 $(top_srcdir)/m4/vamacros.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+ $(am__configure_deps) $(dist_sbin_SCRIPTS) \
+ $(dist_pkgdata_DATA) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
@@ -222,20 +214,6 @@ tests_portable_snprintf_t_OBJECTS = \
$(am_tests_portable_snprintf_t_OBJECTS)
tests_portable_snprintf_t_DEPENDENCIES = tests/tap/libtap.a \
portable/libportable.a
-am_tests_portable_strlcat_t_OBJECTS = \
- tests/portable/strlcat-t.$(OBJEXT) \
- tests/portable/strlcat.$(OBJEXT)
-tests_portable_strlcat_t_OBJECTS = \
- $(am_tests_portable_strlcat_t_OBJECTS)
-tests_portable_strlcat_t_DEPENDENCIES = tests/tap/libtap.a \
- portable/libportable.a
-am_tests_portable_strlcpy_t_OBJECTS = \
- tests/portable/strlcpy-t.$(OBJEXT) \
- tests/portable/strlcpy.$(OBJEXT)
-tests_portable_strlcpy_t_OBJECTS = \
- $(am_tests_portable_strlcpy_t_OBJECTS)
-tests_portable_strlcpy_t_DEPENDENCIES = tests/tap/libtap.a \
- portable/libportable.a
tests_runtests_SOURCES = tests/runtests.c
tests_runtests_OBJECTS = tests/tests_runtests-runtests.$(OBJEXT)
tests_runtests_LDADD = $(LDADD)
@@ -320,9 +298,7 @@ SOURCES = $(client_libwallet_a_SOURCES) \
$(tests_portable_asprintf_t_SOURCES) \
$(tests_portable_mkstemp_t_SOURCES) \
$(tests_portable_setenv_t_SOURCES) \
- $(tests_portable_snprintf_t_SOURCES) \
- $(tests_portable_strlcat_t_SOURCES) \
- $(tests_portable_strlcpy_t_SOURCES) tests/runtests.c \
+ $(tests_portable_snprintf_t_SOURCES) tests/runtests.c \
tests/util/messages-krb5-t.c tests/util/messages-t.c \
tests/util/xmalloc.c
DIST_SOURCES = $(client_libwallet_a_SOURCES) \
@@ -332,9 +308,7 @@ DIST_SOURCES = $(client_libwallet_a_SOURCES) \
$(tests_portable_asprintf_t_SOURCES) \
$(tests_portable_mkstemp_t_SOURCES) \
$(tests_portable_setenv_t_SOURCES) \
- $(tests_portable_snprintf_t_SOURCES) \
- $(tests_portable_strlcat_t_SOURCES) \
- $(tests_portable_strlcpy_t_SOURCES) tests/runtests.c \
+ $(tests_portable_snprintf_t_SOURCES) tests/runtests.c \
tests/util/messages-krb5-t.c tests/util/messages-t.c \
tests/util/xmalloc.c
am__can_run_installinfo = \
@@ -369,6 +343,23 @@ ETAGS = etags
CTAGS = ctags
CSCOPE = cscope
AM_RECURSIVE_TARGETS = cscope
+am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \
+ $(srcdir)/config.h.in $(top_srcdir)/build-aux/ar-lib \
+ $(top_srcdir)/build-aux/compile \
+ $(top_srcdir)/build-aux/depcomp \
+ $(top_srcdir)/build-aux/install-sh \
+ $(top_srcdir)/build-aux/missing \
+ $(top_srcdir)/portable/asprintf.c \
+ $(top_srcdir)/portable/mkstemp.c \
+ $(top_srcdir)/portable/reallocarray.c \
+ $(top_srcdir)/portable/setenv.c \
+ $(top_srcdir)/portable/snprintf.c \
+ $(top_srcdir)/tests/client/basic-t.in \
+ $(top_srcdir)/tests/client/full-t.in \
+ $(top_srcdir)/tests/client/prompt-t.in \
+ $(top_srcdir)/tests/client/rekey-t.in NEWS README TODO \
+ build-aux/ar-lib build-aux/compile build-aux/depcomp \
+ build-aux/install-sh build-aux/missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
@@ -481,6 +472,7 @@ pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
@@ -496,17 +488,18 @@ top_srcdir = @top_srcdir@
# builddir != srcdir builds.
PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
perl/lib/Wallet/ACL.pm perl/lib/Wallet/ACL/Base.pm \
- perl/lib/Wallet/ACL/Krb5.pm perl/lib/Wallet/ACL/Krb5/Regex.pm \
- perl/lib/Wallet/ACL/LDAP/Attribute.pm perl/lib/Wallet/ACL/NetDB.pm \
+ perl/lib/Wallet/ACL/External.pm perl/lib/Wallet/ACL/Krb5.pm \
+ perl/lib/Wallet/ACL/Krb5/Regex.pm \
+ perl/lib/Wallet/ACL/LDAP/Attribute.pm \
+ perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm \
+ perl/lib/Wallet/ACL/NetDB.pm perl/lib/Wallet/ACL/Nested.pm \
perl/lib/Wallet/ACL/NetDB/Root.pm perl/lib/Wallet/Admin.pm \
perl/lib/Wallet/Config.pm perl/lib/Wallet/Database.pm \
- perl/lib/Wallet/Kadmin.pm perl/lib/Wallet/Kadmin/Heimdal.pm \
- perl/lib/Wallet/Kadmin/MIT.pm perl/lib/Wallet/Object/Base.pm \
- perl/lib/Wallet/Object/Duo.pm \
- perl/lib/Wallet/Object/Duo/LDAPProxy.pm \
- perl/lib/Wallet/Object/Duo/PAM.pm perl/lib/Wallet/Object/Duo/RDP.pm \
- perl/lib/Wallet/Object/Duo/RadiusProxy.pm \
+ perl/lib/Wallet/Kadmin.pm perl/lib/Wallet/Kadmin/AD.pm \
+ perl/lib/Wallet/Kadmin/Heimdal.pm perl/lib/Wallet/Kadmin/MIT.pm \
+ perl/lib/Wallet/Object/Base.pm perl/lib/Wallet/Object/Duo.pm \
perl/lib/Wallet/Object/File.pm perl/lib/Wallet/Object/Keytab.pm \
+ perl/lib/Wallet/Object/Password.pm \
perl/lib/Wallet/Object/WAKeyring.pm \
perl/lib/Wallet/Policy/Stanford.pm perl/lib/Wallet/Report.pm \
perl/lib/Wallet/Schema.pm perl/lib/Wallet/Server.pm \
@@ -541,8 +534,9 @@ PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
perl/sql/Wallet-Schema-0.09-0.10-SQLite.sql \
perl/sql/Wallet-Schema-0.10-MySQL.sql \
perl/sql/Wallet-Schema-0.10-PostgreSQL.sql \
- perl/sql/Wallet-Schema-0.10-SQLite.sql perl/t/data/README \
- perl/t/data/duo/integration.json \
+ perl/sql/Wallet-Schema-0.10-SQLite.sql \
+ perl/sql/wallet-1.3-update-duo.sql perl/t/data/README \
+ perl/t/data/acl-command perl/t/data/duo/integration.json \
perl/t/data/duo/integration-ldap.json \
perl/t/data/duo/integration-radius.json \
perl/t/data/duo/integration-rdp.json perl/t/data/duo/keys.json \
@@ -555,49 +549,53 @@ PERL_FILES = perl/Build.PL perl/MANIFEST perl/MANIFEST.SKIP perl/create-ddl \
perl/t/object/duo.t perl/t/object/duo-ldap.t \
perl/t/object/duo-pam.t perl/t/object/duo-radius.t \
perl/t/object/duo-rdp.t perl/t/object/file.t perl/t/object/keytab.t \
- perl/t/object/wa-keyring.t perl/t/policy/stanford.t \
- perl/t/style/minimum-version.t perl/t/style/strict.t \
- perl/t/util/kadmin.t perl/t/verifier/basic.t \
- perl/t/verifier/ldap-attr.t perl/t/verifier/netdb.t
+ perl/t/object/password.t perl/t/object/wa-keyring.t \
+ perl/t/policy/stanford.t perl/t/style/minimum-version.t \
+ perl/t/style/strict.t perl/t/util/kadmin.t perl/t/verifier/basic.t \
+ perl/t/verifier/external.t perl/t/verifier/ldap-attr.t \
+ perl/t/verifier/nested.t perl/t/verifier/netdb.t
# Directories that have to be created in builddir != srcdir builds before
# copying PERL_FILES over.
PERL_DIRECTORIES = perl perl/lib perl/lib/Wallet perl/lib/Wallet/ACL \
perl/lib/Wallet/ACL/Krb5 perl/lib/Wallet/ACL/LDAP \
- perl/lib/Wallet/ACL/NetDB perl/lib/Wallet/Kadmin \
- perl/lib/Wallet/Object perl/lib/Wallet/Object/Duo \
+ perl/lib/Wallet/ACL/LDAP/Attribute perl/lib/Wallet/ACL/NetDB \
+ perl/lib/Wallet/Kadmin perl/lib/Wallet/Object \
perl/lib/Wallet/Policy perl/lib/Wallet/Schema \
perl/lib/Wallet/Schema/Result perl/sql perl/t perl/t/data \
perl/t/data/duo perl/t/docs perl/t/general perl/t/lib perl/t/object \
perl/t/policy perl/t/style perl/t/util perl/t/verifier
ACLOCAL_AMFLAGS = -I m4
-EXTRA_DIST = .gitignore LICENSE autogen client/wallet.pod \
- client/wallet-rekey.pod config/allow-extract config/keytab \
- config/keytab.acl config/wallet config/wallet-report.acl \
- docs/design contrib/README contrib/convert-srvtab-db \
- contrib/used-principals contrib/wallet-contacts \
- contrib/wallet-rekey-periodic contrib/wallet-rekey-periodic.8 \
- contrib/wallet-summary contrib/wallet-summary.8 \
- contrib/wallet-unknown-hosts contrib/wallet-unknown-hosts.8 \
- docs/design-acl docs/design-api docs/netdb-role-api docs/notes \
- docs/objects-and-schemes docs/setup docs/stanford-naming \
- examples/stanford.conf tests/HOWTO tests/TESTS tests/config/README \
- tests/data/allow-extract tests/data/basic.conf tests/data/cmd-fake \
- tests/data/cmd-wrapper tests/data/fake-data tests/data/fake-kadmin \
- tests/data/fake-keytab tests/data/fake-keytab-2 \
- tests/data/fake-keytab-foreign tests/data/fake-keytab-merge \
- tests/data/fake-keytab-old tests/data/fake-keytab-partial \
- tests/data/fake-keytab-partial-result tests/data/fake-keytab-rekey \
- tests/data/fake-keytab-unknown tests/data/fake-srvtab \
- tests/data/full.conf tests/data/perl.conf tests/data/wallet.conf \
- tests/docs/pod-spelling-t tests/docs/pod-t \
- tests/perl/minimum-version-t tests/perl/strict-t \
- tests/server/admin-t tests/server/backend-t tests/server/keytab-t \
- tests/server/report-t tests/tap/kerberos.sh tests/tap/libtap.sh \
- tests/tap/perl/Test/RRA.pm tests/tap/perl/Test/RRA/Automake.pm \
- tests/tap/perl/Test/RRA/Config.pm tests/tap/remctl.sh \
+EXTRA_DIST = .gitignore .travis.yml LICENSE autogen client/wallet.pod \
+ client/wallet-rekey.pod config/allow-extract config/keytab \
+ config/keytab.acl config/wallet config/wallet-report.acl \
+ docs/design contrib/README contrib/commerzbank/wallet-history \
+ contrib/convert-srvtab-db contrib/used-principals \
+ contrib/wallet-contacts contrib/wallet-rekey-periodic \
+ contrib/wallet-rekey-periodic.8 contrib/wallet-summary \
+ contrib/wallet-summary.8 contrib/wallet-unknown-hosts \
+ contrib/wallet-unknown-hosts.8 docs/design-acl docs/design-api \
+ docs/netdb-role-api docs/notes docs/objects-and-schemes docs/setup \
+ docs/stanford-naming examples/stanford.conf tests/HOWTO tests/TESTS \
+ tests/config/README tests/data/allow-extract tests/data/basic.conf \
+ tests/data/cmd-fake tests/data/cmd-wrapper tests/data/fake-data \
+ tests/data/fake-kadmin tests/data/fake-keytab \
+ tests/data/fake-keytab-2 tests/data/fake-keytab-foreign \
+ tests/data/fake-keytab-merge tests/data/fake-keytab-old \
+ tests/data/fake-keytab-partial \
+ tests/data/fake-keytab-partial-result tests/data/fake-keytab-rekey \
+ tests/data/fake-keytab-unknown tests/data/fake-srvtab \
+ tests/data/full.conf tests/data/perl.conf tests/data/wallet.conf \
+ tests/docs/pod-spelling-t tests/docs/pod-t \
+ tests/perl/minimum-version-t tests/perl/module-version-t \
+ tests/perl/strict-t tests/server/admin-t tests/server/backend-t \
+ tests/server/keytab-t tests/server/report-t tests/tap/kerberos.sh \
+ tests/tap/libtap.sh tests/tap/perl/Test/RRA.pm \
+ tests/tap/perl/Test/RRA/Automake.pm \
+ tests/tap/perl/Test/RRA/Config.pm \
+ tests/tap/perl/Test/RRA/ModuleVersion.pm tests/tap/remctl.sh \
tests/util/xmalloc-t $(PERL_FILES)
@@ -655,23 +653,25 @@ dist_pkgdata_DATA = perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql \
perl/sql/Wallet-Schema-0.09-PostgreSQL.sql \
perl/sql/Wallet-Schema-0.09-SQLite.sql
+@WARNINGS_CLANG_TRUE@WARNINGS = -Weverything -Wno-padded
-# A set of flags for warnings. Add -O because gcc won't find some warnings
+# A set of flags for warnings. Add -O because gcc won't find some warnings
# without optimization turned on. Desirable warnings that can't be turned
# on due to other problems:
#
-# -Wconversion http://bugs.debian.org/488884 (htons warnings)
+# -Wconversion http://bugs.debian.org/488884 (htons warnings)
#
-# Last checked against gcc 4.8.2 (2014-04-12). -D_FORTIFY_SOURCE=2 enables
+# Last checked against gcc 4.8.2 (2014-04-12). -D_FORTIFY_SOURCE=2 enables
# warn_unused_result attribute markings on glibc functions on Linux, which
# catches a few more issues.
-WARNINGS = -g -O -fstrict-overflow -fstrict-aliasing -D_FORTIFY_SOURCE=2 \
- -Wall -Wextra -Wendif-labels -Wformat=2 -Winit-self -Wswitch-enum \
- -Wstrict-overflow=5 -Wfloat-equal -Wdeclaration-after-statement \
- -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align \
- -Wwrite-strings -Wjump-misses-init -Wlogical-op -Wstrict-prototypes \
- -Wold-style-definition -Wmissing-prototypes -Wnormalized=nfc \
- -Wpacked -Wredundant-decls -Wnested-externs -Winline -Wvla -Werror
+@WARNINGS_GCC_TRUE@WARNINGS = -g -O -fstrict-overflow -fstrict-aliasing -D_FORTIFY_SOURCE=2 \
+@WARNINGS_GCC_TRUE@ -Wall -Wextra -Wendif-labels -Wformat=2 -Winit-self -Wswitch-enum \
+@WARNINGS_GCC_TRUE@ -Wstrict-overflow=5 -Wmissing-format-attribute -Wfloat-equal \
+@WARNINGS_GCC_TRUE@ -Wdeclaration-after-statement -Wshadow -Wpointer-arith \
+@WARNINGS_GCC_TRUE@ -Wbad-function-cast -Wcast-align -Wwrite-strings -Wjump-misses-init \
+@WARNINGS_GCC_TRUE@ -Wlogical-op -Wstrict-prototypes -Wold-style-definition \
+@WARNINGS_GCC_TRUE@ -Wmissing-prototypes -Wnormalized=nfc -Wpacked -Wredundant-decls \
+@WARNINGS_GCC_TRUE@ -Wnested-externs -Winline -Wvla -Werror
# Remove some additional files.
@@ -712,14 +712,6 @@ tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \
tests/portable/snprintf.c
tests_portable_snprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a
-tests_portable_strlcat_t_SOURCES = tests/portable/strlcat-t.c \
- tests/portable/strlcat.c
-
-tests_portable_strlcat_t_LDADD = tests/tap/libtap.a portable/libportable.a
-tests_portable_strlcpy_t_SOURCES = tests/portable/strlcpy-t.c \
- tests/portable/strlcpy.c
-
-tests_portable_strlcpy_t_LDADD = tests/tap/libtap.a portable/libportable.a
tests_util_messages_krb5_t_CPPFLAGS = $(KRB5_CPPFLAGS)
tests_util_messages_krb5_t_LDFLAGS = $(KRB5_LDFLAGS)
tests_util_messages_krb5_t_LDADD = tests/tap/libtap.a util/libutil.a \
@@ -749,7 +741,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -968,22 +959,6 @@ tests/portable/snprintf.$(OBJEXT): tests/portable/$(am__dirstamp) \
tests/portable/snprintf-t$(EXEEXT): $(tests_portable_snprintf_t_OBJECTS) $(tests_portable_snprintf_t_DEPENDENCIES) $(EXTRA_tests_portable_snprintf_t_DEPENDENCIES) tests/portable/$(am__dirstamp)
@rm -f tests/portable/snprintf-t$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(tests_portable_snprintf_t_OBJECTS) $(tests_portable_snprintf_t_LDADD) $(LIBS)
-tests/portable/strlcat-t.$(OBJEXT): tests/portable/$(am__dirstamp) \
- tests/portable/$(DEPDIR)/$(am__dirstamp)
-tests/portable/strlcat.$(OBJEXT): tests/portable/$(am__dirstamp) \
- tests/portable/$(DEPDIR)/$(am__dirstamp)
-
-tests/portable/strlcat-t$(EXEEXT): $(tests_portable_strlcat_t_OBJECTS) $(tests_portable_strlcat_t_DEPENDENCIES) $(EXTRA_tests_portable_strlcat_t_DEPENDENCIES) tests/portable/$(am__dirstamp)
- @rm -f tests/portable/strlcat-t$(EXEEXT)
- $(AM_V_CCLD)$(LINK) $(tests_portable_strlcat_t_OBJECTS) $(tests_portable_strlcat_t_LDADD) $(LIBS)
-tests/portable/strlcpy-t.$(OBJEXT): tests/portable/$(am__dirstamp) \
- tests/portable/$(DEPDIR)/$(am__dirstamp)
-tests/portable/strlcpy.$(OBJEXT): tests/portable/$(am__dirstamp) \
- tests/portable/$(DEPDIR)/$(am__dirstamp)
-
-tests/portable/strlcpy-t$(EXEEXT): $(tests_portable_strlcpy_t_OBJECTS) $(tests_portable_strlcpy_t_DEPENDENCIES) $(EXTRA_tests_portable_strlcpy_t_DEPENDENCIES) tests/portable/$(am__dirstamp)
- @rm -f tests/portable/strlcpy-t$(EXEEXT)
- $(AM_V_CCLD)$(LINK) $(tests_portable_strlcpy_t_OBJECTS) $(tests_portable_strlcpy_t_LDADD) $(LIBS)
tests/$(am__dirstamp):
@$(MKDIR_P) tests
@: > tests/$(am__dirstamp)
@@ -1085,8 +1060,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@portable/$(DEPDIR)/reallocarray.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@portable/$(DEPDIR)/setenv.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@portable/$(DEPDIR)/snprintf.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@portable/$(DEPDIR)/strlcat.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@portable/$(DEPDIR)/strlcpy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/tests_runtests-runtests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/asprintf-t.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/asprintf.Po@am__quote@
@@ -1096,10 +1069,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/setenv.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/snprintf-t.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/snprintf.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/strlcat-t.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/strlcat.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/strlcpy-t.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@tests/portable/$(DEPDIR)/strlcpy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/tap/$(DEPDIR)/tests_tap_libtap_a-basic.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/tap/$(DEPDIR)/tests_tap_libtap_a-kerberos.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@tests/tap/$(DEPDIR)/tests_tap_libtap_a-messages.Po@am__quote@
@@ -1635,15 +1604,15 @@ dist-xz: distdir
$(am__post_remove_distdir)
dist-tarZ: distdir
- @echo WARNING: "Support for shar distribution archives is" \
- "deprecated." >&2
+ @echo WARNING: "Support for distribution archives compressed with" \
+ "legacy program 'compress' is deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__post_remove_distdir)
dist-shar: distdir
- @echo WARNING: "Support for distribution archives compressed with" \
- "legacy program 'compress' is deprecated." >&2
+ @echo WARNING: "Support for shar distribution archives is" \
+ "deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
$(am__post_remove_distdir)
@@ -1679,17 +1648,17 @@ distcheck: dist
esac
chmod -R a-w $(distdir)
chmod u+w $(distdir)
- mkdir $(distdir)/_build $(distdir)/_inst
+ mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
chmod a-w $(distdir)
test -d $(distdir)/_build || exit 0; \
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& am__cwd=`pwd` \
- && $(am__cd) $(distdir)/_build \
- && ../configure \
+ && $(am__cd) $(distdir)/_build/sub \
+ && ../../configure \
$(AM_DISTCHECK_CONFIGURE_FLAGS) \
$(DISTCHECK_CONFIGURE_FLAGS) \
- --srcdir=.. --prefix="$$dc_install_base" \
+ --srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
@@ -1884,6 +1853,8 @@ uninstall-man: uninstall-man1 uninstall-man8
uninstall-dist_sbinSCRIPTS uninstall-man uninstall-man1 \
uninstall-man8
+.PRECIOUS: Makefile
+
# These variables exist only for the use of the Debian packaging and similar
# situations and aren't normally set. We want to honor them if they're set
@@ -1941,8 +1912,10 @@ clean-local:
cd perl && ./Build realclean ; \
fi
-# Remove the files that we copy over if and only if builddir != srcdir.
+# Remove the Autoconf cache. Remove the files that we copy over if and only
+# if builddir != srcdir.
distclean-local:
+ rm -rf autom4te.cache
set -e; if [ x"$(builddir)" != x"$(srcdir)" ] ; then \
for f in $(PERL_FILES) ; do \
rm -f "$(builddir)/$$f" ; \
diff --git a/NEWS b/NEWS
index 272b109..e299bc1 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,98 @@
User-Visible wallet Changes
+wallet 1.3 (2016-01-17)
+
+ This release adds initial, experimental support for using Active
+ Directory as the KDC for keytab creation. The interface to Active
+ Directory uses a combination of direct LDAP queries and the msktutil
+ utility. This version does not support the wallet unchanging flag.
+ Unchanging requires that a keytab be retrieved without changing the
+ password/kvno which is not supported by msktutil. Active Directory
+ can be selected by setting KEYTAB_KRBTYPE to AD in the wallet
+ configuration. Multiple other configuration options must also be set;
+ see Wallet::Config for more information and README for the additional
+ Perl modules required. Thanks to Bill MacAllister for the
+ implementation.
+
+ A new ACL type, nested (Wallet::ACL::Nested), is now supported. The
+ identifier of this ACL names another ACL, and access is granted if
+ that ACL would grant access. This lets one combine multiple other
+ ACLs and apply the union to an object. To enable this ACL type for an
+ existing wallet database, use wallet-admin to register the new
+ verifier.
+
+ A new ACL type, external (Wallet::ACL::External), is now supported.
+ This ACL runs an external command to check if access is allowed, and
+ passes the principal, type and name of the object, and the ACL
+ identifier to that command. To enable this ACL type for an existing
+ wallet database, use wallet-admin to register the new verifier.
+
+ A new variation on the ldap-attr ACL type, ldap-attr-root
+ (Wallet::ACL::LDAP::Attribute::Root), is now supported. This is
+ similar to netdb-root (compared to netdb): the authenticated principal
+ must end in /root, and the LDAP entry checked will be for the same
+ principal without the /root component. This is useful for limiting
+ access to certain privileged objects to Kerberos root instances. To
+ enable this ACL type for an existing wallet database, use wallet-admin
+ to register the new verifier.
+
+ A new object type, password (Wallet::Object::Password), is now
+ supported. This is a subclass of the file object that will randomly
+ generate content for the object if you do a get before storing any
+ content inside it. To enable this object type for an existing
+ database, use wallet-admin to register the new object.
+
+ Add a new command to wallet-backend, update. This will update the
+ contents of an object before running a get on it, and is only valid
+ for objects that can automatically get new content, such as keytab and
+ password objects. A keytab will get a new kvno regardless of the
+ unchanging flag if called with update. In a future release get will
+ be changed to never update a keytab, and the unchanging flag will be
+ ignored. Please start moving to use get or update as the situation
+ warrants.
+
+ Add an acl replace command, to change all objects owned by one ACL to
+ be owned by another. This currently only handles owner, not any of
+ the more specific ACLs.
+
+ All ACL operations now refer to the ACL by name rather than ID.
+
+ Add a report for unstored objects to wallet-report, and cleaned up the
+ help for the existing unused report that implied it showed unstored as
+ well as unused.
+
+ Add reports that list all object types (types) and all ACL schemes
+ (schemes) currently registered in the wallet database.
+
+ Add a report of all ACLs that nest a given ACL. This requires some
+ additional local configuration (and probably some code). See
+ Wallet::Config for more information.
+
+ Took contributions from Commerzbank AG to improve wallet history. Add
+ a command to dump all object history for searching on to
+ wallet-report, and add a new script for more detailed object history
+ operations to the contrib directory.
+
+ Displays of ACLs and ACL entries are now sorted correctly.
+
+ The versions of all of the wallet Perl modules now match the overall
+ package version except for Wallet::Schema, which is used to version
+ the database schema.
+
+ Update to rra-c-util 5.10:
+
+ * Add missing va_end to xasprintf implementation.
+ * Fix Perl test suite framework for new Automake relative paths.
+ * Improve portability to Kerberos included in Solaris 10.
+ * Use appropriate warning flags with Clang (currently not warning clean).
+
+ Update to C TAP Harness 3.4:
+
+ * Fix segfault in runtests with an empty test list.
+ * Display verbose test results with -v or C_TAP_VERBOSE.
+ * Test infrastructure builds cleanly with Clang warnings.
+ * Support comments and blank lines in test lists.
+
wallet 1.2 (2014-12-08)
The duo object type has been split into several sub-types, each for a
diff --git a/README b/README
index f21b9ad..9dcd711 100644
--- a/README
+++ b/README
@@ -1,12 +1,13 @@
- wallet release 1.2
+ wallet release 1.3
(secure data management system)
Written by Russ Allbery <eagle@eyrie.org>
- Copyright 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014 The Board of
- Trustees of the Leland Stanford Junior University. This software is
- distributed under a BSD-style license. Please see the section LICENSE
- below for more information.
+ Copyright 2014, 2016 Russ Allbery <eagle@eyrie.org>. Copyright 2006,
+ 2007, 2008, 2009, 2010, 2012, 2013, 2014 The Board of Trustees of the
+ Leland Stanford Junior University. This software is distributed under a
+ BSD-style license. Please see the section LICENSE below for more
+ information.
BLURB
@@ -91,12 +92,15 @@ REQUIREMENTS
on CPAN for older versions.
The keytab support in the wallet server supports either Heimdal or MIT
- Kerberos KDCs. The Heimdal support requires the Heimdal::Kadm5 Perl
- module. The MIT Kerberos support requires the MIT Kerberos kadmin
- client program be installed. In either case, wallet also requires that
- the wallet server have a keytab for a principal with appropriate access
- to create, modify, and delete principals from the KDC (as configured in
- kadm5.acl on an MIT Kerberos KDC).
+ Kerberos KDCs and has exeprimental support for Active Directory. The
+ Heimdal support requires the Heimdal::Kadm5 Perl module. The MIT
+ Kerberos support requires the MIT Kerberos kadmin client program be
+ installed. The Active Directory support requires the Net::LDAP,
+ Authen::SASL, and IPC::Run Perl modules and the msktutil client program.
+ In all cases, wallet also requires that the wallet server have a keytab
+ for a principal with appropriate access to create, modify, and delete
+ principals from the KDC (as configured in kadm5.acl on an MIT Kerberos
+ KDC).
To support the unchanging flag on keytab objects with an MIT Kerberos
KDC, the Net::Remctl Perl module (shipped with remctl) must be installed
@@ -109,7 +113,10 @@ REQUIREMENTS
WebAuth Perl module from WebAuth 4.4.0 or later.
The Duo integration object support in the wallet server requires the
- Net::Duo Perl module.
+ Net::Duo, JSON, and Perl6::Slurp Perl modules.
+
+ The password object support in the wallet server requires the
+ Crypt::GeneratePassword Perl module.
To support the LDAP attribute ACL verifier, the Authen::SASL and
Net::LDAP Perl modules must be installed on the server. This verifier
@@ -336,14 +343,19 @@ THANKS
security models.
To Jon Robertson for the refactoring of Wallet::Kadmin, Heimdal support,
- many of the wallet server-side reports, and the initial wallet-rekey
- implementation.
+ many of the wallet server-side reports, the initial wallet-rekey
+ implementation, and lots of work on object and ACL types including
+ nested ACLs.
+
+ To Bill MacAllister for Wallet::Kadmin::AD and the implementation of
+ keytab object types backed by Active Directory.
LICENSE
The wallet distribution as a whole is covered by the following copyright
statement and license:
+ Copyright 2014, 2016 Russ Allbery <eagle@eyrie.org>
Copyright 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014
The Board of Trustees of the Leland Stanford Junior University
diff --git a/TODO b/TODO
index 4babbca..18b68eb 100644
--- a/TODO
+++ b/TODO
@@ -2,290 +2,265 @@
Client:
- * KERB-94: Handle duplicate kvnos in a newly returned keytab and an
- existing keytab (such as when downloading an unchanging keytab and
- merging it into an existing one) in some reasonable fashion.
+ * Handle duplicate kvnos in a newly returned keytab and an existing
+ keytab (such as when downloading an unchanging keytab and merging it
+ into an existing one) in some reasonable fashion.
- * KERB-90: Support removing old kvnos from a merged keytab (similar to
- kadmin ktremove old).
+ * Support removing old kvnos from a merged keytab (similar to kadmin
+ ktremove old).
- * KERB-88: When reading configuration from krb5.conf, we should first try
- to determine our principal from any existing Kerberos ticket cache
- (after obtaining tickets if -u was given) and extract the realm from
- that principal, using it as the default realm when reading
- configuration information.
+ * When reading configuration from krb5.conf, we should first try to
+ determine our principal from any existing Kerberos ticket cache (after
+ obtaining tickets if -u was given) and extract the realm from that
+ principal, using it as the default realm when reading configuration
+ information.
- * KERB-89: Add readline support to the wallet client to make it easier to
- issue multiple commands.
+ * Add readline support to the wallet client to make it easier to issue
+ multiple commands.
- * KERB-115: Support authenticating with a keytab.
+ * Support authenticating with a keytab.
- * KERB-97: When obtaining tickets in the wallet client with -u, directly
- obtain the service ticket we're going to use for remctl.
+ * When obtaining tickets in the wallet client with -u, directly obtain
+ the service ticket we're going to use for remctl.
- * KERB-95: Provide a way to refresh a file object if and only if what's
- stored on the server is different than what's on disk. This will
- require server support as well for returning the checksum of a file.
+ * Provide a way to refresh a file object if and only if what's stored on
+ the server is different than what's on disk. This will require server
+ support as well for returning the checksum of a file.
- * KERB-104: Incorporate the wallet-rekey-periodic script (currently in
- contrib) into the package and teach it how to ignore foreign
- credentials.
+ * Incorporate the wallet-rekey-periodic script (currently in contrib)
+ into the package and teach it how to ignore foreign credentials.
Server Interface:
- * KERB-126: Provide a way to get history for deleted objects and ACLs.
+ * Provide a way to get history for deleted objects and ACLs.
- * KERB-66: Provide an interface to mass-change all instances of one ACL
- to another.
+ * Provide an interface to mass-change all instances of one ACL to
+ another. (Owner changes are currently supported, but not the other
+ ACLs.)
- * KERB-96: Add help functions to wallet-backend, wallet-report, and
- wallet-admin listing the commands.
+ * Add help functions to wallet-backend and wallet-admin listing the
+ commands.
- * KERB-52: Catch exceptions on object creation in wallet-backend so that
- we can log those as well.
+ * Catch exceptions on object creation in wallet-backend so that we can
+ log those as well.
- * KERB-114: Provide a way to list all objects for which the connecting
- user has ACLs.
+ * Provide a way to list all objects for which the connecting user has
+ ACLs.
- * KERB-101: Support limiting returned history information by timestamp.
+ * Support limiting returned history information by timestamp.
- * KERB-128: Provide a REST implementation of the wallet server.
+ * Provide a REST implementation of the wallet server.
- * KERB-79: Provide a CGI implementation of the wallet server.
+ * Provide a CGI implementation of the wallet server.
- * KERB-111: Support setting flags and attributes on autocreate. In
- general, work out a Wallet::Object::Template Perl object that I can
- return that specifies things other than just the ACL.
+ * Support setting flags and attributes on autocreate. In general, work
+ out a Wallet::Object::Template Perl object that I can return that
+ specifies things other than just the ACL.
- * KERB-93: Remove the hard-coded ADMIN ACL in the server with something
- more configurable, perhaps a global ACL table or something.
+ * Remove the hard-coded ADMIN ACL in the server with something more
+ configurable, perhaps a global ACL table or something.
- * KERB-68: Support leap-of-faith keying of systems by registering an
- object for one-time download (ideally from a specific IP address) and
- then allowing that object to be downloaded anonymously from that IP.
- Relies on support for Kerberos anonymous authentication.
+ * Support leap-of-faith keying of systems by registering an object for
+ one-time download (ideally from a specific IP address) and then
+ allowing that object to be downloaded anonymously from that IP. Relies
+ on support for Kerberos anonymous authentication.
- * KERB-84: Split "get" and "update" in semantics, and only do keytab
- rekeying on update. "get" would not be permitted unless the keytab was
- flagged as unchanging, and update would still change even an unchanging
- keytab (maybe). Or, alternately, maybe we allow get of any keytab?
- Requires more thought.
+ * Split "get" and "update" in semantics, and only do keytab rekeying on
+ update. "get" would not be permitted unless the keytab was flagged as
+ unchanging, and update would still change even an unchanging keytab
+ (maybe). Or, alternately, maybe we allow get of any keytab? Requires
+ more thought.
- * KERB-118: Add command to list available types and schemes.
+ * Add a mechanism to automate owner updates based on default_owner.
- * KERB-75: Add a mechanism to automate owner updates based on
- default_owner.
+ * Partially merge create and autocreate. create and autocreate should do
+ the same thing provided there is an autocreation configuration
+ available. If not, autocreate should fail and create should fall back
+ on checking for ADMIN privileges.
- * KERB-64: Partially merge create and autocreate. create and autocreate
- should do the same thing provided there is an autocreation
- configuration available. If not, autocreate should fail and create
- should fall back on checking for ADMIN privileges.
+ * Rewrite server backends to use Net::Remctl::Backend.
- * KERB-116: Support file object renaming.
+ * Merge the Wallet::Logger support written by Commerzbank AG: create a
+ new class that handles logging, probably based on Log::Log4perl, and
+ add logging points to all of the core classes.
- * KERB-131: Rewrite server backends to use Net::Remctl::Backend.
-
- * KERB-132: Merge the Wallet::Logger support written by Commerzbank AG:
- create a new class that handles logging, probably based on
- Log::Log4perl, and add logging points to all of the core classes.
-
- * KERB-133: Support an authorization hook to determine whether or not to
- permit autocreate. One requested example feature is to limit
- autocreate of keytab objects to certain hosts involved in deployment.
- It should be possible to write a hook that takes the information about
- what object is being autocreated and can accept or decline.
+ * Support an authorization hook to determine whether or not to permit
+ autocreate. One requested example feature is to limit autocreate of
+ keytab objects to certain hosts involved in deployment. It should be
+ possible to write a hook that takes the information about what object
+ is being autocreated and can accept or decline.
ACLs:
- * KERB-119: Error messages from ACL operations should refer to the ACLs
- by name instead of by ID.
-
- * KERB-121: Write the PTS ACL verifier.
+ * Error messages from ACL operations should refer to the ACLs by name
+ instead of by ID.
- * KERB-123: Rename Wallet::ACL::* to Wallet::Verifier::*. Add
- Wallet::ACL as a generic interface with Wallet::ACL::Database and
- Wallet::ACL::List implementations (or some similar name) so that we can
- create and check an ACL without having to write it into the database.
- Redo default ACL creation using that functionality.
+ * Write the PTS ACL verifier.
- * KERB-67: Pass a reference to the object for which the ACL is
- interpreted to the ACL API so that ACL APIs can make more complex
- decisions.
+ * Rename Wallet::ACL::* to Wallet::Verifier::*. Add Wallet::ACL as a
+ generic interface with Wallet::ACL::Database and Wallet::ACL::List
+ implementations (or some similar name) so that we can create and check
+ an ACL without having to write it into the database. Redo default ACL
+ creation using that functionality.
- * KERB-109: A group-in-groups ACL schema.
+ * Pass a reference to the object for which the ACL is interpreted to the
+ ACL API so that ACL APIs can make more complex decisions.
- * KERB-113: Provide an API for verifiers to syntax-check the values
- before an ACL is set and implement syntax checking for the krb5 and
- ldap-attr verifiers.
+ * Provide an API for verifiers to syntax-check the values before an ACL
+ is set and implement syntax checking for the krb5 and ldap-attr
+ verifiers.
- * KERB-60: Investigate how best to support client authentication using
- anonymous PKINIT for things like initial system keying.
+ * Investigate how best to support client authentication using anonymous
+ PKINIT for things like initial system keying.
- * KERB-72: Generalize the current NetDB ACL type to allow a generic
- remctl query for whether a particular user is authorized to create
- host-based objects for a particular host.
+ * Generalize the current NetDB ACL type to allow a generic remctl query
+ for whether a particular user is authorized to create host-based
+ objects for a particular host.
- * KERB-78: Add ldap-group ACL scheme.
+ * Add ldap-group ACL scheme (and possibly a root-only version).
- * KERB-63: Provide a root-instance version of the ldap-attr (and possibly
- the ldap-group) ACL schemes.
+ * Add a comment field to ACLs.
- * KERB-86: Add a comment field to ACLs.
+ * Support external ACLs under a backend other than remctl. This will
+ require some way of re-exporting the authenticated user identity
+ instead of relying on the existence of the remctl variables.
Database:
- * KERB-55: Fix case-insensitivity bug in unique keys with MySQL for
- objects. When creating an http/<host> principal when an HTTP/<host>
- principal already existed, MySQL rejected the row entry as a duplicate.
- The name should be case-sensitive.
+ * Fix case-insensitivity bug in unique keys with MySQL for objects. When
+ creating an http/<host> principal when an HTTP/<host> principal already
+ existed, MySQL rejected the row entry as a duplicate. The name should
+ be case-sensitive.
- * KERB-103: On upgrades, support adding new object types and ACL
- verifiers to the class tables.
+ * On upgrades, support adding new object types and ACL verifiers to the
+ class tables.
Objects:
- * KERB-120: Check whether we can just drop the realm restriction on
- keytabs and allow the name to contain the realm if the Kerberos type is
- Heimdal.
+ * Check whether we can just drop the realm restriction on keytabs and
+ allow the name to contain the realm if the Kerberos type is Heimdal.
- * KERB-59: Use the Perl Authen::Krb5::Admin module instead of rolling our
- own kadmin code with Expect now that MIT Kerberos has made the kadmin
- API public.
+ * Use the Perl Authen::Krb5::Admin module instead of rolling our own
+ kadmin code with Expect now that MIT Kerberos has made the kadmin API
+ public.
- * KERB-85: Implement an ssh keypair wallet object. The server can run
- ssh-keygen to generate a public/private key pair and return both to the
- client, which would split them apart. Used primarily for host keys.
- May need a side table to store key types, or a naming convention.
+ * Implement an ssh keypair wallet object. The server can run ssh-keygen
+ to generate a public/private key pair and return both to the client,
+ which would split them apart. Used primarily for host keys. May need
+ a side table to store key types, or a naming convention.
- * KERB-124: Implement an X.509 certificate object. I expect this would
- store the public and private key as a single file in the same format
- that Apache can read for combined public and private keys. There were
- requests for storing the CSR, but I don't see why you'd want to do
- that. Start with store support. The file code is mostly sufficient
- here, but it would be nice to automatically support object expiration
- based on the expiration time for the certificate.
+ * Implement an X.509 certificate object. I expect this would store the
+ public and private key as a single file in the same format that Apache
+ can read for combined public and private keys. There were requests for
+ storing the CSR, but I don't see why you'd want to do that. Start with
+ store support. The file code is mostly sufficient here, but it would
+ be nice to automatically support object expiration based on the
+ expiration time for the certificate.
- * KERB-106: Implement an X.509 CA so that you can get certificate objects
- without storing them first. Need to resolve naming conventions if you
- want to run multiple CAs on the same wallet server (but why?). Should
- this be a different type than stored certificates? Consider using
- hxtool as the underlying CA mechanism.
+ * Implement an X.509 CA so that you can get certificate objects without
+ storing them first. Need to resolve naming conventions if you want to
+ run multiple CAs on the same wallet server (but why?). Should this be
+ a different type than stored certificates? Consider using hxtool as
+ the underlying CA mechanism.
- * KERB-77: Support returning the checksum of a file object stored in
- wallet so that one can determine whether the version stored on disk is
- identical.
+ * Support returning the checksum of a file object stored in wallet so
+ that one can determine whether the version stored on disk is identical.
- * KERB-108: Implement new password wallet object, which is like file
- except that it generates a random, strong password when retrieved the
- first time without being stored.
-
- * KERB-71: Support interrogating objects to find all host-based objects
- for a particular host, allowing cleanup of all of those host's objects
- after retiring the host.
-
- * KERB-127: Support setting the disallow-svr flag on created principals.
- In general, support setting arbitrary principal flags.
+ * Support setting the disallow-svr flag on created principals. In
+ general, support setting arbitrary principal flags.
Reports:
- * KERB-117: Add audit for references to unknown ACLs, possibly introduced
- by previous versions before ACL deletion was checked with database
+ * Add audit for references to unknown ACLs, possibly introduced by
+ previous versions before ACL deletion was checked with database
backends that don't do referential integrity.
- * KERB-105: Add report for all objects that have never been stored.
-
- * KERB-122: For objects tied to hostnames, report on objects referring to
- hosts which do not exist. For the initial pass, this is probably only
- keytab objects with names containing a slash where the part after the
- slash looks like a hostname. This may need some configuration help.
+ * For objects tied to hostnames, report on objects referring to hosts
+ which do not exist. For the initial pass, this is probably only keytab
+ objects with names containing a slash where the part after the slash
+ looks like a hostname. This may need some configuration help.
- * KERB-102: Make contrib/wallet-summary generic and include it in
- wallet-report, with additional configuration in Wallet::Config.
- Enhance it to report on any sort of object, not just on keytabs, and to
- give numbers on downloaded versus not downloaded objects.
+ * Make contrib/wallet-summary generic and include it in wallet-report,
+ with additional configuration in Wallet::Config. Enhance it to report
+ on any sort of object, not just on keytabs, and to give numbers on
+ downloaded versus not downloaded objects.
- * KERB-69: Write a tool to mail the owners of wallet objects, taking the
- list of objects and the mail message to send as inputs. This could
- possibly use the notification service, although a version that sends
- mail directly would be useful external to Stanford.
+ * Write a tool to mail the owners of wallet objects, taking the list of
+ objects and the mail message to send as inputs. This could possibly
+ use the notification service, although a version that sends mail
+ directly would be useful external to Stanford.
- * KERB-134: Merge the Commerzbank AG work to dump all the object history,
- applying various search criteria to it, or clear parts of the object
- history.
+ * Merge the Commerzbank AG work to dump all the object history, applying
+ various search criteria to it, or clear parts of the object history.
Administrative Interface:
- * KERB-80: Add a function to wallet-admin to purge expired entries.
- Possibly also check expiration before allowing anyone to get or store
- objects.
+ * Add a function to wallet-admin to purge expired entries. Possibly also
+ check expiration before allowing anyone to get or store objects.
- * KERB-58: Add a function or separate script to automate removal of
- DNS-based objects for which the hosts no longer exist. Will need to
- support a site-specific callout to determine whether the host exists.
+ * Add a function or separate script to automate removal of DNS-based
+ objects for which the hosts no longer exist. Will need to support a
+ site-specific callout to determine whether the host exists.
- * KERB-54: Database creation appears not to work without the SQL files,
- but it's supposed to work directly from the classes. Double-check
- this.
+ * Database creation appears not to work without the SQL files, but it's
+ supposed to work directly from the classes. Double-check this.
Documentation:
- * KERB-82: Write a conventions document for ACL naming, object naming,
- and similar issues.
+ * Write a conventions document for ACL naming, object naming, and similar
+ issues.
- * KERB-125: Write a future design and roadmap document to collect notes
- about how unimplemented features should be handled.
+ * Write a future design and roadmap document to collect notes about how
+ unimplemented features should be handled.
- * KERB-65: Document using the wallet system over something other than
- remctl.
+ * Document using the wallet system over something other than remctl.
- * KERB-112: Document all diagnostics for all wallet APIs.
+ * Document all diagnostics for all wallet APIs.
- * KERB-135: Document configuration with an Oracle database.
+ * Document configuration with an Oracle database.
Code Style and Cleanup:
- * KERB-98: There is a lot of duplicate code in wallet-backend. Convert
- that to use some sort of data-driven model with argument count and
- flags so that the method calls can be written only once. Convert
- wallet-admin to use the same code.
+ * There is a lot of duplicate code in wallet-backend. Convert that to
+ use some sort of data-driven model with argument count and flags so
+ that the method calls can be written only once. Convert wallet-admin
+ to use the same code.
- * KERB-100: There's a lot of code duplication in the dispatch functions
- in the Wallet::Server class. Find a way to rewrite that so that the
- dispatch doesn't duplicate the same code patterns.
+ * There's a lot of code duplication in the dispatch functions in the
+ Wallet::Server class. Find a way to rewrite that so that the dispatch
+ doesn't duplicate the same code patterns.
- * KERB-73: The wallet-backend and wallet documentation share the COMMANDS
- section. Work out some means to assemble the documentation without
- duplicating content.
+ * The wallet-backend and wallet documentation share the COMMANDS section.
+ Work out some means to assemble the documentation without duplicating
+ content.
- * KERB-110: The Wallet::Config class is very ugly and could use some
- better internal API to reference the variables in it.
+ * The Wallet::Config class is very ugly and could use some better
+ internal API to reference the variables in it.
- * KERB-76: Consider using Class::Accessor to get rid of the scaffolding
- code to access object data. Alternately, consider using Moose.
+ * Consider using Class::Accessor to get rid of the scaffolding code to
+ access object data. Alternately, consider using Moose.
- * KERB-130: Rewrite the error handling to use exceptions instead of the
- C-style return value and separate error call.
+ * Rewrite the error handling to use exceptions instead of the C-style
+ return value and separate error call.
Test Suite:
- * KERB-92: The ldap-attr verifier test case is awful and completely
- specific to people with admin access to the Stanford LDAP tree. Write
- a real test.
+ * The ldap-attr verifier test case is awful and completely specific to
+ people with admin access to the Stanford LDAP tree. Write a real test.
- * KERB-87: Rename the tests to use a subdirectory organization.
+ * Rename the tests to use a subdirectory organization.
- * KERB-61: Add POD coverage testing using Test::POD::Coverage for the
- server modules.
+ * Add POD coverage testing using Test::POD::Coverage for the server
+ modules.
- * KERB-91: Rewrite the client test suite to use Perl and to make better
- use of shared code so that it can be broken into function components.
+ * Rewrite the client test suite to use Perl and to make better use of
+ shared code so that it can be broken into function components.
- * KERB-74: Refactor the test suite for the wallet backend to try to
- reduce the duplicated code. Using a real mock infrastructure should
- make this test suite much easier to write.
+ * Refactor the test suite for the wallet backend to try to reduce the
+ duplicated code. Using a real mock infrastructure should make this
+ test suite much easier to write.
- * KERB-81: Pull common test suite code into a Perl library that can be
- reused.
+ * Pull common test suite code into a Perl library that can be reused.
- * KERB-99: Write a test suite to scan all wallet code looking for
- diagnostics that aren't in the documentation and warn about them.
+ * Write a test suite to scan all wallet code looking for diagnostics that
+ aren't in the documentation and warn about them.
diff --git a/aclocal.m4 b/aclocal.m4
index 6145011..d265d49 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.15 -*- Autoconf -*-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,7 +21,7 @@ If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
# longlong.m4 serial 17
-dnl Copyright (C) 1999-2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 1999-2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -134,7 +134,7 @@ AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET],
| (ullmax / ull) | (ullmax % ull));]])
])
-# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -146,10 +146,10 @@ AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET],
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.14'
+[am__api_version='1.15'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.14.1], [],
+m4_if([$1], [1.15], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -165,12 +165,12 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.14.1])dnl
+[AM_AUTOMAKE_VERSION([1.15])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
-# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -232,7 +232,7 @@ AC_SUBST([AR])dnl
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -284,7 +284,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -315,7 +315,7 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -506,7 +506,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -582,7 +582,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -672,8 +672,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
-# We need awk for the "check" target. The system "awk" is bad on
-# some platforms.
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
@@ -747,6 +747,9 @@ END
AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
fi
fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
])
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
@@ -776,7 +779,7 @@ for _am_header in $config_headers :; do
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -787,7 +790,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -797,7 +800,7 @@ if test x"${install_sh}" != xset; then
fi
AC_SUBST([install_sh])])
-# Copyright (C) 2003-2013 Free Software Foundation, Inc.
+# Copyright (C) 2003-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -819,7 +822,7 @@ AC_SUBST([am__leading_dot])])
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
# From Jim Meyering
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -854,7 +857,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -904,7 +907,7 @@ rm -f confinc confmf
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -943,7 +946,7 @@ fi
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -972,7 +975,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1019,7 +1022,7 @@ AC_LANG_POP([C])])
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1038,7 +1041,7 @@ AC_DEFUN([AM_RUN_LOG],
# Check to make sure that the build environment is sane. -*- Autoconf -*-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1119,7 +1122,7 @@ AC_CONFIG_COMMANDS_PRE(
rm -f conftest.file
])
-# Copyright (C) 2009-2013 Free Software Foundation, Inc.
+# Copyright (C) 2009-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1179,7 +1182,7 @@ AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1207,7 +1210,7 @@ fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006-2013 Free Software Foundation, Inc.
+# Copyright (C) 2006-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1226,7 +1229,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004-2013 Free Software Foundation, Inc.
+# Copyright (C) 2004-2014 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -1357,6 +1360,7 @@ AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
+m4_include([m4/clang.m4])
m4_include([m4/gssapi.m4])
m4_include([m4/krb5-config.m4])
m4_include([m4/krb5.m4])
diff --git a/autogen b/autogen
index f2ef879..3b8bc1b 100755
--- a/autogen
+++ b/autogen
@@ -4,8 +4,8 @@
set -e
+# Regenerate all the autotools files.
autoreconf -i --force
-rm -rf autom4te.cache
# Generate manual pages.
version=`grep '^wallet' NEWS | head -1 | cut -d' ' -f2`
diff --git a/build-aux/ar-lib b/build-aux/ar-lib
index fe2301e..463b9ec 100755
--- a/build-aux/ar-lib
+++ b/build-aux/ar-lib
@@ -4,7 +4,7 @@
me=ar-lib
scriptversion=2012-03-01.08; # UTC
-# Copyright (C) 2010-2013 Free Software Foundation, Inc.
+# Copyright (C) 2010-2014 Free Software Foundation, Inc.
# Written by Peter Rosin <peda@lysator.liu.se>.
#
# This program is free software; you can redistribute it and/or modify
diff --git a/build-aux/compile b/build-aux/compile
index 531136b..a85b723 100755
--- a/build-aux/compile
+++ b/build-aux/compile
@@ -3,7 +3,7 @@
scriptversion=2012-10-14.11; # UTC
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
diff --git a/build-aux/depcomp b/build-aux/depcomp
index 4ebd5b3..fc98710 100755
--- a/build-aux/depcomp
+++ b/build-aux/depcomp
@@ -3,7 +3,7 @@
scriptversion=2013-05-30.07; # UTC
-# Copyright (C) 1999-2013 Free Software Foundation, Inc.
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/install-sh b/build-aux/install-sh
index 377bb86..59990a1 100755
--- a/build-aux/install-sh
+++ b/build-aux/install-sh
@@ -1,7 +1,7 @@
#!/bin/sh
# install - install a program, script, or datafile
-scriptversion=2011-11-20.07; # UTC
+scriptversion=2014-09-12.12; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
@@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC
# This script is compatible with the BSD install script, but was written
# from scratch.
+tab=' '
nl='
'
-IFS=" "" $nl"
+IFS=" $tab$nl"
-# set DOITPROG to echo to test this script
+# Set DOITPROG to "echo" to test this script.
-# Don't use :- since 4.3BSD and earlier shells don't like it.
doit=${DOITPROG-}
-if test -z "$doit"; then
- doit_exec=exec
-else
- doit_exec=$doit
-fi
+doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
@@ -68,17 +64,6 @@ mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
-posix_glob='?'
-initialize_posix_glob='
- test "$posix_glob" != "?" || {
- if (set -f) 2>/dev/null; then
- posix_glob=
- else
- posix_glob=:
- fi
- }
-'
-
posix_mkdir=
# Desired mode of installed file.
@@ -97,7 +82,7 @@ dir_arg=
dst_arg=
copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,46 +122,57 @@ while test $# -ne 0; do
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
- shift;;
+ shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
-o) chowncmd="$chownprog $2"
- shift;;
+ shift;;
-s) stripcmd=$stripprog;;
- -t) dst_arg=$2
- # Protect names problematic for 'test' and other utilities.
- case $dst_arg in
- -* | [=\(\)!]) dst_arg=./$dst_arg;;
- esac
- shift;;
+ -t)
+ is_target_a_directory=always
+ dst_arg=$2
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
- -T) no_target_directory=true;;
+ -T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
- --) shift
- break;;
+ --) shift
+ break;;
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
*) break;;
esac
shift
done
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
@@ -208,6 +204,15 @@ if test $# -eq 0; then
fi
if test -z "$dir_arg"; then
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+fi
+
+if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
@@ -223,16 +228,16 @@ if test -z "$dir_arg"; then
*[0-7])
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw='% 200'
+ u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw=,u+rw
+ u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
@@ -269,41 +274,15 @@ do
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
- if test -n "$no_target_directory"; then
- echo "$0: $dst_arg: Is a directory" >&2
- exit 1
+ if test "$is_target_a_directory" = never; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
- # Prefer dirname, but fall back on a substitute if dirname fails.
- dstdir=`
- (dirname "$dst") 2>/dev/null ||
- expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$dst" : 'X\(//\)[^/]' \| \
- X"$dst" : 'X\(//\)$' \| \
- X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
- echo X"$dst" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'
- `
-
+ dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
@@ -314,74 +293,81 @@ do
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
- # Create intermediate dirs using mode 755 as modified by the umask.
- # This is like FreeBSD 'install' as of 1997-10-28.
- umask=`umask`
- case $stripcmd.$umask in
- # Optimize common cases.
- *[2367][2367]) mkdir_umask=$umask;;
- .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
- *[0-7])
- mkdir_umask=`expr $umask + 22 \
- - $umask % 100 % 40 + $umask % 20 \
- - $umask % 10 % 4 + $umask % 2
- `;;
- *) mkdir_umask=$umask,go-w;;
- esac
-
- # With -d, create the new directory with the user-specified mode.
- # Otherwise, rely on $mkdir_umask.
- if test -n "$dir_arg"; then
- mkdir_mode=-m$mode
- else
- mkdir_mode=
- fi
-
- posix_mkdir=false
- case $umask in
- *[123567][0-7][0-7])
- # POSIX mkdir -p sets u+wx bits regardless of umask, which
- # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
- ;;
- *)
- tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
- trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
- if (umask $mkdir_umask &&
- exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
- then
- if test -z "$dir_arg" || {
- # Check for POSIX incompatibilities with -m.
- # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
- # other-writable bit of parent directory when it shouldn't.
- # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
- ls_ld_tmpdir=`ls -ld "$tmpdir"`
- case $ls_ld_tmpdir in
- d????-?r-*) different_mode=700;;
- d????-?--*) different_mode=755;;
- *) false;;
- esac &&
- $mkdirprog -m$different_mode -p -- "$tmpdir" && {
- ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
- test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
- }
- }
- then posix_mkdir=:
- fi
- rmdir "$tmpdir/d" "$tmpdir"
- else
- # Remove any dirs left behind by ancient mkdir implementations.
- rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
- fi
- trap '' 0;;
- esac;;
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ # $RANDOM is not portable (e.g. dash); use it when possible to
+ # lower collision chance
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ # As "mkdir -p" follows symlinks and we work in /tmp possibly; so
+ # create the $tmpdir first (and fail if unsuccessful) to make sure
+ # that nobody tries to guess the $tmpdir name.
+ if (umask $mkdir_umask &&
+ $mkdirprog $mkdir_mode "$tmpdir" &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ test_tmpdir="$tmpdir/a"
+ ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
esac
if
$posix_mkdir && (
- umask $mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
@@ -391,53 +377,51 @@ do
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
- /*) prefix='/';;
- [-=\(\)!]*) prefix='./';;
- *) prefix='';;
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
esac
- eval "$initialize_posix_glob"
-
oIFS=$IFS
IFS=/
- $posix_glob set -f
+ set -f
set fnord $dstdir
shift
- $posix_glob set +f
+ set +f
IFS=$oIFS
prefixes=
for d
do
- test X"$d" = X && continue
-
- prefix=$prefix$d
- if test -d "$prefix"; then
- prefixes=
- else
- if $posix_mkdir; then
- (umask=$mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
- # Don't fail if two instances are running concurrently.
- test -d "$prefix" || exit 1
- else
- case $prefix in
- *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
- *) qprefix=$prefix;;
- esac
- prefixes="$prefixes '$qprefix'"
- fi
- fi
- prefix=$prefix/
+ test X"$d" = X && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
done
if test -n "$prefixes"; then
- # Don't fail if two instances are running concurrently.
- (umask $mkdir_umask &&
- eval "\$doit_exec \$mkdirprog $prefixes") ||
- test -d "$dstdir" || exit 1
- obsolete_mkdir_used=true
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
fi
fi
fi
@@ -472,15 +456,12 @@ do
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
- old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
- new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
- eval "$initialize_posix_glob" &&
- $posix_glob set -f &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
+ set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
@@ -493,24 +474,24 @@ do
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
- # Now remove or move aside any old file at destination location.
- # We try this two ways since rm can't unlink itself on some
- # systems and the destination file might be busy for other
- # reasons. In this case, the final cleanup might fail but the new
- # file should still install successfully.
- {
- test ! -f "$dst" ||
- $doit $rmcmd -f "$dst" 2>/dev/null ||
- { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
- { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
- } ||
- { echo "$0: cannot unlink or rename $dst" >&2
- (exit 1); exit 1
- }
- } &&
-
- # Now rename the file to the real destination.
- $doit $mvcmd "$dsttmp" "$dst"
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
diff --git a/build-aux/missing b/build-aux/missing
index db98974..f62bbae 100755
--- a/build-aux/missing
+++ b/build-aux/missing
@@ -3,7 +3,7 @@
scriptversion=2013-10-28.13; # UTC
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
diff --git a/client/wallet-rekey.1 b/client/wallet-rekey.1
index 77b2f11..d596f5f 100644
--- a/client/wallet-rekey.1
+++ b/client/wallet-rekey.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-REKEY 1"
-.TH WALLET-REKEY 1 "2014-12-08" "1.2" "wallet"
+.TH WALLET-REKEY 1 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
diff --git a/client/wallet.1 b/client/wallet.1
index 1f41073..4b5cd83 100644
--- a/client/wallet.1
+++ b/client/wallet.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET 1"
-.TH WALLET 1 "2014-12-08" "1.2" "wallet"
+.TH WALLET 1 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -335,6 +335,15 @@ associations with objects will be unchanged. The \f(CW\*(C`ADMIN\*(C'\fR \s-1AC
renamed. <id> may be either the current name or the numeric \s-1ID. \s0 <name>
must not be all-numeric. To rename an \s-1ACL,\s0 the current user must be
authorized by the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL.\s0
+.IP "acl replace <id> <new\-id>" 4
+.IX Item "acl replace <id> <new-id>"
+Find any objects owned by <id>, and then change their ownership to
+<new_id> instead. <new\-id> should already exist, and may already have
+some objects owned by it. <id> is not deleted afterwards, though in
+most cases that is probably your next step. The \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0 may not be
+replaced from. <id> and <new\-id> may be either the current name or the
+numeric \s-1ID. \s0 To replace an \s-1ACL,\s0 the current user must be authorized by
+the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL.\s0
.IP "acl show <id>" 4
.IX Item "acl show <id>"
Display the name, numeric \s-1ID,\s0 and entries of the \s-1ACL\s0 <id>.
@@ -465,6 +474,18 @@ with \fB\-f\fR (if given) or from standard input.
If an object with type <type> and name <name> does not already exist when
this command is issued (as checked with the check interface), \fBwallet\fR
will attempt to automatically create it (using autocreate).
+.IP "update <type> <name>" 4
+.IX Item "update <type> <name>"
+Prints to standard output the data associated with the object identified
+by <type> and <name>, or stores it in a file if the \fB\-f\fR option was
+given. This will generate new data in the object, and only works for
+objects that support generating new data automatically, such as keytabs or
+passwords. Types that do not support generating new data will fail and
+direct you to use get instead.
+.Sp
+If an object with type <type> and name <name> does not already exist when
+this command is issued (as checked with the check interface), \fBwallet\fR
+will attempt to automatically create it (using autocreate).
.SH "ATTRIBUTES"
.IX Header "ATTRIBUTES"
Object attributes store additional properties and configuration
diff --git a/client/wallet.pod b/client/wallet.pod
index 4b58bbf..672f0e4 100644
--- a/client/wallet.pod
+++ b/client/wallet.pod
@@ -227,6 +227,16 @@ renamed. <id> may be either the current name or the numeric ID. <name>
must not be all-numeric. To rename an ACL, the current user must be
authorized by the C<ADMIN> ACL.
+=item acl replace <id> <new-id>
+
+Find any objects owned by <id>, and then change their ownership to
+<new_id> instead. <new-id> should already exist, and may already have
+some objects owned by it. <id> is not deleted afterwards, though in
+most cases that is probably your next step. The C<ADMIN> ACL may not be
+replaced from. <id> and <new-id> may be either the current name or the
+numeric ID. To replace an ACL, the current user must be authorized by
+the C<ADMIN> ACL.
+
=item acl show <id>
Display the name, numeric ID, and entries of the ACL <id>.
@@ -375,6 +385,19 @@ If an object with type <type> and name <name> does not already exist when
this command is issued (as checked with the check interface), B<wallet>
will attempt to automatically create it (using autocreate).
+=item update <type> <name>
+
+Prints to standard output the data associated with the object identified
+by <type> and <name>, or stores it in a file if the B<-f> option was
+given. This will generate new data in the object, and only works for
+objects that support generating new data automatically, such as keytabs or
+passwords. Types that do not support generating new data will fail and
+direct you to use get instead.
+
+If an object with type <type> and name <name> does not already exist when
+this command is issued (as checked with the check interface), B<wallet>
+will attempt to automatically create it (using autocreate).
+
=back
=head1 ATTRIBUTES
diff --git a/config.h.in b/config.h.in
index f525d5e..fb9dc50 100644
--- a/config.h.in
+++ b/config.h.in
@@ -39,6 +39,12 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
+/* Define to 1 if you have the <kerberosv5/com_err.h> header file. */
+#undef HAVE_KERBEROSV5_COM_ERR_H
+
+/* Define to 1 if you have the <kerberosv5/krb5.h> header file. */
+#undef HAVE_KERBEROSV5_KRB5_H
+
/* Define to enable Kerberos features. */
#undef HAVE_KRB5
@@ -124,12 +130,6 @@
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
-/* Define to 1 if you have the `strlcat' function. */
-#undef HAVE_STRLCAT
-
-/* Define to 1 if you have the `strlcpy' function. */
-#undef HAVE_STRLCPY
-
/* Define to 1 if you have the <syslog.h> header file. */
#undef HAVE_SYSLOG_H
@@ -139,6 +139,9 @@
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
diff --git a/configure b/configure
index bdbdf43..39cb4aa 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for wallet 1.2.
+# Generated by GNU Autoconf 2.69 for wallet 1.3.
#
# Report bugs to <eagle@eyrie.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='wallet'
PACKAGE_TARNAME='wallet'
-PACKAGE_VERSION='1.2'
-PACKAGE_STRING='wallet 1.2'
+PACKAGE_VERSION='1.3'
+PACKAGE_STRING='wallet 1.3'
PACKAGE_BUGREPORT='eagle@eyrie.org'
PACKAGE_URL=''
@@ -625,6 +625,10 @@ ac_includes_default="\
ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
+WARNINGS_CLANG_FALSE
+WARNINGS_CLANG_TRUE
+WARNINGS_GCC_FALSE
+WARNINGS_GCC_TRUE
REMCTLD
LIBOBJS
KRB5_USES_COM_ERR_FALSE
@@ -712,6 +716,7 @@ infodir
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -800,6 +805,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1052,6 +1058,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1189,7 +1204,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1302,7 +1317,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures wallet 1.2 to adapt to many kinds of systems.
+\`configure' configures wallet 1.3 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1342,6 +1357,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1368,7 +1384,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of wallet 1.2:";;
+ short | recursive ) echo "Configuration of wallet 1.3:";;
esac
cat <<\_ACEOF
@@ -1486,7 +1502,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-wallet configure 1.2
+wallet configure 1.3
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2195,7 +2211,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by wallet $as_me 1.2, which was
+It was created by wallet $as_me 1.3, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2574,7 +2590,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
-am__api_version='1.14'
+am__api_version='1.15'
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
@@ -2766,7 +2782,7 @@ else
$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
fi
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -3060,7 +3076,7 @@ fi
# Define the identity of the package.
PACKAGE='wallet'
- VERSION='1.2'
+ VERSION='1.3'
cat >>confdefs.h <<_ACEOF
@@ -3094,8 +3110,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
mkdir_p='$(MKDIR_P)'
-# We need awk for the "check" target. The system "awk" is bad on
-# some platforms.
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AMTAR='$${TAR-tar}'
@@ -3177,6 +3193,8 @@ fi
+
+
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -4675,6 +4693,31 @@ $as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
$as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler is Clang" >&5
+$as_echo_n "checking if the compiler is Clang... " >&6; }
+if ${rra_cv_prog_cc_clang+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if ! __clang__
+#error
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ rra_cv_prog_cc_clang=yes
+else
+ rra_cv_prog_cc_clang=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $rra_cv_prog_cc_clang" >&5
+$as_echo "$rra_cv_prog_cc_clang" >&6; }
+ if test x"$rra_cv_prog_cc_clang" = xyes; then :
+ CLANG=yes
+fi
# Check whether --enable-largefile was given.
if test "${enable_largefile+set}" = set; then :
enableval=$enable_largefile;
@@ -8215,7 +8258,7 @@ fi
LIBS="$KRB5_LIBS $LIBS"
if test x"$rra_krb5_incroot" = x; then :
- for ac_header in krb5.h krb5/krb5.h
+ for ac_header in krb5.h kerberosv5/krb5.h krb5/krb5.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -8243,6 +8286,20 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/krb5.h" >&5
+$as_echo_n "checking for kerberosv5/krb5.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/krb5.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_KRB5_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for krb5/krb5.h" >&5
$as_echo_n "checking for krb5/krb5.h... " >&6; }
if test -f "${rra_krb5_incroot}/krb5/krb5.h"; then :
@@ -8340,6 +8397,8 @@ do :
ac_fn_c_check_header_compile "$LINENO" "ibm_svc/krb5_svc.h" "ac_cv_header_ibm_svc_krb5_svc_h" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -8401,18 +8460,50 @@ else
fi
fi
- for ac_header in et/com_err.h
+ if test x"$rra_krb5_incroot" = x; then :
+ for ac_header in et/com_err.h kerberosv5/com_err.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "et/com_err.h" "ac_cv_header_et_com_err_h" "$ac_includes_default"
-if test "x$ac_cv_header_et_com_err_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
-#define HAVE_ET_COM_ERR_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for et/com_err.h" >&5
+$as_echo_n "checking for et/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/et/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ET_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/com_err.h" >&5
+$as_echo_n "checking for kerberosv5/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+fi
fi
fi
@@ -9331,7 +9422,7 @@ fi
LIBS="$KRB5_LIBS $LIBS"
if test x"$rra_krb5_incroot" = x; then :
- for ac_header in krb5.h krb5/krb5.h
+ for ac_header in krb5.h kerberosv5/krb5.h krb5/krb5.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -9359,6 +9450,20 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/krb5.h" >&5
+$as_echo_n "checking for kerberosv5/krb5.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/krb5.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_KRB5_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for krb5/krb5.h" >&5
$as_echo_n "checking for krb5/krb5.h... " >&6; }
if test -f "${rra_krb5_incroot}/krb5/krb5.h"; then :
@@ -9423,6 +9528,8 @@ do :
ac_fn_c_check_header_compile "$LINENO" "ibm_svc/krb5_svc.h" "ac_cv_header_ibm_svc_krb5_svc_h" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -9438,18 +9545,50 @@ fi
done
else
- for ac_header in et/com_err.h
+ if test x"$rra_krb5_incroot" = x; then :
+ for ac_header in et/com_err.h kerberosv5/com_err.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "et/com_err.h" "ac_cv_header_et_com_err_h" "$ac_includes_default"
-if test "x$ac_cv_header_et_com_err_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
-#define HAVE_ET_COM_ERR_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for et/com_err.h" >&5
+$as_echo_n "checking for et/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/et/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ET_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/com_err.h" >&5
+$as_echo_n "checking for kerberosv5/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+fi
fi
done
@@ -9474,7 +9613,7 @@ fi
LDFLAGS="$KRB5_LDFLAGS $LDFLAGS"
LIBS="$KRB5_LIBS $LIBS"
if test x"$rra_krb5_incroot" = x; then :
- for ac_header in krb5.h krb5/krb5.h
+ for ac_header in krb5.h kerberosv5/krb5.h krb5/krb5.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -9502,7 +9641,21 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for krb5/krb5.h" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/krb5.h" >&5
+$as_echo_n "checking for kerberosv5/krb5.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/krb5.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_KRB5_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for krb5/krb5.h" >&5
$as_echo_n "checking for krb5/krb5.h... " >&6; }
if test -f "${rra_krb5_incroot}/krb5/krb5.h"; then :
@@ -9566,6 +9719,8 @@ do :
ac_fn_c_check_header_compile "$LINENO" "ibm_svc/krb5_svc.h" "ac_cv_header_ibm_svc_krb5_svc_h" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -9581,18 +9736,50 @@ fi
done
else
- for ac_header in et/com_err.h
+ if test x"$rra_krb5_incroot" = x; then :
+ for ac_header in et/com_err.h kerberosv5/com_err.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "et/com_err.h" "ac_cv_header_et_com_err_h" "$ac_includes_default"
-if test "x$ac_cv_header_et_com_err_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
-#define HAVE_ET_COM_ERR_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for et/com_err.h" >&5
+$as_echo_n "checking for et/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/et/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ET_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/com_err.h" >&5
+$as_echo_n "checking for kerberosv5/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+fi
fi
done
@@ -10436,7 +10623,7 @@ fi
LIBS="$KRB5_LIBS $LIBS"
if test x"$rra_krb5_incroot" = x; then :
- for ac_header in krb5.h krb5/krb5.h
+ for ac_header in krb5.h kerberosv5/krb5.h krb5/krb5.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -10464,6 +10651,20 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/krb5.h" >&5
+$as_echo_n "checking for kerberosv5/krb5.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/krb5.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_KRB5_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for krb5/krb5.h" >&5
$as_echo_n "checking for krb5/krb5.h... " >&6; }
if test -f "${rra_krb5_incroot}/krb5/krb5.h"; then :
@@ -10528,6 +10729,8 @@ do :
ac_fn_c_check_header_compile "$LINENO" "ibm_svc/krb5_svc.h" "ac_cv_header_ibm_svc_krb5_svc_h" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -10543,18 +10746,50 @@ fi
done
else
- for ac_header in et/com_err.h
+ if test x"$rra_krb5_incroot" = x; then :
+ for ac_header in et/com_err.h kerberosv5/com_err.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "et/com_err.h" "ac_cv_header_et_com_err_h" "$ac_includes_default"
-if test "x$ac_cv_header_et_com_err_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
-#define HAVE_ET_COM_ERR_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for et/com_err.h" >&5
+$as_echo_n "checking for et/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/et/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ET_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/com_err.h" >&5
+$as_echo_n "checking for kerberosv5/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+fi
fi
done
@@ -11399,7 +11634,7 @@ fi
LIBS="$KRB5_LIBS $LIBS"
if test x"$rra_krb5_incroot" = x; then :
- for ac_header in krb5.h krb5/krb5.h
+ for ac_header in krb5.h kerberosv5/krb5.h krb5/krb5.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -11427,6 +11662,20 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/krb5.h" >&5
+$as_echo_n "checking for kerberosv5/krb5.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/krb5.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_KRB5_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for krb5/krb5.h" >&5
$as_echo_n "checking for krb5/krb5.h... " >&6; }
if test -f "${rra_krb5_incroot}/krb5/krb5.h"; then :
@@ -11491,6 +11740,8 @@ do :
ac_fn_c_check_header_compile "$LINENO" "ibm_svc/krb5_svc.h" "ac_cv_header_ibm_svc_krb5_svc_h" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -11506,18 +11757,50 @@ fi
done
else
- for ac_header in et/com_err.h
+ if test x"$rra_krb5_incroot" = x; then :
+ for ac_header in et/com_err.h kerberosv5/com_err.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "et/com_err.h" "ac_cv_header_et_com_err_h" "$ac_includes_default"
-if test "x$ac_cv_header_et_com_err_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
-#define HAVE_ET_COM_ERR_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for et/com_err.h" >&5
+$as_echo_n "checking for et/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/et/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_ET_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kerberosv5/com_err.h" >&5
+$as_echo_n "checking for kerberosv5/com_err.h... " >&6; }
+ if test -f "${rra_krb5_incroot}/kerberosv5/com_err.h"; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_KERBEROSV5_COM_ERR_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+fi
fi
done
@@ -11591,6 +11874,8 @@ else
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -11624,6 +11909,8 @@ done
ac_fn_c_check_decl "$LINENO" "krb5_kt_free_entry" "ac_cv_have_decl_krb5_kt_free_entry" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -11653,6 +11940,8 @@ _ACEOF
ac_fn_c_check_member "$LINENO" "krb5_keytab_entry" "keyblock" "ac_cv_member_krb5_keytab_entry_keyblock" "
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -11764,7 +12053,7 @@ $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h
fi
-for ac_header in sys/bitypes.h sys/uio.h syslog.h
+for ac_header in sys/bitypes.h sys/uio.h sys/time.h syslog.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -12115,32 +12404,6 @@ esac
fi
-ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
-if test "x$ac_cv_func_strlcat" = xyes; then :
- $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
-
-else
- case " $LIBOBJS " in
- *" strlcat.$ac_objext "* ) ;;
- *) LIBOBJS="$LIBOBJS strlcat.$ac_objext"
- ;;
-esac
-
-fi
-
-ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
-if test "x$ac_cv_func_strlcpy" = xyes; then :
- $as_echo "#define HAVE_STRLCPY 1" >>confdefs.h
-
-else
- case " $LIBOBJS " in
- *" strlcpy.$ac_objext "* ) ;;
- *) LIBOBJS="$LIBOBJS strlcpy.$ac_objext"
- ;;
-esac
-
-fi
-
@@ -12193,6 +12456,23 @@ _ACEOF
fi
+ if test x"$GCC" = xyes && test x"$CLANG" != xyes; then
+ WARNINGS_GCC_TRUE=
+ WARNINGS_GCC_FALSE='#'
+else
+ WARNINGS_GCC_TRUE='#'
+ WARNINGS_GCC_FALSE=
+fi
+
+ if test x"$CLANG" = xyes; then
+ WARNINGS_CLANG_TRUE=
+ WARNINGS_CLANG_FALSE='#'
+else
+ WARNINGS_CLANG_TRUE='#'
+ WARNINGS_CLANG_FALSE=
+fi
+
+
ac_config_headers="$ac_config_headers config.h"
ac_config_files="$ac_config_files Makefile"
@@ -12348,6 +12628,14 @@ if test -z "${KRB5_USES_COM_ERR_TRUE}" && test -z "${KRB5_USES_COM_ERR_FALSE}";
as_fn_error $? "conditional \"KRB5_USES_COM_ERR\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${WARNINGS_GCC_TRUE}" && test -z "${WARNINGS_GCC_FALSE}"; then
+ as_fn_error $? "conditional \"WARNINGS_GCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WARNINGS_CLANG_TRUE}" && test -z "${WARNINGS_CLANG_FALSE}"; then
+ as_fn_error $? "conditional \"WARNINGS_CLANG\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
@@ -12745,7 +13033,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by wallet $as_me 1.2, which was
+This file was extended by wallet $as_me 1.3, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12811,7 +13099,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-wallet config.status 1.2
+wallet config.status 1.3
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 4efc5d7..4f56eb0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,13 +1,14 @@
dnl Autoconf configuration for wallet.
dnl
dnl Written by Russ Allbery <eagle@eyrie.org>
+dnl Copyright 2014, 2016 Russ Allbery <eagle@eyrie.org>
dnl Copyright 2006, 2007, 2008, 2010, 2013, 2014
dnl The Board of Trustees of the Leland Stanford Junior University
dnl
dnl See LICENSE for licensing terms.
AC_PREREQ([2.64])
-AC_INIT([wallet], [1.2], [eagle@eyrie.org])
+AC_INIT([wallet], [1.3], [eagle@eyrie.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_LIBOBJ_DIR([portable])
AC_CONFIG_MACRO_DIR([m4])
@@ -15,10 +16,14 @@ AM_INIT_AUTOMAKE([1.11 check-news dist-xz foreign silent-rules subdir-objects
-Wall -Wno-override -Werror])
AM_MAINTAINER_MODE
+dnl Detect unexpanded macros.
+m4_pattern_forbid([^_?RRA_])
+
dnl AM_PROG_AR is required for Automake 1.12 by Libtool but not defined at all
dnl (or needed) in Automake 1.11. Work around this bug.
AC_PROG_CC
AC_USE_SYSTEM_EXTENSIONS
+RRA_PROG_CC_CLANG
AC_SYS_LARGEFILE
AM_PROG_CC_C_O
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
@@ -56,7 +61,7 @@ RRA_LIB_KRB5_RESTORE
dnl Probe for properties of the C library.
AC_HEADER_STDBOOL
-AC_CHECK_HEADERS([sys/bitypes.h sys/uio.h syslog.h])
+AC_CHECK_HEADERS([sys/bitypes.h sys/uio.h sys/time.h syslog.h])
AC_CHECK_DECLS([snprintf, vsnprintf])
RRA_C_C99_VAMACROS
RRA_C_GNU_VAMACROS
@@ -65,7 +70,7 @@ AC_CHECK_TYPES([ssize_t], [], [],
[#include <sys/types.h>])
RRA_FUNC_SNPRINTF
AC_CHECK_FUNCS([setrlimit])
-AC_REPLACE_FUNCS([asprintf mkstemp reallocarray setenv strlcat strlcpy])
+AC_REPLACE_FUNCS([asprintf mkstemp reallocarray setenv])
dnl Find a remctld binary for the test suite.
AC_ARG_VAR([REMCTLD], [Path to the remctld binary])
@@ -74,6 +79,10 @@ AS_IF([test x"$REMCTLD" != x],
[AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"],
[Define to the full path to remctld to run remctl tests.])])
+dnl Enable appropriate warnings.
+AM_CONDITIONAL([WARNINGS_GCC], [test x"$GCC" = xyes && test x"$CLANG" != xyes])
+AM_CONDITIONAL([WARNINGS_CLANG], [test x"$CLANG" = xyes])
+
dnl Output section.
AC_CONFIG_HEADER([config.h])
AC_CONFIG_FILES([Makefile])
diff --git a/contrib/commerzbank/wallet-history b/contrib/commerzbank/wallet-history
new file mode 100755
index 0000000..9826057
--- /dev/null
+++ b/contrib/commerzbank/wallet-history
@@ -0,0 +1,475 @@
+#!/usr/bin/perl -w
+#
+# -*- perl -*-
+#
+#--------------------------------------------------------------------------------------------------------------
+# Program : wallet-history
+# Function : Tool for listing and/or modifying Wallet's object history
+# Author : Commerzbank AG
+# History : 20.01.2014 - V0.1 - First version - maxcrc
+# : 21.01.2014 - V0.2 - Fixed some minor bugs and code formatting - Gerhard Stahl
+# Added some remarks for nessesary fixes
+# : 29.01.2014 - V0.2 - Fixed issue related to variable comparision of undef values - maxcrc
+# Reformatted code to use functions like debug, usage - maxcrc
+# : 06.02.2014 - V0.3 - Added support for ACL history in addition to object history - maxcrc
+# : 07.02.2014 - V0.4 - A lot of improvements, run with --help to see - maxcrc
+# --acl and --obj argument names removed, please use --acls and --objs
+# --from and --to argument names replaced with --start and --end
+# Added new filtering args --from, --action, --acl, --by, --type and --name
+# Added new --version action type
+# Added new --columns action type
+# : 08.02.2014 - V0.5 - Added support for ACL names via new ah_name column - maxcrc
+#
+#--------------------------------------------------------------------------------------------------------------
+#
+# Usage:
+#
+# type ./wallet-history with no arguments to get the full help text (or with --help).
+#
+# In general, use a command of the form:
+#
+# perl wallet-history.pl ... (t.b.d.)...
+#
+#--------------------------------------------------------------------------------------------------------------
+# Version.
+
+my $VERSION = "0.5";
+
+=head1 Needed Modules
+
+Here is the list of modules we need:
+
+=cut
+
+use strict;
+use Getopt::Long;
+use Wallet::Schema;
+use DateTime;
+use DateTime::Format::Strptime;
+#use DBIx::ResultSet;
+use DBI;
+#use DateTime ();
+#use Scalar::Util;
+
+my $debug_on = 0;
+
+=head1 Subroutines
+
+=head2 Utility subroutines
+
+=head3 usage - display a usage message
+
+=cut
+
+sub usage() {
+ print "wallet-history is tool for listing and/or modifying Wallet's history\n";
+ print "usage:\n";
+ print "wallet-history [mandatory action] [optional query type] [optional filtering arguments]\n";
+ print " where mandatory action is one of:\n";
+ print " --help - display this help information\n";
+ print " --version - display app version\n";
+ print " --columns - display column names (use with --objs or --acls)\n";
+ print " --list - list ACL or object history with/without from/to date filters\n";
+ print " --clear - clear ACL or object history with/without from/to date filters\n";
+ print " optional query type is one of:\n";
+ print " --acls - query is for ACL history (default is --objs)\n";
+ print " --objs - query is for object history (this is default)\n";
+ print " optional filtering data entries by (any [name] can be regular expression):\n";
+ print " --start [date/time] - starting date\n";
+ print " --end [date/time] - ending date\n";
+ print " --from [name] - computer name\n";
+ print " --action [name] - action\n";
+ print " --acl [name] - acl (with --acls only)\n";
+ print " --by [name] - by address (with --acls only)\n";
+ print " --type [name] - type (with --objs only)\n";
+ print " --name [name] - host name with --objs, acl name with --acls\n";
+}
+
+
+=head3 usage - prints debug messages
+
+=cut
+
+sub debug {
+ my $msg = @_;
+ return unless ( $debug_on == 1);
+ print $msg . "\n";
+}
+
+
+my $parser = DateTime::Format::Strptime->new(
+ pattern => '%Y-%m-%dT%H:%M:%S',
+ on_error => 'croak',
+ );
+
+my $opt_result = undef;
+my $an_action_help = undef;
+my $an_action_version = undef;
+my $an_action_columns = undef;
+my $an_action_list = undef;
+my $an_action_clear = undef;
+my $is_action_acls = 0;
+my $is_action_objs = 1;
+my $str_date_from = undef;
+my $str_date_to = undef;
+my $str_filter_computer_name = undef;
+my $str_filter_action = undef;
+my $str_filter_acl = undef;
+my $str_filter_by = undef;
+my $str_filter_type = undef;
+my $str_filter_name = undef;
+ $opt_result = GetOptions (
+ "help" => \$an_action_help
+ , "version" => \$an_action_version
+ , "columns" => \$an_action_columns
+ , "list" => \$an_action_list
+ , "clear" => \$an_action_clear
+ , "acls" => \$is_action_acls
+ , "objs" => \$is_action_objs
+ , "start=s" => \$str_date_from
+ , "end=s" => \$str_date_to
+ , "from=s" => \$str_filter_computer_name
+ , "action=s" => \$str_filter_action
+ , "acl=s" => \$str_filter_acl
+ , "by=s" => \$str_filter_by
+ , "type=s" => \$str_filter_type
+ , "name=s" => \$str_filter_name
+ );
+
+ if( ( defined $an_action_help ) or ( defined $an_action_version ) ) {
+ if( defined $an_action_version ) {
+ print "wallet-history version is $VERSION\n";
+ }
+ if( defined $an_action_help ) {
+ usage;
+ }
+ exit 0; # FIN #
+ }
+
+my $str_resultset_name = undef;
+ if( $is_action_acls ) {
+ $is_action_objs = 0;
+ $str_resultset_name = 'AclHistory';
+ } else {
+ $is_action_objs = 1;
+ $str_resultset_name = 'ObjectHistory';
+ }
+
+ if( defined $an_action_columns ) {
+ if( $is_action_acls ) {
+ print "\"ah_on\" \"ah_acl\" \"ah_by\" \"ah_action\" \"ah_from\"\n";
+ } else { if( $is_action_objs ) {
+ print "\"oh_on\" \"oh_by\" \"oh_type\" \"oh_name\" \"oh_action\" \"oh_from\"\n";
+ } else {
+ die "Critical internal error - unknown dataset type\n";
+ } }
+ exit 0; # FIN #
+ }
+
+
+my $date_from = undef;
+ if ( defined $str_date_from ) {
+ $date_from = $parser->parse_datetime( $str_date_from );
+ debug "Using minimal(from) date $date_from\n";
+ }
+my $date_to = undef;
+ if ( defined $str_date_to ) {
+ $date_to = $parser->parse_datetime( $str_date_to );
+ debug "Using maximal(to) date $date_to\n";
+ }
+
+ if ( $an_action_list || $an_action_clear ) {
+ my $schema = Wallet::Schema->connect;
+ my @data_entries;
+ eval {
+ my @data_entries_rs = $schema->resultset( $str_resultset_name )->search(); # ( $search_ref, $options_ref );
+ for my $entry_rs (@data_entries_rs) {
+ if( $is_action_acls ) {
+ # begin - working with ACLs history
+ my $is_filter_passed = 1;
+ my $str_date_on = "" . $entry_rs->ah_on;
+ my $date_on = $parser->parse_datetime( $str_date_on );
+ debug "date on as string ", $str_date_on, "\n";
+ debug "date on as date ", $date_on, "\n";
+ if ( ( defined $date_from ) || ( defined $date_to ) ) {
+ if ( ( defined $date_from ) && ( $date_from > $date_on ) ) {
+ $is_filter_passed = 0;
+ }
+ if ( ( defined $date_to ) && ( $date_to < $date_on ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if ( ( defined $date_from ) || ( defined $date_to ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) ) {
+ my $str_text_to_test = "" . $entry_rs->ah_from;
+ if( not ( $str_text_to_test =~ /$str_filter_computer_name/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_action ) ) {
+ my $str_text_to_test = "" . $entry_rs->ah_action;
+ if( not ( $str_text_to_test =~ /$str_filter_action/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_action ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_acl ) ) {
+ my $str_text_to_test = "" . $entry_rs->ah_acl;
+ if( not ( $str_text_to_test =~ /$str_filter_acl/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_acl ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_by ) ) {
+ my $str_text_to_test = "" . $entry_rs->ah_by;
+ if( not ( $str_text_to_test =~ /$str_filter_by/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_by ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_name ) ) {
+ my $str_text_to_test = "" . $entry_rs->ah_name;
+ if( not ( $str_text_to_test =~ /$str_filter_name/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_name ) )
+ if( $is_filter_passed != 0 ) {
+ push (@data_entries, [$entry_rs->ah_on, $entry_rs->ah_acl, $entry_rs->ah_by, $entry_rs->ah_action, $entry_rs->ah_from, $entry_rs->ah_name ]);
+ debug "Found: \"", $entry_rs->ah_on, "\" \"", $entry_rs->ah_acl, "\" \"", $entry_rs->ah_by, "\" \"", $entry_rs->ah_action, "\" \"", $entry_rs->ah_from, "\" \"", $entry_rs->ah_name, "\n";
+ } # if( $is_filter_passed != 0 )
+ debug "walked throgh dates \"", ref($date_from), "-", $date_from, "\" \"", ref($date_on), "-", $date_on, "\" \"", ref($date_to), "-", $date_to, "\" result is ", $is_filter_passed, "\n";
+ # end - working with ACLs history
+ } else { if( $is_action_objs ) {
+ # begin - working with objects history
+ my $is_filter_passed = 1;
+ my $str_date_on = "" . $entry_rs->oh_on;
+ my $date_on = $parser->parse_datetime( $str_date_on );
+ debug "date on as string ", $str_date_on, "\n";
+ debug "date on as date ", $date_on, "\n";
+ if ( ( defined $date_from ) || ( defined $date_to ) ) {
+ if ( ( defined $date_from ) && ( $date_from > $date_on ) ) {
+ $is_filter_passed = 0;
+ }
+ if ( ( defined $date_to ) && ( $date_to < $date_on ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if ( ( defined $date_from ) || ( defined $date_to ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) ) {
+ my $str_text_to_test = "" . $entry_rs->oh_from;
+ if( not ( $str_text_to_test =~ /$str_filter_computer_name/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_computer_name ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_action ) ) {
+ my $str_text_to_test = "" . $entry_rs->oh_action;
+ if( not ( $str_text_to_test =~ /$str_filter_action/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_action ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_type ) ) {
+ my $str_text_to_test = "" . $entry_rs->oh_type;
+ if( not ( $str_text_to_test =~ /$str_filter_type/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_type ) )
+ if( $is_filter_passed != 0 && ( defined $str_filter_name ) ) {
+ my $str_text_to_test = "" . $entry_rs->oh_name;
+ if( not ( $str_text_to_test =~ /$str_filter_name/ ) ) {
+ $is_filter_passed = 0;
+ }
+ } # if( $is_filter_passed != 0 && ( defined $str_filter_name ) )
+ if( $is_filter_passed != 0 ) {
+ push (@data_entries, [$entry_rs->oh_on, $entry_rs->oh_by, $entry_rs->oh_type, $entry_rs->oh_name, $entry_rs->oh_action, $entry_rs->oh_from ]);
+ debug "Found: \"", $entry_rs->oh_on, "\" \"", $entry_rs->oh_by, "\" \"", $entry_rs->oh_type, "\" \"", $entry_rs->oh_name, "\" \"", $entry_rs->oh_action, "\" \"", $entry_rs->oh_from, "\"", "\n";
+ } # if( $is_filter_passed != 0 )
+ debug "walked throgh dates \"", ref($date_from), "-", $date_from, "\" \"", ref($date_on), "-", $date_on, "\" \"", ref($date_to), "-", $date_to, "\" result is ", $is_filter_passed, "\n";
+ # end - working with objects history
+ } else {
+ die "Critical internal error - unknown dataset type\n";
+ } }
+ } # for my $entry_rs (@data_entries_rs)
+ }; # eval
+ if ($@) {
+ print "cannot list data entries: $@";
+ exit -1;
+ }
+
+ if( $is_action_acls ) {
+ # begin - working with ACLs history
+ for my $group (@data_entries) {
+ if ( $an_action_clear ) {
+ my $is_deleted_ok = 0;
+ print "\"", join ("\" \"", @$group), "\" ... ";
+ my $sql_delete_error = undef;
+ eval {
+ my $str_ah_on = "" . $group->[0];
+ my $str_ah_acl = "" . $group->[1];
+ my $str_ah_by = "" . $group->[2];
+ my $str_ah_action = "" . $group->[3];
+ my $str_ah_from = "" . $group->[4];
+ my $str_ah_name = "" . $group->[5];
+
+ my $string = $str_ah_on; # '01234567890';
+ my $find = 'T'; # '0';
+ my $replace = ' '; # 'a';
+ my $pos = index($string, $find);
+ while ( $pos > -1 ) {
+ substr( $string, $pos, length( $find ), $replace );
+ $pos = index( $string, $find, $pos + length( $replace ));
+ }
+ $str_ah_on = $string;
+
+ debug "Searching \"$str_ah_on\" \"$str_ah_acl\" \"$str_ah_by\" \"$str_ah_action\" \"$str_ah_from\" \"$str_ah_name\" ... \n";
+
+ #my @data_entries_rs =
+ # $schema->resultset('AclHistory')->search( [
+ # { ah_on => $str_ah_on }
+ # , { ah_acl => $str_ah_acl }
+ # , { ah_by => $str_ah_by }
+ # , { ah_action => $str_ah_action }
+ # , { ah_from => $str_ah_from }
+ # , { ah_name => $str_ah_name }
+ # ] ); # -> delete;
+
+ #my @data_entries_rs =
+ # $schema->resultset('AclHistory')->search( [
+ # { ah_on => { '=' , $str_ah_on } }
+ # , { ah_acl => { '=' , $str_ah_acl } }
+ # , { ah_by => { '=' , $str_ah_by } }
+ # , { ah_action => { '=' , $str_ah_action } }
+ # , { ah_from => { '=' , $str_ah_from } }
+ # , { ah_name => { '=' , $str_ah_name } }
+ # ] ); # -> delete;
+
+ my $str_sql_del = "DELETE FROM acl_history WHERE ah_on = TO_DATE(\'" . $str_ah_on . "\', 'YYYY-MM-DD HH24:MI:SS') AND ah_acl = \'" . $str_ah_acl . "\' AND ah_by = \'" . $str_ah_by . "\' AND ah_action = \'" . $str_ah_action . "\' AND ah_from = \'" . $str_ah_from . "\' AND ah_name = \'" . $str_ah_name . "\'";
+
+ #$schema->storage->debug(1);
+ my @ret = $schema->storage->dbh_do(
+ sub {
+ my ($storage, $dbh, @args) = @_;
+ debug "Attempting to ", $str_sql_del, "\n";
+ debug "dbh is ", $dbh, "\n";
+ my $sth = $dbh->prepare( $str_sql_del );
+ #$sth->{PrintError} = 1;
+ #$sth->{RaiseError} = 1;
+ debug "sth is ", $sth, "\n";
+ my $ret = $sth->execute(); # or die "Can't execute SQL statement: $DBI::errstr\n";
+ debug "ret is ", $ret, "\n";
+ debug "error string is", $sth->errstr(), "\n";
+ if ( $ret == 0 ) {
+ $sql_delete_error = "Internal error";
+ } else {
+ $is_deleted_ok = 1;
+ }
+ },
+ $str_sql_del
+ );
+
+ #$is_deleted_ok = 1;
+
+ #for my $entry_rs (@data_entries_rs) {
+ # print "Will delete: \"", $entry_rs->ah_on, "\" \"", $entry_rs->ah_acl, "\" \"", $entry_rs->ah_by, "\" \"", $entry_rs->ah_action, "\" \"", $entry_rs->ah_from, "\"", "\n";
+ #} # for my $entry_rs (@data_entries_rs)
+
+ }; # eval
+ if ( $is_deleted_ok ) {
+ print "Deleted\n";
+ } else {
+ print "Error ", $sql_delete_error, "\n";
+ }
+ } else { # else from if ( $an_action_clear )
+ print "\"", join ("\" \"", @$group), "\"\n";
+ } # else from if ( $an_action_clear )
+ } # for my $group (@data_entries)
+ # end - working with ACLs history
+ } else { if( $is_action_objs ) {
+ # begin - working with objects history
+ for my $group (@data_entries) {
+ if ( $an_action_clear ) {
+ my $is_deleted_ok = 0;
+ print "\"", join ("\" \"", @$group), "\" ... ";
+ my $sql_delete_error = undef;
+ eval {
+ my $str_oh_on = "" . $group->[0];
+ my $str_oh_by = "" . $group->[1];
+ my $str_oh_type = "" . $group->[2];
+ my $str_oh_name = "" . $group->[3];
+ my $str_oh_action = "" . $group->[4];
+ my $str_oh_from = "" . $group->[5];
+
+ my $string = $str_oh_on; # '01234567890';
+ my $find = 'T'; # '0';
+ my $replace = ' '; # 'a';
+ my $pos = index($string, $find);
+ while ( $pos > -1 ) {
+ substr( $string, $pos, length( $find ), $replace );
+ $pos = index( $string, $find, $pos + length( $replace ));
+ }
+ $str_oh_on = $string;
+
+ debug "Searching \"$str_oh_on\" \"$str_oh_by\" \"$str_oh_type\" \"$str_oh_name\" \"$str_oh_action\" \"$str_oh_from\" ... \n";
+
+ #my @data_entries_rs =
+ # $schema->resultset('ObjectHistory')->search( [
+ # { oh_on => $str_oh_on }
+ # , { oh_type => $str_oh_type }
+ # , { oh_name => $str_oh_name }
+ # , { oh_action => $str_oh_action }
+ # , { oh_from => $str_oh_from }
+ # ] ); # -> delete;
+
+ #my @data_entries_rs =
+ # $schema->resultset('ObjectHistory')->search( [
+ # { oh_on => { '=' , $str_oh_on } }
+ # , { oh_type => { '=' , $str_oh_type } }
+ # , { oh_name => { '=' , $str_oh_name } }
+ # , { oh_action => { '=' , $str_oh_action } }
+ # , { oh_from => { '=' , $str_oh_from } }
+ # ] ); # -> delete;
+
+ my $str_sql_del = "DELETE FROM object_history WHERE oh_on = TO_DATE(\'" . $str_oh_on . "\', 'YYYY-MM-DD HH24:MI:SS') AND oh_type = \'" . $str_oh_type . "\' AND oh_name = \'" . $str_oh_name . "\' AND oh_action = \'" . $str_oh_action . "\' AND oh_from = \'" . $str_oh_from . "\'";
+
+ #$schema->storage->debug(1);
+ my @ret = $schema->storage->dbh_do(
+ sub {
+ my ($storage, $dbh, @args) = @_;
+ debug "Attempting to ", $str_sql_del, "\n";
+ debug "dbh is ", $dbh, "\n";
+ my $sth = $dbh->prepare( $str_sql_del );
+ #$sth->{PrintError} = 1;
+ #$sth->{RaiseError} = 1;
+ debug "sth is ", $sth, "\n";
+ my $ret = $sth->execute(); # or die "Can't execute SQL statement: $DBI::errstr\n";
+ debug "ret is ", $ret, "\n";
+ debug "error string is", $sth->errstr(), "\n";
+ if ( $ret == 0 ) {
+ $sql_delete_error = "Internal error";
+ } else {
+ $is_deleted_ok = 1;
+ }
+ },
+ $str_sql_del
+ );
+
+ #$is_deleted_ok = 1;
+
+ #for my $entry_rs (@data_entries_rs) {
+ # print "Will delete: \"", $entry_rs->oh_on, "\" \"", $entry_rs->oh_type, "\" \"", $entry_rs->oh_name, "\" \"", $entry_rs->oh_action, "\" \"", $entry_rs->oh_from, "\"", "\n";
+ #} # for my $entry_rs (@data_entries_rs)
+
+ }; # eval
+ if ( $is_deleted_ok ) {
+ print "Deleted\n";
+ } else {
+ print "Error ", $sql_delete_error, "\n";
+ }
+ } else { # else from if ( $an_action_clear )
+ print "\"", join ("\" \"", @$group), "\"\n";
+ } # else from if ( $an_action_clear )
+ } # for my $group (@data_entries)
+ # end - working with objects history
+ } else {
+ die "Critical internal error - unknown dataset type\n";
+ } }
+
+ exit 0; # FIN #
+ } # if ( $an_action_list || $an_action_clear )
+
+ usage;
+ exit 0; # FIN #
diff --git a/contrib/wallet-contacts b/contrib/wallet-contacts
index 2799db3..0c72c9c 100755
--- a/contrib/wallet-contacts
+++ b/contrib/wallet-contacts
@@ -3,7 +3,7 @@
# wallet-contacts -- Report contact addresses for matching wallet objects.
#
# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2009
+# Copyright 2009, 2015
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -12,17 +12,33 @@
# Modules and declarations
##############################################################################
-require 5.006;
-
+use 5.008;
+use autodie;
use strict;
+use warnings;
use Getopt::Long qw(GetOptions);
-use Wallet::Admin ();
+use Perl6::Slurp;
+use Wallet::Report ();
# Used to cache lookups of e-mail addresses by identifiers.
our %EMAIL;
##############################################################################
+# Mail sending
+##############################################################################
+
+# Given a message, mail it through sendmail.
+sub mail {
+ my ($message) = @_;
+
+ open (MAIL, '| /usr/sbin/sendmail -t -oi -oem')
+ or die "$0: cannot fork sendmail: $!\n";
+ print MAIL $message;
+ close (MAIL);
+}
+
+##############################################################################
# whois lookups
##############################################################################
@@ -79,9 +95,12 @@ sub whois_lookup {
##############################################################################
# Read in command-line options.
-my ($help);
+my ($help, $mail, $dryrun);
Getopt::Long::config ('no_ignore_case', 'bundling');
-GetOptions ('help|h' => \$help) or exit 1;
+GetOptions ('help|h' => \$help,
+ 'mail=s' => \$mail,
+ 'dryrun' => \$dryrun,
+ ) or exit 1;
if ($help) {
print "Feeding myself to perldoc, please wait....\n";
exec ('perldoc', '-t', $0);
@@ -95,10 +114,10 @@ if (@ARGV > 2 or not defined $name) {
$0 =~ s%.*/%%;
# Gather the list of ACL lines.
-my $admin = Wallet::Admin->new;
-my @lines = $admin->report_owners ($type, $name);
-if (!@lines and $admin->error) {
- die $admin->error, "\n";
+my $report = Wallet::Report->new;
+my @lines = $report->owners ($type, $name);
+if (!@lines and $report->error) {
+ die $report->error, "\n";
}
# Now, for each line, turn it into an e-mail address. krb5 ACLs go as-is if
@@ -127,17 +146,35 @@ for (@lines) {
}
# We now have a list of e-mail addresses. De-duplicate and then print them
-# out.
+# out or mail to them.
my %seen;
@email = grep { !$seen{$_}++ } sort @email;
-print join ("\n", @email, '');
+if ($mail) {
+ if (!-e $mail) {
+ die "mail file $mail does not exist!\n";
+ }
+
+ # Load the message and set the To header.
+ my $message = slurp($mail);
+ my $mailto = join (', ', @email);
+ $message =~ s{^To:.*$}{To: $mailto}m;
+
+ if ($dryrun) {
+ print $message;
+ } else {
+ mail ($message);
+ }
+
+} else {
+ print join ("\n", @email, '');
+}
##############################################################################
# Documentation
##############################################################################
=for stopwords
-ACL NetDB SQL hostname lookup swhois whois Allbery
+ACL NetDB SQL hostname lookup swhois whois Allbery -dryrun
=head1 NAME
@@ -181,6 +218,17 @@ e-mail address for an administrator or user, it will warn but continue.
Print out this documentation (which is done simply by feeding the script
to C<perldoc -t>).
+=item B<-mail>=<fname>
+
+Takes a given email message file, replaces the contents of the To: line
+with the contacts found, and sends out that mail. This can be used for
+simple notifications that have no template requirements.
+
+=item B<-dryrun>
+
+If --mail has been set, only print to the screen rather than actually
+sending mail. Does nothing if --mail is not set.
+
=back
=head1 CAVEATS
diff --git a/contrib/wallet-rekey-periodic.8 b/contrib/wallet-rekey-periodic.8
index e4c927b..0cd04f3 100644
--- a/contrib/wallet-rekey-periodic.8
+++ b/contrib/wallet-rekey-periodic.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-REKEY-PERIODIC 8"
-.TH WALLET-REKEY-PERIODIC 8 "2014-12-08" "1.2" "wallet"
+.TH WALLET-REKEY-PERIODIC 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
diff --git a/contrib/wallet-summary b/contrib/wallet-summary
index 5cbf6e0..ba224d0 100755
--- a/contrib/wallet-summary
+++ b/contrib/wallet-summary
@@ -146,7 +146,7 @@ if ($mail) {
}
# Run the report.
-my @principals = read_dump ();
+my @principals = list_keytabs ();
report_principals (@principals);
# If -m was given, take the saved report and mail it as well.
diff --git a/contrib/wallet-summary.8 b/contrib/wallet-summary.8
index 0e04384..e64bc61 100644
--- a/contrib/wallet-summary.8
+++ b/contrib/wallet-summary.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-SUMMARY 8"
-.TH WALLET-SUMMARY 8 "2014-12-08" "1.2" "wallet"
+.TH WALLET-SUMMARY 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
diff --git a/contrib/wallet-unknown-hosts.8 b/contrib/wallet-unknown-hosts.8
index d61587f..2e52b11 100644
--- a/contrib/wallet-unknown-hosts.8
+++ b/contrib/wallet-unknown-hosts.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-UNKNOWN-HOSTS 8"
-.TH WALLET-UNKNOWN-HOSTS 8 "2014-12-08" "1.2" "wallet"
+.TH WALLET-UNKNOWN-HOSTS 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
diff --git a/docs/design-acl b/docs/design-acl
index 424b3c6..836c411 100644
--- a/docs/design-acl
+++ b/docs/design-acl
@@ -13,7 +13,7 @@ Introduction
Syntax
An ACL entry in the wallet consists of two pieces of data, a <scheme>
- and an <instance>. <scheme> is one or more characters in the set
+ and an <identifier>. <scheme> is one or more characters in the set
[a-z0-9-] that identifies the ACL backend to use when interpreting
this ACL. <identifier> is zero or more characters including all
printable ASCII characters except whitespace. Only the implementation
@@ -31,9 +31,10 @@ Semantics
used: Iterate through each ACL entry in the ACL in question. If the
ACL entry is malformatted or the scheme is not recognized, skip it.
Otherwise, dispatch the question to the check function of the ACL
- implementation, passing it the principal identifying the client and
- the <identifier> portion of the ACL entry. This function returns
- either authorized or unauthorized. If authorized, end the search; if
+ implementation, passing it the principal identifying the client, the
+ <identifier> portion of the ACL entry, and the type and name of the
+ object the user is attempting to access. This function returns either
+ authorized or unauthorized. If authorized, end the search; if
unauthorized, continue to the next ACL entry.
There is no support in this scheme for negative ACLs.
@@ -50,11 +51,35 @@ Semantics
ACL Schemes
+ external
+
+ The <identifier> is arguments to an external command. Access is
+ granted if the external command returns success. The standard remctl
+ environment variables are exposed to the external command.
+
krb5
The <identifier> is a fully-qualified Kerberos principal. Access is
granted if the principal of the client matches <identifier>.
+ ldap-attr
+
+ <identifier> is an an attribute followed by an equal sign and a value.
+ If the LDAP entry corresponding to the given principal contains the
+ attribute and value specified by <identifier>, access is granted.
+
+ ldap-attr-root
+
+ This is almost identical to netdb except that the user must be in the
+ form of a root instance (<user>/root) and the "/root" portion is
+ stripped before checking the NetDB roles.
+
+ nested
+
+ <identifier> is the name of another ACL, and access is granted if it
+ is granted by that ACL. This can be used to organize multiple ACLs
+ into a group and apply their union to an object.
+
netdb
<identifier> is the name of a system. Access is granted if the user
@@ -67,13 +92,6 @@ ACL Schemes
form of a root instance (<user>/root) and the "/root" portion is
stripped before checking the NetDB roles.
- ldap-entitlement
-
- (Not yet implemented.) <identifier> is an entitlement. If the
- entitlement attribute of the LDAP entry corresponding to the given
- principal contains the entitlement specified in <identifier>, access
- is granted.
-
pts
(Not yet implemented.) <identifier> is the name of an AFS PTS group.
@@ -82,6 +100,7 @@ ACL Schemes
License
+ Copyright 2016 Russ Allbery <eagle@eyrie.org>
Copyright 2006, 2007, 2008, 2013
The Board of Trustees of the Leland Stanford Junior University
diff --git a/docs/objects-and-schemes b/docs/objects-and-schemes
index 97e6289..763a24b 100644
--- a/docs/objects-and-schemes
+++ b/docs/objects-and-schemes
@@ -10,17 +10,21 @@ Introduction
Object Types
- duo
+ duo-ldap
+ duo-pam
+ duo-radius
+ duo-rdp
Stores the configuration for a Duo Security integration. Duo is a
cloud provider of multifactor authentication services. A Duo
integration consists of some local configuration and a secret key that
permits verification of a second factor using the Duo cloud service.
- Currently, only UNIX integrations are supported. In the future, this
- object type will likely be split into several object types
- corresponding to the supported types of Duo integrations.
+ Each of these types is the same except for the output, which is
+ specialized towards giving information in the format suited for a
+ specific application.
- Implemented via Wallet::Object::Duo.
+ Implemented via Wallet::Object::Duo::PAM, Wallet::Object::Duo::RDP,
+ Wallet::Object::Duo::LDAPProxy, Wallet::Object::Duo::RadiusProxy.
file
@@ -33,6 +37,16 @@ Object Types
Implemented via Wallet::Object::File.
+ password
+
+ Stores a file with single password in it and allows retrieval of that
+ file. This is built on the file object and is almost entirely
+ identical in function. It adds the ability to automatically generate
+ randomized content if you get the object before it's been stored,
+ letting you get autogenerated passwords.
+
+ Implemented via Wallet::Object::Password.
+
keytab
Stores a keytab representing private keys for a given Kerberos
diff --git a/docs/stanford-naming b/docs/stanford-naming
index c86c820..cb05a23 100644
--- a/docs/stanford-naming
+++ b/docs/stanford-naming
@@ -90,27 +90,6 @@ Object Naming
(OLD: <group>-<server>-htpasswd-<app>)
- password-ipmi/<server>
-
- Stores the password for remote IPMI/iLO/ILOM access to the
- system.
-
- (OLD: <group>-<server>-password-ipmi)
-
- password-root/<server>
-
- Stores the root password for a given server.
-
- (OLD: <group>-<server>-password-root)
-
- password-tivoli/<server>
-
- Stores the Tivoli TSM backup password for a given server. See
- also tivoli-key/<server>, but depending on what one wants to do
- with the password, this may be a better representation.
-
- (OLD: <group>-<server>-password-tivoli)
-
ssh-<type>/<server>
Stores the SSH private key for <server>. For shared private keys
@@ -197,20 +176,6 @@ Object Naming
(OLD: <group>-<service>-gpg-key)
- password/<group>/<service>/<name>
-
- A password for some account, service, keystore, or something
- similar that is not covered by one of the more specific naming
- conventions, such as a password used to connect to a remote ssh
- service. <service> is the service that uses this password and
- <name> is the thing the password is used for (such as the remote
- account name). This may be a file containing only the password,
- or a configuration file of some type that includes a field name
- and the password. (However, use the db type described above for
- database passwords.)
-
- (OLD: <group>-<server>-password-<account>)
-
properties/<group>/<service>[/<name>]
The properties file for a Java application that contains some
@@ -262,6 +227,68 @@ Object Naming
<group>-<server>-pam-<app>
<group>-<service>-puppetconf
<group>-<service>-shibboleth
+ <group>-<server>-password-ipmi
+ <group>-<server>-password-root
+ <group>-<server>-password-tivoli
+ <group>-<server>-password-<account>
+
+ Replaced by password objects:
+
+ password-ipmi/<server>
+ password-root/<server>
+ password-tivoli/<server>
+
+ password/<group>/<service>/<name> should be replaced by the password
+ service/<group>/<service>/<name> object if a single password, or by
+ the file object db/* or config/* format if the object contains more
+ than just the bare password.
+
+ Password
+
+ Passwords are a recent type and so most password data is actually
+ in file objects. However, we'd like to move things there both for
+ the added features of password objects to self-set, and because it
+ helps clean up the file namespace a little more.
+
+ Host-based:
+
+ ipmi/<server>
+
+ Stores the password for remote IPMI/iLO/ILOM access to the
+ system.
+
+ tivoli/<server>
+
+ Stores the Tivoli TSM backup password for a given server. See
+ also tivoli-key/<server> in the file section, but depending on
+ what one wants to do with the password, this may be a better
+ representation.
+
+ root/<server>
+
+ Stores the root password for a given server.
+
+ system/<server>/<account>
+
+ Stores the password for a non-root system account, such as a user
+ required for file uploads.
+
+ app/<server>/<application>
+
+ Stores an application password bound to a certain server.
+
+ Service-based:
+
+ service/<group>/<service>/<name>
+
+ A password for some account, service, keystore, or something
+ similar that is not covered by one of the more specific naming
+ conventions, such as a password used to connect to a remote ssh
+ service. <service> is the service that uses this password and
+ <name> is the thing the password is used for (such as the remote
+ account name). This should only be for something including the
+ password and nothing else. See the file password/ object name
+ for something that includes more data.
ACL Naming
diff --git a/m4/clang.m4 b/m4/clang.m4
new file mode 100644
index 0000000..0659d82
--- /dev/null
+++ b/m4/clang.m4
@@ -0,0 +1,26 @@
+dnl Determine whether the current compiler is Clang.
+dnl
+dnl If the current compiler is Clang, set the shell variable CLANG to yes.
+dnl
+dnl The canonical version of this file is maintained in the rra-c-util
+dnl package, available at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+dnl
+dnl Copyright 2015 Russ Allbery <eagle@eyrie.org>
+dnl
+dnl This file is free software; the authors give unlimited permission to copy
+dnl and/or distribute it, with or without modifications, as long as this
+dnl notice is preserved.
+
+dnl Source used by RRA_PROG_CC_CLANG.
+AC_DEFUN([_RRA_PROG_CC_CLANG_SOURCE], [[
+#if ! __clang__
+#error
+#endif
+]])
+
+AC_DEFUN([RRA_PROG_CC_CLANG],
+[AC_CACHE_CHECK([if the compiler is Clang], [rra_cv_prog_cc_clang],
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_PROG_CC_CLANG_SOURCE])],
+ [rra_cv_prog_cc_clang=yes],
+ [rra_cv_prog_cc_clang=no])])
+ AS_IF([test x"$rra_cv_prog_cc_clang" = xyes], [CLANG=yes])])
diff --git a/m4/krb5.m4 b/m4/krb5.m4
index 79ef961..683e30b 100644
--- a/m4/krb5.m4
+++ b/m4/krb5.m4
@@ -64,6 +64,8 @@ dnl Headers to include when probing for Kerberos library properties.
AC_DEFUN([RRA_INCLUDES_KRB5], [[
#if HAVE_KRB5_H
# include <krb5.h>
+#elif HAVE_KERBEROSV5_KRB5_H
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
@@ -113,6 +115,23 @@ AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER],
AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])])])
+dnl Check for the com_err header. Internal helper macro since we need
+dnl to do the same checks in multiple places.
+AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR],
+[AS_IF([test x"$rra_krb5_incroot" = x],
+ [AC_CHECK_HEADERS([et/com_err.h kerberosv5/com_err.h])],
+ [_RRA_LIB_KRB5_CHECK_HEADER([et/com_err.h])
+ _RRA_LIB_KRB5_CHECK_HEADER([kerberosv5/com_err.h])])])
+
+dnl Check for the main Kerberos header. Internal helper macro since we need
+dnl to do the same checks in multiple places.
+AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER_KRB5],
+[AS_IF([test x"$rra_krb5_incroot" = x],
+ [AC_CHECK_HEADERS([krb5.h kerberosv5/krb5.h krb5/krb5.h])],
+ [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
+ _RRA_LIB_KRB5_CHECK_HEADER([kerberosv5/krb5.h])
+ _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])])
+
dnl Does the appropriate library checks for reduced-dependency Kerberos
dnl linkage. The single argument, if true, says to fail if Kerberos could not
dnl be found.
@@ -122,10 +141,7 @@ AC_DEFUN([_RRA_LIB_KRB5_REDUCED],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable Kerberos library])])])
LIBS="$KRB5_LIBS $LIBS"
- AS_IF([test x"$rra_krb5_incroot" = x],
- [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])],
- [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
- _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])
+ _RRA_LIB_KRB5_CHECK_HEADER_KRB5
AC_CHECK_FUNCS([krb5_get_error_message],
[AC_CHECK_FUNCS([krb5_free_error_message])],
[AC_CHECK_FUNCS([krb5_get_error_string], [],
@@ -140,7 +156,7 @@ AC_DEFUN([_RRA_LIB_KRB5_REDUCED],
[AS_IF([test x"$1" = xtrue],
[AC_MSG_ERROR([cannot find usable com_err library])],
[KRB5_LIBS=""])])
- AC_CHECK_HEADERS([et/com_err.h])])])])])
+ _RRA_LIB_KRB5_CHECK_HEADER_COM_ERR])])])])
RRA_LIB_KRB5_RESTORE])
dnl Does the appropriate library checks for Kerberos linkage when we don't
@@ -187,10 +203,7 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL],
[$rra_krb5_extra])],
[-lasn1 -lcom_err -lcrypto $rra_krb5_extra])
LIBS="$KRB5_LIBS $LIBS"
- AS_IF([test x"$rra_krb5_incroot" = x],
- [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])],
- [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
- _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])
+ _RRA_LIB_KRB5_CHECK_HEADER_KRB5
AC_CHECK_FUNCS([krb5_get_error_message],
[AC_CHECK_FUNCS([krb5_free_error_message])],
[AC_CHECK_FUNCS([krb5_get_error_string], [],
@@ -198,7 +211,7 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL],
[AC_CHECK_FUNCS([krb5_svc_get_msg],
[AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
[RRA_INCLUDES_KRB5])],
- [AC_CHECK_HEADERS([et/com_err.h])])])])])
+ [_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR])])])])
RRA_LIB_KRB5_RESTORE])
dnl Sanity-check the results of krb5-config and be sure we can really link a
@@ -222,10 +235,7 @@ AC_DEFUN([_RRA_LIB_KRB5_CONFIG],
[RRA_KRB5_CONFIG([${rra_krb5_root}], [krb5], [KRB5],
[_RRA_LIB_KRB5_CHECK([$1])
RRA_LIB_KRB5_SWITCH
- AS_IF([test x"$rra_krb5_incroot" = x],
- [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])],
- [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h])
- _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])])
+ _RRA_LIB_KRB5_CHECK_HEADER_KRB5
AC_CHECK_FUNCS([krb5_get_error_message],
[AC_CHECK_FUNCS([krb5_free_error_message])],
[AC_CHECK_FUNCS([krb5_get_error_string], [],
@@ -233,7 +243,7 @@ AC_DEFUN([_RRA_LIB_KRB5_CONFIG],
[AC_CHECK_FUNCS([krb5_svc_get_msg],
[AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [],
[RRA_INCLUDES_KRB5])],
- [AC_CHECK_HEADERS([et/com_err.h])])])])])
+ [_RRA_LIB_KRB5_CHECK_HEADER_COM_ERR])])])])
RRA_LIB_KRB5_RESTORE],
[_RRA_LIB_KRB5_PATHS
_RRA_LIB_KRB5_MANUAL([$1])])])
diff --git a/perl/Build.PL b/perl/Build.PL
index 968ae37..c50e569 100644
--- a/perl/Build.PL
+++ b/perl/Build.PL
@@ -3,6 +3,7 @@
# Build script for the wallet distribution.
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -19,7 +20,7 @@ my $build = Module::Build->new(
dist_abstract => 'Secure credential management system',
dist_author => 'Russ Allbery <eagle@eyrie.org>',
dist_name => 'Wallet',
- dist_version => '1.01',
+ dist_version_from => 'lib/Wallet/Server.pm',
license => 'mit',
module_name => 'Wallet::Server',
recursive_test_files => 1,
@@ -36,12 +37,16 @@ my $build = Module::Build->new(
perl => '5.008',
},
recommends => {
- 'Authen::SASL' => 0,
- 'Heimdal::Kadm5' => 0,
- 'Net::Duo' => 0,
- 'Net::LDAP' => 0,
- 'Net::Remctl' => 0,
- WebAuth => 0,
+ 'Authen::SASL' => 0,
+ 'Crypt::GeneratePassword' => 0,
+ 'DateTime::Format::SQLite' => 0,
+ 'DBD::SQLite' => 0,
+ 'Heimdal::Kadm5' => 0,
+ 'IPC::Run' => 0,
+ 'Net::Duo' => 0,
+ 'Net::LDAP' => 0,
+ 'Net::Remctl' => 0,
+ WebAuth => 0,
},
);
diff --git a/perl/create-ddl b/perl/create-ddl
index b2b6f95..51fa8ff 100755
--- a/perl/create-ddl
+++ b/perl/create-ddl
@@ -57,6 +57,9 @@ exit(0);
# Documentation
##############################################################################
+=for stopwords
+DDL create-ddl
+
=head1 NAME
create-ddl - Create DDL files for Wallet
diff --git a/perl/lib/Wallet/ACL.pm b/perl/lib/Wallet/ACL.pm
index a3b0146..ad0eb2c 100644
--- a/perl/lib/Wallet/ACL.pm
+++ b/perl/lib/Wallet/ACL.pm
@@ -1,7 +1,8 @@
-# Wallet::ACL -- Implementation of ACLs in the wallet system.
+# Wallet::ACL -- Implementation of ACLs in the wallet system
#
# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2007, 2008, 2010, 2013, 2014
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2007, 2008, 2010, 2013, 2014, 2015
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -11,19 +12,15 @@
##############################################################################
package Wallet::ACL;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($VERSION);
use DateTime;
-use DBI;
+use Wallet::Object::Base;
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.08';
+our $VERSION = '1.03';
##############################################################################
# Constructors
@@ -197,16 +194,55 @@ sub rename {
$acls->ac_name ($name);
$acls->update;
$self->log_acl ('rename', undef, undef, $user, $host, $time);
+
+ # Find any references to this being used as a nested verifier and
+ # update the name. This really breaks out of the normal flow, but
+ # it's hard to do otherwise.
+ %search = (ae_scheme => 'nested',
+ ae_identifier => $self->{name},
+ );
+ my @entries = $self->{schema}->resultset('AclEntry')->search(\%search);
+ for my $entry (@entries) {
+ $entry->ae_identifier ($name);
+ $entry->update;
+ }
+
$guard->commit;
};
if ($@) {
- $self->error ("cannot rename ACL $self->{id} to $name: $@");
+ $self->error ("cannot rename ACL $self->{name} to $name: $@");
return;
}
$self->{name} = $name;
return 1;
}
+# Moves everything owned by one ACL to instead be owned by another. You'll
+# normally want to use rename, but this exists for cases where the replacing
+# ACL already exists and has things assigned to it. Returns true on success,
+# false on failure.
+sub replace {
+ my ($self, $replace_id, $user, $host, $time) = @_;
+ $time ||= time;
+
+ my %search = (ob_owner => $self->{id});
+ my @objects = $self->{schema}->resultset('Object')->search (\%search);
+ if (@objects) {
+ for my $object (@objects) {
+ my $type = $object->ob_type;
+ my $name = $object->ob_name;
+ my $object = eval {
+ Wallet::Object::Base->new($type, $name, $self->{schema});
+ };
+ $object->owner ($replace_id, $user, $host, $time);
+ }
+ } else {
+ $self->error ("no objects found for ACL $self->{name}");
+ return;
+ }
+ return 1;
+}
+
# Destroy the ACL, deleting it out of the database. Returns true on success,
# false on failure.
#
@@ -233,8 +269,20 @@ sub destroy {
die "ACL in use by ".$entry->ob_type.":".$entry->ob_name;
}
+ # Also make certain the ACL isn't being nested in another.
+ my %search = (ae_scheme => 'nested',
+ ae_identifier => $self->{name});
+ my %options = (join => 'acls',
+ prefetch => 'acls');
+ @entries = $self->{schema}->resultset('AclEntry')->search(\%search,
+ \%options);
+ if (@entries) {
+ my ($entry) = @entries;
+ die "ACL is nested in ACL ".$entry->acls->ac_name;
+ }
+
# Delete any entries (there may or may not be any).
- my %search = (ae_id => $self->{id});
+ %search = (ae_id => $self->{id});
@entries = $self->{schema}->resultset('AclEntry')->search(\%search);
for my $entry (@entries) {
$entry->delete;
@@ -257,7 +305,7 @@ sub destroy {
$guard->commit;
};
if ($@) {
- $self->error ("cannot destroy ACL $self->{id}: $@");
+ $self->error ("cannot destroy ACL $self->{name}: $@");
return;
}
return 1;
@@ -275,6 +323,22 @@ sub add {
$self->error ("unknown ACL scheme $scheme");
return;
}
+
+ # Check to make sure that this entry has a valid name for the scheme.
+ my $class = $self->scheme_mapping ($scheme);
+ my $object = eval {
+ $class->new ($identifier, $self->{schema});
+ };
+ if ($@) {
+ $self->error ("cannot create ACL verifier: $@");
+ return;
+ }
+ unless ($object && $object->syntax_check ($identifier)) {
+ $self->error ("invalid ACL identifier $identifier for $scheme");
+ return;
+ };
+
+ # Actually create the scheme.
eval {
my $guard = $self->{schema}->txn_scope_guard;
my %record = (ae_id => $self->{id},
@@ -285,7 +349,7 @@ sub add {
$guard->commit;
};
if ($@) {
- $self->error ("cannot add $scheme:$identifier to $self->{id}: $@");
+ $self->error ("cannot add $scheme:$identifier to $self->{name}: $@");
return;
}
return 1;
@@ -312,7 +376,7 @@ sub remove {
};
if ($@) {
my $entry = "$scheme:$identifier";
- $self->error ("cannot remove $entry from $self->{id}: $@");
+ $self->error ("cannot remove $entry from $self->{name}: $@");
return;
}
return 1;
@@ -340,7 +404,7 @@ sub list {
$guard->commit;
};
if ($@) {
- $self->error ("cannot retrieve ACL $self->{id}: $@");
+ $self->error ("cannot retrieve ACL $self->{name}: $@");
return;
} else {
return @entries;
@@ -395,7 +459,7 @@ sub history {
$guard->commit;
};
if ($@) {
- $self->error ("cannot read history for $self->{id}: $@");
+ $self->error ("cannot read history for $self->{name}: $@");
return;
}
return $output;
@@ -412,20 +476,21 @@ sub history {
{
my %verifier;
sub check_line {
- my ($self, $principal, $scheme, $identifier) = @_;
+ my ($self, $principal, $scheme, $identifier, $type, $name) = @_;
unless ($verifier{$scheme}) {
my $class = $self->scheme_mapping ($scheme);
unless ($class) {
push (@{ $self->{check_errors} }, "unknown scheme $scheme");
return;
}
- $verifier{$scheme} = $class->new;
+ $verifier{$scheme} = $class->new ($identifier, $self->{schema});
unless (defined $verifier{$scheme}) {
push (@{ $self->{check_errors} }, "cannot verify $scheme");
return;
}
}
- my $result = ($verifier{$scheme})->check ($principal, $identifier);
+ my $result = ($verifier{$scheme})->check ($principal, $identifier,
+ $type, $name);
if (not defined $result) {
push (@{ $self->{check_errors} }, ($verifier{$scheme})->error);
return;
@@ -435,13 +500,13 @@ sub history {
}
}
-# Given a principal, check whether it should be granted access according to
-# this ACL. Returns 1 if access was granted, 0 if access was denied, and
-# undef on some error. Errors from ACL verifiers do not cause an error
-# return, but are instead accumulated in the check_errors variable returned by
-# the check_errors() method.
+# Given a principal, object type, and object name, check whether that
+# principal should be granted access according to this ACL. Returns 1 if
+# access was granted, 0 if access was denied, and undef on some error. Errors
+# from ACL verifiers do not cause an error return, but are instead accumulated
+# in the check_errors variable returned by the check_errors() method.
sub check {
- my ($self, $principal) = @_;
+ my ($self, $principal, $type, $name) = @_;
unless ($principal) {
$self->error ('no principal specified');
return;
@@ -452,7 +517,8 @@ sub check {
$self->{check_errors} = [];
for my $entry (@entries) {
my ($scheme, $identifier) = @$entry;
- my $result = $self->check_line ($principal, $scheme, $identifier);
+ my $result = $self->check_line ($principal, $scheme, $identifier,
+ $type, $name);
return 1 if $result;
}
return 0;
@@ -643,6 +709,14 @@ On failure, the caller should call error() to get the error message.
Note that rename() operations are not logged in the ACL history.
+=item replace(ID)
+
+Replace this ACL with another. This goes through each object owned by
+the ACL and changes its ownership to the new ACL, leaving this ACL owning
+nothing (and probably then needing to be deleted). Returns true on
+success and false on failure. On failure, the caller should call error()
+to get the error message.
+
=item show()
Returns a human-readable description of this ACL, including its
diff --git a/perl/lib/Wallet/ACL/Base.pm b/perl/lib/Wallet/ACL/Base.pm
index a2b07cc..235a9cb 100644
--- a/perl/lib/Wallet/ACL/Base.pm
+++ b/perl/lib/Wallet/ACL/Base.pm
@@ -1,6 +1,7 @@
-# Wallet::ACL::Base -- Parent class for wallet ACL verifiers.
+# Wallet::ACL::Base -- Parent class for wallet ACL verifiers
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,16 +12,12 @@
##############################################################################
package Wallet::ACL::Base;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($VERSION);
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.02';
+our $VERSION = '1.03';
##############################################################################
# Interface
@@ -37,6 +34,11 @@ sub new {
return $self;
}
+# The default name check method allows any name.
+sub syntax_check {
+ return 1;
+}
+
# The default check method denies all access.
sub check {
return 0;
@@ -92,10 +94,18 @@ inherit from it. It is not used directly.
Creates a new ACL verifier. The generic function provided here just
creates and blesses an object.
-=item check(PRINCIPAL, ACL)
+=item syntax_check(PRINCIPAL, ACL)
+
+This method should be overridden by any child classes that want to
+implement validating the name of an ACL before creation. The default
+implementation allows any name for an ACL.
+
+=item check(PRINCIPAL, ACL, TYPE, NAME)
This method should always be overridden by child classes. The default
-implementation just declines all access.
+implementation just declines all access. TYPE and NAME are the type and
+name of the object being accessed, which may be used by some ACL schemes
+or may be ignored.
=item error([ERROR ...])
diff --git a/perl/lib/Wallet/ACL/External.pm b/perl/lib/Wallet/ACL/External.pm
new file mode 100644
index 0000000..caed80e
--- /dev/null
+++ b/perl/lib/Wallet/ACL/External.pm
@@ -0,0 +1,192 @@
+# Wallet::ACL::External -- Wallet external ACL verifier
+#
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+#
+# See LICENSE for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::ACL::External;
+
+use 5.008;
+use strict;
+use warnings;
+
+use POSIX qw(_exit);
+use Wallet::ACL::Base;
+use Wallet::Config;
+
+our @ISA = qw(Wallet::ACL::Base);
+our $VERSION = '1.03';
+
+##############################################################################
+# Interface
+##############################################################################
+
+# Creates a new persistent verifier. This just checks if the configuration
+# is in place.
+sub new {
+ my $type = shift;
+ unless ($Wallet::Config::EXTERNAL_COMMAND) {
+ die "external ACL support not configured\n";
+ }
+ my $self = {};
+ bless ($self, $type);
+ return $self;
+}
+
+# The most trivial ACL verifier. Returns true if the provided principal
+# matches the ACL.
+sub check {
+ my ($self, $principal, $acl, $type, $name) = @_;
+ unless ($principal) {
+ $self->error ('no principal specified');
+ return;
+ }
+ my @args = ($principal, $type, $name, $acl);
+ my $pid = open (EXTERNAL, '-|');
+ if (not defined $pid) {
+ $self->error ("cannot fork: $!");
+ return;
+ } elsif ($pid == 0) {
+ unless (open (STDERR, '>&STDOUT')) {
+ warn "wallet: cannot dup stdout: $!\n";
+ _exit(1);
+ }
+ unless (exec ($Wallet::Config::EXTERNAL_COMMAND, @args)) {
+ warn "wallet: cannot run $Wallet::Config::EXTERNAL_COMMAND: $!\n";
+ _exit(1);
+ }
+ }
+ local $_;
+ my @output = <EXTERNAL>;
+ close EXTERNAL;
+ if ($? == 0) {
+ return 1;
+ } else {
+ if (@output) {
+ $self->error ($output[0]);
+ return;
+ } else {
+ return 0;
+ }
+ }
+}
+
+1;
+__END__
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=for stopwords
+ACL Allbery verifier remctl
+
+=head1 NAME
+
+Wallet::ACL::External - Wallet ACL verifier using an external command
+
+=head1 SYNOPSIS
+
+ my $verifier = Wallet::ACL::External->new;
+ my $status = $verifier->check ($principal, $acl);
+ if (not defined $status) {
+ die "Something failed: ", $verifier->error, "\n";
+ } elsif ($status) {
+ print "Access granted\n";
+ } else {
+ print "Access denied\n";
+ }
+
+=head1 DESCRIPTION
+
+Wallet::ACL::External runs an external command to determine whether access is
+granted. The command configured via $EXTERNAL_COMMAND in L<Wallet::Config>
+will be run. The first argument to the command will be the principal
+requesting access. The identifier of the ACL will be split on whitespace and
+passed in as the remaining arguments to this command.
+
+No other arguments are passed to the command, but the command will have access
+to all of the remctl environment variables seen by the wallet server (such as
+REMOTE_USER). For a full list of environment variables, see
+L<remctld(8)/ENVIRONMENT>.
+
+The external command should exit with a non-zero status but no output to
+indicate a normal failure to satisfy the ACL. Any output will be treated as
+an error.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Creates a new ACL verifier. For this verifier, this just confirms that
+the wallet configuration sets an external command.
+
+=item check(PRINCIPAL, ACL, TYPE, NAME)
+
+Returns true if the external command returns success when run with that
+PRINCIPAL, object TYPE and NAME, and ACL. So, for example, the ACL C<external
+mdbset shell> will, when triggered by a request from rra@EXAMPLE.COM for the
+object C<file password>, result in the command:
+
+ $Wallet::Config::EXTERNAL_COMMAND rra@EXAMPLE.COM file password \
+ 'mdbset shell'
+
+=item error()
+
+Returns the error if check() returned undef.
+
+=back
+
+=head1 DIAGNOSTICS
+
+The new() method may fail with one of the following exceptions:
+
+=over 4
+
+=item external ACL support not configured
+
+The required configuration parameters were not set. See L<Wallet::Config>
+for the required configuration parameters and how to set them.
+
+=back
+
+Verifying an external ACL may fail with the following errors (returned by
+the error() method):
+
+=over 4
+
+=item cannot fork: %s
+
+The attempt to fork in order to execute the external ACL verifier
+command failed, probably due to a lack of system resources.
+
+=item no principal specified
+
+The PRINCIPAL parameter to check() was undefined or the empty string.
+
+=back
+
+In addition, if the external command fails and produces some output,
+that will be considered a failure and the first line of its output will
+be returned as the error message. The external command should exit
+with a non-zero status but no error to indicate a normal failure.
+
+=head1 SEE ALSO
+
+remctld(8), Wallet::ACL(3), Wallet::ACL::Base(3), Wallet::Config(3),
+wallet-backend(8)
+
+This module is part of the wallet system. The current version is
+available from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=cut
diff --git a/perl/lib/Wallet/ACL/Krb5.pm b/perl/lib/Wallet/ACL/Krb5.pm
index 80d32bd..e0e9a61 100644
--- a/perl/lib/Wallet/ACL/Krb5.pm
+++ b/perl/lib/Wallet/ACL/Krb5.pm
@@ -1,6 +1,7 @@
-# Wallet::ACL::Krb5 -- Wallet Kerberos v5 principal ACL verifier.
+# Wallet::ACL::Krb5 -- Wallet Kerberos v5 principal ACL verifier
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,20 +12,15 @@
##############################################################################
package Wallet::ACL::Krb5;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Wallet::ACL::Base;
-@ISA = qw(Wallet::ACL::Base);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.02';
+our @ISA = qw(Wallet::ACL::Base);
+our $VERSION = '1.03';
##############################################################################
# Interface
diff --git a/perl/lib/Wallet/ACL/Krb5/Regex.pm b/perl/lib/Wallet/ACL/Krb5/Regex.pm
index 4934cfc..f3b9a06 100644
--- a/perl/lib/Wallet/ACL/Krb5/Regex.pm
+++ b/perl/lib/Wallet/ACL/Krb5/Regex.pm
@@ -1,6 +1,7 @@
# Wallet::ACL::Krb5::Regex -- Wallet Kerberos v5 principal regex ACL verifier
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,20 +12,15 @@
##############################################################################
package Wallet::ACL::Krb5::Regex;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Wallet::ACL::Krb5;
-@ISA = qw(Wallet::ACL::Krb5);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
+our @ISA = qw(Wallet::ACL::Krb5);
+our $VERSION = '1.03';
##############################################################################
# Interface
diff --git a/perl/lib/Wallet/ACL/LDAP/Attribute.pm b/perl/lib/Wallet/ACL/LDAP/Attribute.pm
index c27729e..fcb8447 100644
--- a/perl/lib/Wallet/ACL/LDAP/Attribute.pm
+++ b/perl/lib/Wallet/ACL/LDAP/Attribute.pm
@@ -1,6 +1,7 @@
-# Wallet::ACL::LDAP::Attribute -- Wallet LDAP attribute ACL verifier.
+# Wallet::ACL::LDAP::Attribute -- Wallet LDAP attribute ACL verifier
#
# Written by Russ Allbery
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2012, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,23 +12,18 @@
##############################################################################
package Wallet::ACL::LDAP::Attribute;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
-use Authen::SASL ();
+use Authen::SASL;
use Net::LDAP qw(LDAP_COMPARE_TRUE);
use Wallet::ACL::Base;
use Wallet::Config;
-@ISA = qw(Wallet::ACL::Base);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
+our @ISA = qw(Wallet::ACL::Base);
+our $VERSION = '1.03';
##############################################################################
# Interface
diff --git a/perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm b/perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm
new file mode 100644
index 0000000..8451394
--- /dev/null
+++ b/perl/lib/Wallet/ACL/LDAP/Attribute/Root.pm
@@ -0,0 +1,123 @@
+# Wallet::ACL::LDAP::Attribute::Root -- Wallet root instance LDAP ACL verifier
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Based on Wallet::ACL::NetDB::Root by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2015
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::ACL::LDAP::Attribute::Root;
+
+use 5.008;
+use strict;
+use warnings;
+
+use Wallet::ACL::LDAP::Attribute;
+
+our @ISA = qw(Wallet::ACL::LDAP::Attribute);
+our $VERSION = '1.03';
+
+##############################################################################
+# Interface
+##############################################################################
+
+# Override the check method of Wallet::ACL::LDAP::Attribute to require that
+# the principal be a root instance and to strip /root out of the principal
+# name before checking roles.
+sub check {
+ my ($self, $principal, $acl) = @_;
+ undef $self->{error};
+ unless ($principal) {
+ $self->error ('no principal specified');
+ return;
+ }
+ unless ($principal =~ s%^([^/\@]+)/root(\@|\z)%$1$2%) {
+ return 0;
+ }
+ return $self->SUPER::check ($principal, $acl);
+}
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=for stopwords
+ACL Allbery LDAP verifier
+
+=head1 NAME
+
+Wallet::ACL::LDAP::Attribute::Root - Wallet ACL verifier for LDAP attributes (root instances)
+
+=head1 SYNOPSIS
+
+ my $verifier = Wallet::ACL::LDAP::Attribute::Root->new;
+ my $status = $verifier->check ($principal, "$attr=$value");
+ if (not defined $status) {
+ die "Something failed: ", $verifier->error, "\n";
+ } elsif ($status) {
+ print "Access granted\n";
+ } else {
+ print "Access denied\n";
+ }
+
+=head1 DESCRIPTION
+
+Wallet::ACL::LDAP::Attribute::Root works identically to
+Wallet::ACL::LDAP::Attribute except that it requires the principal to
+be a root instance (in other words, to be in the form
+<principal>/root@<realm>) and strips the C</root> portion from the
+principal before checking against the LDAP attribute and value. As
+with the base LDAP Attribute ACL verifier, the value of such a
+C<ldap-attr-root> ACL is an attribute followed by an equal sign and a
+value, and the ACL grants access to a given principal if and only if
+the LDAP entry for that principal (with C</root> stripped) has that
+attribute set to that value.
+
+To use this object, the same configuration parameters must be set as for
+Wallet::ACL::LDAP::Attribute. See Wallet::Config(3) for details on
+those configuration parameters and information about how to set wallet
+configuration.
+
+=head1 METHODS
+
+=over 4
+
+=item check(PRINCIPAL, ACL)
+
+Returns true if PRINCIPAL is granted access according to ACL, false if
+not, and undef on an error (see L<"DIAGNOSTICS"> below). ACL must be an
+attribute name and a value, separated by an equal sign (with no
+whitespace). PRINCIPAL will be granted access if it has an instance of
+C<root> and if (with C</root> stripped off) its LDAP entry contains
+that attribute with that value
+
+=back
+
+=head1 DIAGNOSTICS
+
+Same as for Wallet::ACL::LDAP::Attribute.
+
+=head1 CAVEATS
+
+The instance to strip is not currently configurable.
+
+=head1 SEE ALSO
+
+Net::Remctl(3), Wallet::ACL(3), Wallet::ACL::Base(3),
+Wallet::ACL::LDAP::Attribute(3), Wallet::Config(3), wallet-backend(8)
+
+This module is part of the wallet system. The current version is
+available from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+=head1 AUTHORS
+
+Jon Robertson <jonrober@stanford.edu>
+Russ Allbery <eagle@eyrie.org>
+
+=cut
diff --git a/perl/lib/Wallet/ACL/Nested.pm b/perl/lib/Wallet/ACL/Nested.pm
new file mode 100644
index 0000000..da42286
--- /dev/null
+++ b/perl/lib/Wallet/ACL/Nested.pm
@@ -0,0 +1,186 @@
+# Wallet::ACL::Nested - ACL class for nesting ACLs
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2015
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::ACL::Nested;
+
+use 5.008;
+use strict;
+use warnings;
+
+use Wallet::ACL::Base;
+
+our @ISA = qw(Wallet::ACL::Base);
+our $VERSION = '1.03';
+
+##############################################################################
+# Interface
+##############################################################################
+
+# Creates a new persistant verifier, taking a database handle to use for
+# syntax check validation.
+sub new {
+ my $type = shift;
+ my ($name, $schema) = @_;
+ my $self = {
+ schema => $schema,
+ expanded => {},
+ };
+ bless ($self, $type);
+ return $self;
+}
+
+# Name checking requires checking that there's an existing ACL already by
+# this name. Try to create the ACL object and use that to determine.
+sub syntax_check {
+ my ($self, $group) = @_;
+
+ my $acl;
+ eval { $acl = Wallet::ACL->new ($group, $self->{schema}) };
+ return 0 if $@;
+ return 0 unless $acl;
+ return 1;
+}
+
+# For checking a nested ACL, we need to expand each entry and then check
+# that entry. We also want to keep track of things already checked in order
+# to avoid any loops.
+sub check {
+ my ($self, $principal, $group, $type, $name) = @_;
+ unless ($principal) {
+ $self->error ('no principal specified');
+ return;
+ }
+ unless ($group) {
+ $self->error ('malformed nested ACL');
+ return;
+ }
+
+ # Make an ACL object just so that we can use it to drop back into the
+ # normal ACL validation after we have expanded the nesting.
+ my $acl;
+ eval { $acl = Wallet::ACL->new ($group, $self->{schema}) };
+
+ # Get the list of all nested acl entries within this entry, and use it
+ # to go through each entry and decide if the given acl has access.
+ my @members = $self->get_membership ($group);
+ for my $entry (@members) {
+ my ($scheme, $identifier) = @{ $entry };
+ my $result = $acl->check_line ($principal, $scheme, $identifier,
+ $type, $name);
+ return 1 if $result;
+ }
+ return 0;
+}
+
+# Get the membership of a group recursively. The final result will be a list
+# of arrayrefs like that from Wallet::ACL->list, but expanded for full
+# membership.
+sub get_membership {
+ my ($self, $group) = @_;
+
+ # Get the list of members for this nested acl. Consider any missing acls
+ # as empty.
+ my $schema = $self->{schema};
+ my @members;
+ eval {
+ my $acl = Wallet::ACL->new ($group, $schema);
+ @members = $acl->list;
+ };
+
+ # Now go through and expand any other nested groups into their own
+ # memberships.
+ my @expanded;
+ for my $entry (@members) {
+ my ($type, $name) = @{ $entry };
+ if ($type eq 'nested') {
+
+ # Keep track of things we've already expanded and don't look them
+ # up again.
+ next if exists $self->{expanded}{$name};
+ $self->{expanded}{$name} = 1;
+ push (@expanded, $self->get_membership ($name));
+
+ } else {
+ push (@expanded, $entry);
+ }
+ }
+
+ return @expanded;
+}
+
+1;
+__END__
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=for stopwords
+ACL Allbery verifier verifiers
+
+=head1 NAME
+
+Wallet::ACL::Nested - Wallet ACL verifier to check another ACL
+
+=head1 SYNOPSIS
+
+ my $verifier = Wallet::ACL::Nested->new;
+ my $status = $verifier->check ($principal, $acl);
+ if (not defined $status) {
+ die "Something failed: ", $verifier->error, "\n";
+ } elsif ($status) {
+ print "Access granted\n";
+ } else {
+ print "Access denied\n";
+ }
+
+=head1 DESCRIPTION
+
+Wallet::ACL::Nested checks whether the principal is permitted by another
+named ACL and, if so, returns success. It is used to nest one ACL inside
+another.
+
+=head1 METHODS
+
+=over 4
+
+=item new()
+
+Creates a new ACL verifier.
+
+=item check(PRINCIPAL, ACL)
+
+Returns true if PRINCIPAL is granted access according to the nested ACL,
+specified by name. Returns false if it is not, and undef on error.
+
+=item error([ERROR ...])
+
+Returns the error of the last failing operation or undef if no operations
+have failed. Callers should call this function to get the error message
+after an undef return from any other instance method. The returned errors
+will generally come from the nested child ACL.
+
+=back
+
+=head1 SEE ALSO
+
+Wallet::ACL(3), wallet-backend(8)
+
+This module is part of the wallet system. The current version is
+available from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+=head1 AUTHOR
+
+Jon Robertson <jonrober@stanford.edu>
+
+=cut
diff --git a/perl/lib/Wallet/ACL/NetDB.pm b/perl/lib/Wallet/ACL/NetDB.pm
index ad2164b..a4c7fb0 100644
--- a/perl/lib/Wallet/ACL/NetDB.pm
+++ b/perl/lib/Wallet/ACL/NetDB.pm
@@ -1,6 +1,7 @@
-# Wallet::ACL::NetDB -- Wallet NetDB role ACL verifier.
+# Wallet::ACL::NetDB -- Wallet NetDB role ACL verifier
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,21 +12,16 @@
##############################################################################
package Wallet::ACL::NetDB;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Wallet::ACL::Base;
use Wallet::Config;
-@ISA = qw(Wallet::ACL::Base);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.05';
+our @ISA = qw(Wallet::ACL::Base);
+our $VERSION = '1.03';
##############################################################################
# Interface
diff --git a/perl/lib/Wallet/ACL/NetDB/Root.pm b/perl/lib/Wallet/ACL/NetDB/Root.pm
index 34163e7..bfd13b4 100644
--- a/perl/lib/Wallet/ACL/NetDB/Root.pm
+++ b/perl/lib/Wallet/ACL/NetDB/Root.pm
@@ -1,6 +1,7 @@
-# Wallet::ACL::NetDB::Root -- Wallet NetDB role ACL verifier (root instances).
+# Wallet::ACL::NetDB::Root -- Wallet NetDB role ACL verifier (root instances)
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,21 +12,15 @@
##############################################################################
package Wallet::ACL::NetDB::Root;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Wallet::ACL::NetDB;
-use Wallet::Config;
-@ISA = qw(Wallet::ACL::NetDB);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.02';
+our @ISA = qw(Wallet::ACL::NetDB);
+our $VERSION = '1.03';
##############################################################################
# Interface
diff --git a/perl/lib/Wallet/Admin.pm b/perl/lib/Wallet/Admin.pm
index 8120e9c..9b63174 100644
--- a/perl/lib/Wallet/Admin.pm
+++ b/perl/lib/Wallet/Admin.pm
@@ -1,6 +1,7 @@
-# Wallet::Admin -- Wallet system administrative interface.
+# Wallet::Admin -- Wallet system administrative interface
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,19 +12,15 @@
##############################################################################
package Wallet::Admin;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($VERSION);
use Wallet::ACL;
use Wallet::Schema;
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.08';
+our $VERSION = '1.03';
# The last non-DBIx::Class version of Wallet::Schema. If a database has no
# DBIx::Class versioning, we artificially install this version number before
@@ -115,22 +112,25 @@ sub default_data {
# acl_schemes default rows.
my ($r1) = $self->{schema}->resultset('AclScheme')->populate ([
[ qw/as_name as_class/ ],
- [ 'krb5', 'Wallet::ACL::Krb5' ],
- [ 'krb5-regex', 'Wallet::ACL::Krb5::Regex' ],
- [ 'ldap-attr', 'Wallet::ACL::LDAP::Attribute' ],
- [ 'netdb', 'Wallet::ACL::NetDB' ],
- [ 'netdb-root', 'Wallet::ACL::NetDB::Root' ],
+ [ 'krb5', 'Wallet::ACL::Krb5' ],
+ [ 'krb5-regex', 'Wallet::ACL::Krb5::Regex' ],
+ [ 'ldap-attr', 'Wallet::ACL::LDAP::Attribute' ],
+ [ 'ldap-attr-root', 'Wallet::ACL::LDAP::Attribute::Root' ],
+ [ 'nested', 'Wallet::ACL::Nested' ],
+ [ 'netdb', 'Wallet::ACL::NetDB' ],
+ [ 'netdb-root', 'Wallet::ACL::NetDB::Root' ],
]);
warn "default AclScheme not installed" unless defined $r1;
# types default rows.
my @record = ([ qw/ty_name ty_class/ ],
[ 'duo', 'Wallet::Object::Duo' ],
- [ 'duo-ldap', 'Wallet::Object::Duo::LDAPProxy' ],
- [ 'duo-pam', 'Wallet::Object::Duo::PAM' ],
- [ 'duo-radius', 'Wallet::Object::Duo::RadiusProxy' ],
- [ 'duo-rdp', 'Wallet::Object::Duo::RDP' ],
+ [ 'duo-ldap', 'Wallet::Object::Duo' ],
+ [ 'duo-pam', 'Wallet::Object::Duo' ],
+ [ 'duo-radius', 'Wallet::Object::Duo' ],
+ [ 'duo-rdp', 'Wallet::Object::Duo' ],
[ 'file', 'Wallet::Object::File' ],
+ [ 'password', 'Wallet::Object::Password' ],
[ 'keytab', 'Wallet::Object::Keytab' ],
[ 'wa-keyring', 'Wallet::Object::WAKeyring' ]);
($r1) = $self->{schema}->resultset('Type')->populate (\@record);
diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm
index 2eb57f9..b8771c3 100644
--- a/perl/lib/Wallet/Config.pm
+++ b/perl/lib/Wallet/Config.pm
@@ -1,25 +1,22 @@
-# Wallet::Config -- Configuration handling for the wallet server.
+# Wallet::Config -- Configuration handling for the wallet server
#
# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2007, 2008, 2010, 2013, 2014
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2007, 2008, 2010, 2013, 2014, 2015
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
package Wallet::Config;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($PATH $VERSION);
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.05';
+our $VERSION = '1.03';
# Path to the config file to load.
-$PATH = $ENV{WALLET_CONFIG} || '/etc/wallet/wallet.conf';
+our $PATH = $ENV{WALLET_CONFIG} || '/etc/wallet/wallet.conf';
=head1 NAME
@@ -29,7 +26,7 @@ Wallet::Config - Configuration handling for the wallet server
DBI DSN SQLite subdirectories KEYTAB keytab kadmind KDC add-ons kadmin DNS
SRV kadmin keytabs remctl backend lowercased NETDB ACL NetDB unscoped
usernames rekey hostnames Allbery wallet-backend keytab-backend Heimdal
-rekeys WebAuth WEBAUTH keyring LDAP DN GSS-API integrations
+rekeys WebAuth WEBAUTH keyring LDAP DN GSS-API integrations msktutil
=head1 SYNOPSIS
@@ -260,6 +257,49 @@ our $FILE_MAX_SIZE;
=back
+=head1 PASSWORD OBJECT CONFIGURATION
+
+These configuration variables only need to be set if you intend to use the
+C<password> object type (the Wallet::Object::Password class). You will also
+need to set the FILE_MAX_SIZE value from the file object configuration, as
+that is inherited.
+
+=over 4
+
+=item PWD_FILE_BUCKET
+
+The directory into which to store password objects. Password objects will
+be stored in subdirectories of this directory. See
+L<Wallet::Object::Password> for the full details of the naming scheme. This
+directory must be writable by the wallet server and the wallet server must
+be able to create subdirectories of it.
+
+PWD_FILE_BUCKET must be set to use file objects.
+
+=cut
+
+our $PWD_FILE_BUCKET;
+
+=item PWD_LENGTH_MIN
+
+The minimum length for any auto-generated password objects created when get
+is run before data is stored.
+
+=cut
+
+our $PWD_LENGTH_MIN = 20;
+
+=item PWD_LENGTH_MAX
+
+The maximum length for any auto-generated password objects created when get
+is run before data is stored.
+
+=cut
+
+our $PWD_LENGTH_MAX = 21;
+
+=back
+
=head1 KEYTAB OBJECT CONFIGURATION
These configuration variables only need to be set if you intend to use the
@@ -275,7 +315,8 @@ modify, inspect, and delete any principals that should be managed by the
wallet. (In MIT Kerberos F<kadm5.acl> parlance, this is C<admci>
privileges.)
-KEYTAB_FILE must be set to use keytab objects.
+KEYTAB_FILE must be set to use keytab objects with any backend other than
+Active Directory.
=cut
@@ -292,16 +333,18 @@ is generally pointless and may interact poorly with the way C<addprinc
-randkey> works when third-party add-ons for password strength checking
are used.)
+This option is ignored when using Active Directory.
+
=cut
our $KEYTAB_FLAGS = '-clearpolicy';
=item KEYTAB_HOST
-Specifies the host on which the kadmin service is running. This setting
-overrides the C<admin_server> setting in the [realms] section of
-F<krb5.conf> and any DNS SRV records and allows the wallet to run on a
-system that doesn't have a Kerberos configuration for the wallet's realm.
+Specifies the host on which the kadmin or Active Directory service is running.
+This setting overrides the C<admin_server> setting in the [realms] section of
+F<krb5.conf> and any DNS SRV records and allows the wallet to run on a system
+that doesn't have a Kerberos configuration for the wallet's realm.
=cut
@@ -313,13 +356,15 @@ The path to the B<kadmin> command-line client. The default value is
C<kadmin>, which will cause the wallet to search for B<kadmin> on its
default PATH.
+This option is ignored when using Active Directory.
+
=cut
our $KEYTAB_KADMIN = 'kadmin';
=item KEYTAB_KRBTYPE
-The Kerberos KDC implementation type, either C<Heimdal> or C<MIT>
+The Kerberos KDC implementation type, chosen from C<AD>, C<Heimdal>, or C<MIT>
(case-insensitive). KEYTAB_KRBTYPE must be set to use keytab objects.
=cut
@@ -331,9 +376,9 @@ our $KEYTAB_KRBTYPE;
The principal whose key is stored in KEYTAB_FILE. The wallet will
authenticate as this principal to the kadmin service.
-KEYTAB_PRINCIPAL must be set to use keytab objects, at least until
-B<kadmin> is smart enough to use the first principal found in the keytab
-it's using for authentication.
+KEYTAB_PRINCIPAL must be set to use keytab objects unless Active Directory is
+the backend, at least until B<kadmin> is smart enough to use the first
+principal found in the keytab it's using for authentication.
=cut
@@ -347,7 +392,7 @@ installation and the keytab object names are stored without realm.
KEYTAB_REALM is added when talking to the KDC via B<kadmin>.
KEYTAB_REALM must be set to use keytab objects. C<ktadd> doesn't always
-default to the local realm.
+default to the local realm and the Active Directory integration requires it.
=cut
@@ -370,6 +415,69 @@ our $KEYTAB_TMP;
=back
+The following parameters are specific to generating keytabs from Active
+Directory (KEYTAB_KRBTYPE is set to C<AD>).
+
+=over 4
+
+=item AD_CACHE
+
+Specifies the ticket cache to use when manipulating Active Directory objects.
+The ticket cache must be for a principal able to bind to Active Directory and
+run B<msktutil>.
+
+AD_CACHE must be set to use Active Directory support.
+
+=cut
+
+our $AD_CACHE;
+
+=item AD_COMPUTER_DN
+
+The LDAP base DN for computer objects inside Active Directory. All keytabs of
+the form host/<hostname> will be mapped to objects with a C<samAccountName> of
+the <hostname> portion under this DN.
+
+AD_COMPUTER_DN must be set if using Active Directory as the keytab backend.
+
+=cut
+
+our $AD_COMPUTER_DN;
+
+=item AD_DEBUG
+
+If set to true, asks for some additional debugging information, such as the
+B<msktutil> command, to be logged to syslog. These debugging messages will be
+logged to the C<local3> facility.
+
+=cut
+
+our $AD_DEBUG = 0;
+
+=item AD_MSKTUTIL
+
+The path to the B<msktutil> command-line client. The default value is
+C<msktutil>, which will cause the wallet to search for B<msktutil> on its
+default PATH.
+
+=cut
+
+our $AD_MSKTUTIL = 'msktutil';
+
+=item AD_USER_DN
+
+The LDAP base DN for user objects inside Active Directory. All keytabs of the
+form service/<user> will be mapped to objects with a C<servicePrincipalName>
+matching the wallet object name under this DN.
+
+AD_USER_DN must be set if using Active Directory as the keytab backend.
+
+=cut
+
+our $AD_USER_DN;
+
+=back
+
=head2 Retrieving Existing Keytabs
Heimdal provides the choice, over the network protocol, of either
@@ -497,6 +605,36 @@ our $WAKEYRING_PURGE_INTERVAL = 60 * 60 * 24 * 90;
=back
+=head1 EXTERNAL ACL CONFIGURATION
+
+This configuration variable is only needed if you intend to use the
+C<external> ACL type (the Wallet::ACL::External class). This ACL type
+runs an external command to determine if access is granted.
+
+=over 4
+
+=item EXTERNAL_COMMAND
+
+Path to the command to run to determine whether access is granted. The first
+argument to the command will be the principal requesting access. The second
+and third arguments will be the type and name of the object that principal is
+requesting access to. The final argument will be the identifier of the ACL.
+
+No other arguments are passed to the command, but the command will have
+access to all of the remctl environment variables seen by the wallet
+server (such as REMOTE_USER). For a full list of environment variables,
+see L<remctld(8)/ENVIRONMENT>.
+
+The external command should exit with a non-zero status but no output to
+indicate a normal failure to satisfy the ACL. Any output will be treated
+as an error.
+
+=cut
+
+our $EXTERNAL_COMMAND;
+
+=back
+
=head1 LDAP ACL CONFIGURATION
These configuration variables are only needed if you intend to use the
@@ -749,6 +887,34 @@ keytab objects for particular principals have fully-qualified hostnames:
Objects that aren't of type C<keytab> or which aren't for a host-based key
have no naming requirements enforced by this example.
+=head1 OBJECT HOST-BASED NAMES
+
+The above demonstrates having a host-based naming convention, where we
+expect one part of an object name to be the name of the host that this
+object is for. The most obvious examples are those keytab objects
+above, where we want certain keytab names to be in the form of
+<service>/<hostname>. It's then also useful to provide a Perl function
+named is_for_host which then can be used to tell if a given object is a
+host-based keytab for a specific host. This function is then called by
+the objects_hostname in Wallet::Report to give a list of all host-based
+objects for a given hostname. It should return true if the given object
+is a host-based object for the hostname, otherwise false.
+
+An example that matches the same policy as the last verify_name example
+would be:
+
+ sub is_for_host {
+ my ($type, $name, $hostname) = @_;
+ my %host_based = map { $_ => 1 }
+ qw(HTTP cifs host imap ldap nfs pop sieve smtp webauth);
+ return 0 unless $type eq 'keytab';
+ return 0 unless $name =~ m%/%;
+ my ($service, $instance) = split ('/', $name, 2);
+ return 0 unless $host_based{$service};
+ return 1 if $hostname eq $instance;
+ return 0;
+ }
+
=head1 ACL NAMING ENFORCEMENT
Similar to object names, by default wallet permits administrators to
diff --git a/perl/lib/Wallet/Database.pm b/perl/lib/Wallet/Database.pm
index 3a4e130..23b059f 100644
--- a/perl/lib/Wallet/Database.pm
+++ b/perl/lib/Wallet/Database.pm
@@ -1,4 +1,4 @@
-# Wallet::Database -- Wallet system database connection management.
+# Wallet::Database -- Wallet system database connection management
#
# This module is a thin wrapper around DBIx::Class to handle determination
# of the database configuration settings automatically on connect. The
@@ -6,6 +6,7 @@
# like DBIx::Class objects in the rest of the code.
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2008, 2009, 2010, 2012, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -16,21 +17,16 @@
##############################################################################
package Wallet::Database;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
-use Wallet::Schema;
use Wallet::Config;
+use Wallet::Schema;
-@ISA = qw(Wallet::Schema);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.04';
+our @ISA = qw(Wallet::Schema);
+our $VERSION = '1.03';
##############################################################################
# Core overrides
diff --git a/perl/lib/Wallet/Kadmin.pm b/perl/lib/Wallet/Kadmin.pm
index 65a5700..8851c7e 100644
--- a/perl/lib/Wallet/Kadmin.pm
+++ b/perl/lib/Wallet/Kadmin.pm
@@ -1,6 +1,7 @@
-# Wallet::Kadmin -- Kerberos administration API for wallet keytab backend.
+# Wallet::Kadmin -- Kerberos administration API for wallet keytab backend
#
# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2009, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,18 +12,14 @@
##############################################################################
package Wallet::Kadmin;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($VERSION);
-use Wallet::Config ();
+use Wallet::Config;
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.03';
+our $VERSION = '1.03';
##############################################################################
# Utility functions for child classes
@@ -69,6 +66,9 @@ sub new {
} elsif (lc ($Wallet::Config::KEYTAB_KRBTYPE) eq 'heimdal') {
require Wallet::Kadmin::Heimdal;
$kadmin = Wallet::Kadmin::Heimdal->new;
+ } elsif (lc ($Wallet::Config::KEYTAB_KRBTYPE) eq 'ad') {
+ require Wallet::Kadmin::AD;
+ $kadmin = Wallet::Kadmin::AD->new;
} else {
my $type = $Wallet::Config::KEYTAB_KRBTYPE;
die "unknown KEYTAB_KRBTYPE setting: $type\n";
diff --git a/perl/lib/Wallet/Kadmin/AD.pm b/perl/lib/Wallet/Kadmin/AD.pm
new file mode 100644
index 0000000..5b71d41
--- /dev/null
+++ b/perl/lib/Wallet/Kadmin/AD.pm
@@ -0,0 +1,472 @@
+# Wallet::Kadmin::AD -- Wallet Kerberos administration API for AD
+#
+# Written by Bill MacAllister <bill@ca-zephyr.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2015 Dropbox, Inc.
+# Copyright 2007, 2008, 2009, 2010, 2014
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::Kadmin::AD;
+
+use 5.008;
+use strict;
+use warnings;
+
+use Authen::SASL;
+use Net::LDAP;
+use IPC::Run qw(run timeout);
+use Sys::Syslog qw(:standard :macros);
+use Wallet::Config;
+use Wallet::Kadmin;
+
+our @ISA = qw(Wallet::Kadmin);
+our $VERSION = '1.03';
+
+##############################################################################
+# kadmin Interaction
+##############################################################################
+
+# Send debugging output to syslog.
+
+sub ad_debug {
+ my ($self, $l, $m) = @_;
+ if (!$self->{SYSLOG}) {
+ openlog('wallet-server', 'ndelay,nofatal', 'local3');
+ $self->{SYSLOG} = 1;
+ }
+ syslog($l, $m);
+ return;
+}
+
+# Make sure that principals are well-formed and don't contain
+# characters that will cause us problems when talking to kadmin.
+# Takes a principal and returns true if it's okay, false otherwise.
+# Note that we do not permit realm information here.
+sub valid_principal {
+ my ($self, $principal) = @_;
+ my $valid = 0;
+ if ($principal =~ m,^(host|service)(/[\w_.-]+)?\z,) {
+ my $k_type = $1;
+ my $k_id = $2;
+ if ($k_type eq 'host') {
+ $valid = 1 if $k_id =~ m/[.]/xms;
+ } elsif ($k_type eq 'service') {
+ $valid = 1 if length($k_id) < 19;
+ }
+ }
+ return $valid;
+}
+
+# Connect to the Active Directory server using LDAP. The connection is
+# used to retrieve information about existing keytabs since msktutil
+# does not have this functionality.
+sub ldap_connect {
+ my ($self) = @_;
+
+ if (!-e $Wallet::Config::AD_CACHE) {
+ die 'Missing kerberos ticket cache ' . $Wallet::Config::AD_CACHE;
+ }
+
+ my $ldap;
+ eval {
+ local $ENV{KRB5CCNAME} = $Wallet::Config::AD_CACHE;
+ my $sasl = Authen::SASL->new(mechanism => 'GSSAPI');
+ $ldap = Net::LDAP->new($Wallet::Config::KEYTAB_HOST, onerror => 'die');
+ my $mesg = eval { $ldap->bind(undef, sasl => $sasl) };
+ };
+ if ($@) {
+ my $error = $@;
+ chomp $error;
+ 1 while ($error =~ s/ at \S+ line \d+\.?\z//);
+ die "LDAP bind to AD failed: $error\n";
+ }
+
+ return $ldap;
+}
+
+# Construct a base filter for searching Active Directory.
+
+sub ldap_base_filter {
+ my ($self, $principal) = @_;
+ my $base;
+ my $filter;
+ if ($principal =~ m,^host/(\S+),xms) {
+ my $fqdn = $1;
+ my $host = $fqdn;
+ $host =~ s/[.].*//xms;
+ $base = $Wallet::Config::AD_COMPUTER_DN;
+ $filter = "(samAccountName=${host}\$)";
+ } elsif ($principal =~ m,^service/(\S+),xms) {
+ my $id = $1;
+ $base = $Wallet::Config::AD_USER_DN;
+ $filter = "(servicePrincipalName=service/${id})";
+ }
+ return ($base, $filter);
+}
+
+# TODO: Get a keytab from the keytab cache.
+sub get_ad_keytab {
+ my ($self, $principal) = @_;
+ return;
+}
+
+# Run a msktutil command and capture the output. Returns the output,
+# either as a list of lines or, in scalar context, as one string.
+# Depending on the exit status of msktutil or on the eval trap to know
+# when the msktutil command fails. The error string returned from the
+# call to run frequently contains information about a success rather
+# that error output.
+sub msktutil {
+ my ($self, $args_ref) = @_;
+ unless (defined($Wallet::Config::KEYTAB_HOST)
+ and defined($Wallet::Config::KEYTAB_REALM))
+ {
+ die "keytab object implementation not configured\n";
+ }
+ unless (defined($Wallet::Config::AD_CACHE)
+ and defined($Wallet::Config::AD_COMPUTER_DN)
+ and defined($Wallet::Config::AD_USER_DN))
+ {
+ die "Active Directory support not configured\n";
+ }
+ my @args = @{$args_ref};
+ my @cmd = ($Wallet::Config::AD_MSKTUTIL);
+ push @cmd, @args;
+ if ($Wallet::Config::AD_DEBUG) {
+ $self->ad_debug('debug', join(' ', @cmd));
+ }
+
+ my $in;
+ my $out;
+ my $err;
+ my $err_msg;
+ my $err_no;
+ eval {
+ local $ENV{KRB5CCNAME} = $Wallet::Config::AD_CACHE;
+ run \@cmd, \$in, \$out, \$err, timeout(120);
+ if ($?) {
+ $err_no = $?;
+ }
+ };
+ if ($@) {
+ $err_msg .= "ERROR ($err_no): $@\n";
+ }
+ if ($err_no || $err_msg) {
+ if ($err) {
+ $err_msg .= "ERROR: $err\n";
+ $err_msg .= 'Problem command: ' . join(' ', @cmd) . "\n";
+ }
+ die $err_msg;
+ } else {
+ if ($err) {
+ $out .= "\n" . $err;
+ }
+ }
+ if ($Wallet::Config::AD_DEBUG) {
+ $self->ad_debug('debug', $out);
+ }
+ return $out;
+}
+
+# Either create or update a keytab for the principal. Return the
+# name of the keytab file created.
+sub ad_create_update {
+ my ($self, $principal, $action) = @_;
+ my $keytab = $Wallet::Config::KEYTAB_TMP . "/keytab.$$";
+ if (-e $keytab) {
+ unlink $keytab or die "Problem deleting $keytab\n";
+ }
+ my @cmd = ('--' . $action);
+ push @cmd, '--server', $Wallet::Config::AD_SERVER;
+ push @cmd, '--enctypes', '0x4';
+ push @cmd, '--enctypes', '0x8';
+ push @cmd, '--enctypes', '0x10';
+ push @cmd, '--keytab', $keytab;
+ push @cmd, '--realm', $Wallet::Config::KEYTAB_REALM;
+
+ if ($principal =~ m,^host/(\S+),xms) {
+ my $fqdn = $1;
+ my $host = $fqdn;
+ $host =~ s/[.].*//xms;
+ push @cmd, '--dont-expire-password';
+ push @cmd, '--computer-name', $host;
+ push @cmd, '--upn', "host/$fqdn";
+ push @cmd, '--hostname', $fqdn;
+ } elsif ($principal =~ m,^service/(\S+),xms) {
+ my $service_id = $1;
+ push @cmd, '--use-service-account';
+ push @cmd, '--service', "service/$service_id";
+ push @cmd, '--account-name', "srv-${service_id}";
+ push @cmd, '--no-pac';
+ }
+ my $out = $self->msktutil(\@cmd);
+ if ($out =~ /Error:\s+\S+\s+failed/xms) {
+ $self->ad_delete($principal);
+ my $m = "ERROR: problem creating keytab:\n" . $out;
+ $m .= 'INFO: the keytab used to by wallet probably has'
+ . " insufficient access to AD\n";
+ die $m;
+ }
+
+ return $keytab;
+}
+
+##############################################################################
+# Public interfaces
+##############################################################################
+
+# Set a callback to be called for forked kadmin processes.
+sub fork_callback {
+ my ($self, $callback) = @_;
+ $self->{fork_callback} = $callback;
+}
+
+# Check whether a given principal already exists. Returns true if so,
+# false otherwise. The best way to do this with AD is to perform an
+# ldap query.
+sub exists {
+ my ($self, $principal) = @_;
+ return unless $self->valid_principal($principal);
+
+ my $ldap = $self->ldap_connect();
+ my ($base, $filter) = $self->ldap_base_filter($principal);
+ my @attrs = ('objectClass', 'msds-KeyVersionNumber');
+
+ my $result;
+ eval {
+ $result = $ldap->search(
+ base => $base,
+ scope => 'subtree',
+ filter => $filter,
+ attrs => \@attrs
+ );
+ };
+
+ if ($@) {
+ my $error = $@;
+ die "LDAP search error: $error\n";
+ }
+ if ($result->code) {
+ my $m;
+ $m .= "INFO base:$base filter:$filter scope:subtree\n";
+ $m .= 'ERROR:' . $result->error . "\n";
+ die $m;
+ }
+ if ($result->count > 1) {
+ my $m = "ERROR: too many AD entries for this keytab\n";
+ for my $entry ($result->entries) {
+ $m .= 'INFO: dn found ' . $entry->dn . "\n";
+ }
+ die $m;
+ }
+ if ($result->count) {
+ for my $entry ($result->entries) {
+ return $entry->get_value('msds-KeyVersionNumber');
+ }
+ } else {
+ return 0;
+ }
+ return;
+}
+
+# Call msktutil to Create a principal in Kerberos. Sets the error and
+# returns undef on failure, and returns 1 on either success or if the
+# principal already exists. Note, this creates a keytab that is never
+# used because it is not returned to the user.
+sub create {
+ my ($self, $principal) = @_;
+ unless ($self->valid_principal($principal)) {
+ die "ERROR: invalid principal name $principal\n";
+ return;
+ }
+ if ($self->exists($principal)) {
+ if ($Wallet::Config::AD_DEBUG) {
+ $self->ad_debug('debug', "$principal exists");
+ }
+ return 1;
+ }
+ my $file = $self->ad_create_update($principal, 'create');
+ if (-e $file) {
+ unlink $file or die "Problem deleting $file\n";
+ }
+ return 1;
+}
+
+# TODO: Return a keytab. Need to create a local keytab cache when
+# a keytab is marked unchanging and return that.
+sub keytab {
+ my ($self, $principal) = @_;
+ unless ($self->valid_principal($principal)) {
+ die "ERROR: invalid principal name $principal\n";
+ return;
+ }
+ my $file = 'call to route to get the file name of local keytab file';
+ if (!-e $file) {
+ die "ERROR: keytab file $file does not exist.\n";
+ }
+ return $self->read_keytab($file);
+}
+
+# Update a keytab for a principal. This action changes the AD
+# password for the principal and increments the kvno. The enctypes
+# passed in are ignored.
+sub keytab_rekey {
+ my ($self, $principal, @enctypes) = @_;
+ unless ($self->valid_principal($principal)) {
+ die "ERROR: invalid principal name: $principal\n";
+ return;
+ }
+ if (!$self->exists($principal)) {
+ die "ERROR: $principal does not exist\n";
+ }
+ unless ($self->valid_principal($principal)) {
+ die "ERROR: invalid principal name $principal\n";
+ return;
+ }
+ my $file = $self->ad_create_update($principal, 'update');
+ return $self->read_keytab($file);
+}
+
+# Delete a principal from Kerberos. Return true if successful, false
+# otherwise. If the deletion fails, sets the error. If the principal
+# doesn't exist, return success; we're bringing reality in line with
+# our expectations. For AD this means just delete the object using
+# LDAP.
+sub destroy {
+ my ($self, $principal) = @_;
+ unless ($self->valid_principal($principal)) {
+ $self->error("invalid principal name: $principal");
+ }
+ my $exists = $self->exists($principal);
+ if (!defined $exists) {
+ return;
+ } elsif (not $exists) {
+ return 1;
+ }
+
+ return $self->ad_delete($principal);
+}
+
+# Delete an entry from AD using LDAP.
+
+sub ad_delete {
+ my ($self, $principal) = @_;
+
+ my $k_type;
+ my $k_id;
+ my $dn;
+ if ($principal =~ m,^(host|service)/(\S+),xms) {
+ $k_type = $1;
+ $k_id = $2;
+ if ($k_type eq 'host') {
+ my $host = $k_id;
+ $host =~ s/[.].*//;
+ $dn = "cn=${host}," . $Wallet::Config::AD_COMPUTER_DN;
+ } elsif ($k_type eq 'service') {
+ $dn = "cn=srv-${k_id}," . $Wallet::Config::AD_USER_DN;
+ }
+ }
+
+ my $ldap = $self->ldap_connect();
+ my $msgid = $ldap->delete($dn);
+ if ($msgid->code) {
+ my $m;
+ $m .= "ERROR: Problem deleting $dn\n";
+ $m .= $msgid->error;
+ die $m;
+ }
+ return 1;
+}
+
+# Create a new AD kadmin object. Very empty for the moment, but later it
+# will probably fill out if we go to using a module rather than calling
+# kadmin directly.
+sub new {
+ my ($class) = @_;
+ unless (defined($Wallet::Config::KEYTAB_TMP)) {
+ die "KEYTAB_TMP configuration variable not set\n";
+ }
+ my $self = {};
+ $self->{SYSLOG} = undef;
+ bless($self, $class);
+ return $self;
+}
+
+1;
+__END__
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=for stopwords
+rekeying rekeys remctl backend keytabs keytab kadmin KDC API Allbery
+unlinked MacAllister msktutil
+
+=head1 NAME
+
+Wallet::Kadmin::AD - Wallet Kerberos administration API for Active Directory
+
+=head1 SYNOPSIS
+
+ my $kadmin = Wallet::Kadmin::AD->new;
+ $kadmin->create ('host/foo.example.com');
+ my $data = $kadmin->keytab_rekey ('host/foo.example.com');
+ $data = $kadmin->keytab ('host/foo.example.com');
+ my $exists = $kadmin->exists ('host/oldshell.example.com');
+ $kadmin->destroy ('host/oldshell.example.com') if $exists;
+
+=head1 DESCRIPTION
+
+Wallet::Kadmin::AD implements the Wallet::Kadmin API for Active
+Directory Kerberos, providing an interface to create and delete
+principals and create keytabs. It provides the API documented in
+L<Wallet::Kadmin> for an Active Directory Kerberos KDC.
+
+AD Kerberos does not provide any method via msktutil to retrieve a
+keytab for a principal without rekeying it, so the keytab() method (as
+opposed to keytab_rekey(), which rekeys the principal) is implemented
+using a local keytab cache.
+
+To use this class, several configuration parameters must be set. See
+L<Wallet::Config/"KEYTAB OBJECT CONFIGURATION"> for details.
+
+=head1 FILES
+
+=over 4
+
+=item KEYTAB_TMP/keytab.<pid>
+
+The keytab is created in this file and then read into memory. KEYTAB_TMP
+is set in the wallet configuration, and <pid> is the process ID of the
+current process. The file is unlinked after being read.
+
+=back
+
+=head1 LIMITATIONS
+
+Currently, this implementation calls an external B<msktutil> program rather
+than using a native Perl module and therefore requires B<msktutil> be
+installed and parses its output.
+
+=head1 SEE ALSO
+
+msktutil, Wallet::Config(3), Wallet::Kadmin(3),
+Wallet::Object::Keytab(3), wallet-backend(8)
+
+This module is part of the wallet system. The current version is
+available from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+=head1 AUTHORS
+
+Bill MacAllister <whm@dropbox.com>
+and Russ Allbery <eagle@eyrie.org>
+and Jon Robertson <jonrober@stanford.edu>.
+
+=cut
diff --git a/perl/lib/Wallet/Kadmin/Heimdal.pm b/perl/lib/Wallet/Kadmin/Heimdal.pm
index 1208801..22bdd59 100644
--- a/perl/lib/Wallet/Kadmin/Heimdal.pm
+++ b/perl/lib/Wallet/Kadmin/Heimdal.pm
@@ -1,6 +1,7 @@
-# Wallet::Kadmin::Heimdal -- Wallet Kerberos administration API for Heimdal.
+# Wallet::Kadmin::Heimdal -- Wallet Kerberos administration API for Heimdal
#
# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2009, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,22 +12,17 @@
##############################################################################
package Wallet::Kadmin::Heimdal;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Heimdal::Kadm5 qw(KRB5_KDB_DISALLOW_ALL_TIX);
-use Wallet::Config ();
-use Wallet::Kadmin ();
+use Wallet::Config;
+use Wallet::Kadmin;
-@ISA = qw(Wallet::Kadmin);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.04';
+our @ISA = qw(Wallet::Kadmin);
+our $VERSION = '1.03';
##############################################################################
# Utility functions
diff --git a/perl/lib/Wallet/Kadmin/MIT.pm b/perl/lib/Wallet/Kadmin/MIT.pm
index ac45265..9f0f50f 100644
--- a/perl/lib/Wallet/Kadmin/MIT.pm
+++ b/perl/lib/Wallet/Kadmin/MIT.pm
@@ -1,7 +1,8 @@
-# Wallet::Kadmin::MIT -- Wallet Kerberos administration API for MIT.
+# Wallet::Kadmin::MIT -- Wallet Kerberos administration API for MIT
#
# Written by Russ Allbery <eagle@eyrie.org>
# Pulled into a module by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2008, 2009, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -12,21 +13,17 @@
##############################################################################
package Wallet::Kadmin::MIT;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
-use Wallet::Config ();
-use Wallet::Kadmin ();
+use POSIX qw(_exit);
+use Wallet::Config;
+use Wallet::Kadmin;
-@ISA = qw(Wallet::Kadmin);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.03';
+our @ISA = qw(Wallet::Kadmin);
+our $VERSION = '1.03';
##############################################################################
# kadmin Interaction
@@ -65,11 +62,11 @@ sub kadmin {
$self->{fork_callback} () if $self->{fork_callback};
unless (open (STDERR, '>&STDOUT')) {
warn "wallet: cannot dup stdout: $!\n";
- exit 1;
+ _exit(1);
}
unless (exec ($Wallet::Config::KEYTAB_KADMIN, @args)) {
warn "wallet: cannot run $Wallet::Config::KEYTAB_KADMIN: $!\n";
- exit 1;
+ _exit(1);
}
}
local $_;
diff --git a/perl/lib/Wallet/Object/Base.pm b/perl/lib/Wallet/Object/Base.pm
index bdd61fb..221031f 100644
--- a/perl/lib/Wallet/Object/Base.pm
+++ b/perl/lib/Wallet/Object/Base.pm
@@ -1,6 +1,7 @@
-# Wallet::Object::Base -- Parent class for any object stored in the wallet.
+# Wallet::Object::Base -- Parent class for any object stored in the wallet
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2008, 2010, 2011, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,22 +12,17 @@
##############################################################################
package Wallet::Object::Base;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($VERSION);
use DateTime;
use Date::Parse qw(str2time);
-use DBI;
use Text::Wrap qw(wrap);
use Wallet::ACL;
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.08';
+our $VERSION = '1.03';
##############################################################################
# Constructors
@@ -609,6 +605,15 @@ sub history {
# The get methods must always be overridden by the subclass.
sub get { die "Do not instantiate Wallet::Object::Base directly\n"; }
+# The update method should only work if a subclass supports it as something
+# different from get. That makes it explicit about whether the subclass has
+# a meaningful update.
+sub update {
+ my ($self) = @_;
+ $self->error ("update is not supported for this type, use get instead");
+ return;
+}
+
# Provide a default store implementation that returns an immutable object
# error so that auto-generated types don't have to provide their own.
sub store {
diff --git a/perl/lib/Wallet/Object/Duo.pm b/perl/lib/Wallet/Object/Duo.pm
index d08294b..1aca979 100644
--- a/perl/lib/Wallet/Object/Duo.pm
+++ b/perl/lib/Wallet/Object/Duo.pm
@@ -1,7 +1,8 @@
# Wallet::Object::Duo -- Base Duo object implementation for the wallet
#
# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2014
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2014, 2015
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -11,25 +12,111 @@
##############################################################################
package Wallet::Object::Duo;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use JSON;
-use Net::Duo::Admin;
-use Net::Duo::Admin::Integration;
use Perl6::Slurp qw(slurp);
-use Wallet::Config ();
+use Wallet::Config;
use Wallet::Object::Base;
-@ISA = qw(Wallet::Object::Base);
+our @ISA = qw(Wallet::Object::Base);
+our $VERSION = '1.03';
+
+# Mappings from our types into what Duo calls the integration types.
+our %DUO_TYPES = (
+ 'duo' => {
+ integration => 'unix',
+ output => \&_output_generic,
+ },
+ 'duo-ldap' => {
+ integration => 'ldapproxy',
+ output => \&_output_ldap,
+ },
+ 'duo-pam' => {
+ integration => 'unix',
+ output => \&_output_pam,
+ },
+ 'duo-radius' => {
+ integration => 'radius',
+ output => \&_output_radius,
+ },
+ );
+
+# Extra types to add. These are all just named as the Duo integration name
+# with duo- before it and go to the generic output. Put them here to prevent
+# pages of settings. These are also not all actually set as types in the
+# types table to prevent overpopulation. You should manually create the
+# entries in that table for any Duo integrations you want to add.
+our @EXTRA_TYPES = ('accountsapi', 'adfs', 'adminapi', 'array', 'barracuda',
+ 'cisco', 'citrixcag', 'citrixns', 'confluence', 'drupal',
+ 'f5bigip', 'f5firepass', 'fortinet', 'jira', 'juniper',
+ 'juniperuac', 'lastpass', 'okta', 'onelogin', 'openvpn',
+ 'openvpnas', 'owa', 'paloalto', 'rdgateway', 'rdp',
+ 'rdweb', 'rest', 'rras', 'shibboleth', 'sonicwallsra',
+ 'splunk', 'tmg', 'uag', 'verify', 'vmwareview', 'websdk',
+ 'wordpress');
+for my $type (@EXTRA_TYPES) {
+ my $wallet_type = 'duo-'.$type;
+ $DUO_TYPES{$wallet_type}{integration} = $type;
+ $DUO_TYPES{$wallet_type}{output} = \&_output_generic;
+};
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.02';
+##############################################################################
+# Get output methods
+##############################################################################
+
+# Output for any miscellaneous Duo integration, usually those that use a GUI
+# to set information and so don't need a custom configuration file.
+sub _output_generic {
+ my ($key, $secret, $hostname) = @_;
+
+ my $output;
+ $output .= "Integration key: $key\n";
+ $output .= "Secret key: $secret\n";
+ $output .= "Host: $hostname\n";
+
+ return $output;
+}
+
+# Output for the Duo unix integration, which hooks into the PAM stack.
+sub _output_pam {
+ my ($key, $secret, $hostname) = @_;
+
+ my $output = "[duo]\n";
+ $output .= "ikey = $key\n";
+ $output .= "skey = $secret\n";
+ $output .= "host = $hostname\n";
+
+ return $output;
+}
+
+# Output for the radius proxy, which can be plugged into the proxy config.
+sub _output_radius {
+ my ($key, $secret, $hostname) = @_;
+
+ my $output = "[radius_server_challenge]\n";
+ $output .= "ikey = $key\n";
+ $output .= "skey = $secret\n";
+ $output .= "api_host = $hostname\n";
+ $output .= "client = radius_client\n";
+
+ return $output;
+}
+
+# Output for the LDAP proxy, which can be plugged into the proxy config.
+sub _output_ldap {
+ my ($key, $secret, $hostname) = @_;
+
+ my $output = "[ldap_server_challenge]\n";
+ $output .= "ikey = $key\n";
+ $output .= "skey = $secret\n";
+ $output .= "api_host = $hostname\n";
+
+ return $output;
+}
##############################################################################
# Core methods
@@ -66,8 +153,20 @@ sub new {
my $key_file = $Wallet::Config::DUO_KEY_FILE;
my $agent = $Wallet::Config::DUO_AGENT;
+ # Check that we can load all of the required modules.
+ eval {
+ require Net::Duo;
+ require Net::Duo::Admin;
+ require Net::Duo::Admin::Integration;
+ };
+ if ($@) {
+ my $error = $@;
+ chomp $error;
+ 1 while ($error =~ s/ at \S+ line \d+\.?\z//);
+ die "Duo object support not available: $error\n";
+ }
+
# Construct the Net::Duo::Admin object.
- require Net::Duo::Admin;
my $duo = Net::Duo::Admin->new (
{
key_file => $key_file,
@@ -86,7 +185,7 @@ sub new {
# great here since we don't have a way to communicate the error back to the
# caller.
sub create {
- my ($class, $type, $name, $schema, $creator, $host, $time, $duo_type) = @_;
+ my ($class, $type, $name, $schema, $creator, $host, $time) = @_;
# We have to have a Duo integration key file set.
if (not $Wallet::Config::DUO_KEY_FILE) {
@@ -95,8 +194,26 @@ sub create {
my $key_file = $Wallet::Config::DUO_KEY_FILE;
my $agent = $Wallet::Config::DUO_AGENT;
+ # Make sure this is actually a type we know about, since this handler
+ # can handle many types.
+ if (!exists $DUO_TYPES{$type}) {
+ die "$type is not a valid duo integration\n";
+ }
+
+ # Check that we can load all of the required modules.
+ eval {
+ require Net::Duo;
+ require Net::Duo::Admin;
+ require Net::Duo::Admin::Integration;
+ };
+ if ($@) {
+ my $error = $@;
+ chomp $error;
+ 1 while ($error =~ s/ at \S+ line \d+\.?\z//);
+ die "Duo object support not available: $error\n";
+ }
+
# Construct the Net::Duo::Admin object.
- require Net::Duo::Admin;
my $duo = Net::Duo::Admin->new (
{
key_file => $key_file,
@@ -105,8 +222,7 @@ sub create {
);
# Create the object in Duo.
- require Net::Duo::Admin::Integration;
- $duo_type ||= $Wallet::Config::DUO_TYPE;
+ my $duo_type = $DUO_TYPES{$type}{integration};
my %data = (
name => "$name ($duo_type)",
notes => 'Managed by wallet',
@@ -201,11 +317,17 @@ sub get {
my $json = JSON->new->utf8 (1)->relaxed (1);
my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE);
- # Construct the returned file.
- my $output;
- $output .= "Integration key: $key\n";
- $output .= 'Secret key: ' . $integration->secret_key . "\n";
- $output .= "Host: $config->{api_hostname}\n";
+ # Construct the returned file. Assume the generic handler in case there
+ # is no valid handler, though that shouldn't happen.
+ my $output_sub;
+ my $type = $self->{type};
+ if (exists $DUO_TYPES{$type}{output}) {
+ $output_sub = $DUO_TYPES{$type}{output};
+ } else {
+ $output_sub = \&_output_generic;
+ }
+ my $output = $output_sub->($key, $integration->secret_key,
+ $config->{api_hostname});
# Log the action and return.
$self->log_action ('get', $user, $host, $time);
diff --git a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm
deleted file mode 100644
index 74ff43c..0000000
--- a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm
+++ /dev/null
@@ -1,202 +0,0 @@
-# Wallet::Object::Duo::LDAPProxy -- Duo auth proxy integration for LDAP
-#
-# Written by Jon Robertson <jonrober@stanford.edu>
-# Copyright 2014
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-##############################################################################
-# Modules and declarations
-##############################################################################
-
-package Wallet::Object::Duo::LDAPProxy;
-require 5.006;
-
-use strict;
-use warnings;
-use vars qw(@ISA $VERSION);
-
-use JSON;
-use Net::Duo::Admin;
-use Net::Duo::Admin::Integration;
-use Perl6::Slurp qw(slurp);
-use Wallet::Config ();
-use Wallet::Object::Duo;
-
-@ISA = qw(Wallet::Object::Duo);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
-
-##############################################################################
-# Core methods
-##############################################################################
-
-# Override create to provide the specific Duo integration type that will be
-# used in the remote Duo record.
-sub create {
- my ($class, $type, $name, $schema, $creator, $host, $time) = @_;
-
- $time ||= time;
- my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host,
- $time, 'ldapproxy');
- return $self;
-}
-
-# Override get to output the data in a specific format used for Duo LDAP
-# integration
-sub get {
- my ($self, $user, $host, $time) = @_;
- $time ||= time;
-
- # Check that the object isn't locked.
- my $id = $self->{type} . ':' . $self->{name};
- if ($self->flag_check ('locked')) {
- $self->error ("cannot get $id: object is locked");
- return;
- }
-
- # Retrieve the integration from Duo.
- my $key;
- eval {
- my %search = (du_name => $self->{name});
- my $row = $self->{schema}->resultset ('Duo')->find (\%search);
- $key = $row->get_column ('du_key');
- };
- if ($@) {
- $self->error ($@);
- return;
- }
- my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key);
-
- # We also need the admin server name, which we can get from the Duo object
- # configuration with a bit of JSON decoding.
- my $json = JSON->new->utf8 (1)->relaxed (1);
- my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE);
-
- # Construct the returned file.
- my $output = "[ldap_server_challenge]\n";
- $output .= "ikey = $key\n";
- $output .= 'skey = ' . $integration->secret_key . "\n";
- $output .= "api_host = $config->{api_hostname}\n";
-
- # Log the action and return.
- $self->log_action ('get', $user, $host, $time);
- return $output;
-}
-
-1;
-__END__
-
-##############################################################################
-# Documentation
-##############################################################################
-
-=for stopwords
-Allbery Duo integration DBH keytab LDAP auth
-
-=head1 NAME
-
-Wallet::Object::Duo::LDAPProxy -- Duo auth proxy integration for LDAP
-
-=head1 SYNOPSIS
-
- my @name = qw(duo-ldap host.example.com);
- my @trace = ($user, $host, time);
- my $object = Wallet::Object::Duo::LDAPProxy->create (@name, $schema, @trace);
- my $config = $object->get (@trace);
- $object->destroy (@trace);
-
-=head1 DESCRIPTION
-
-Wallet::Object::Duo::LDAPProxy is a representation of Duo
-integrations with the wallet, specifically to output Duo integrations
-in a format that can easily be pulled into configuring the Duo
-Authentication Proxy for LDAP. It implements the wallet object API
-and provides the necessary glue to create a Duo integration, return a
-configuration file containing the key and API information for that
-integration, and delete the integration from Duo when the wallet object
-is destroyed.
-
-The integration information is always returned in the configuration file
-format expected by the Authentication Proxy for Duo in configuring it
-for LDAP.
-
-This object can be retrieved repeatedly without changing the secret key,
-matching Duo's native behavior with integrations. To change the keys of
-the integration, delete it and recreate it.
-
-To use this object, at least one configuration parameter must be set. See
-L<Wallet::Config> for details on supported configuration parameters and
-information about how to set wallet configuration.
-
-=head1 METHODS
-
-This object mostly inherits from Wallet::Object::Duo. See the
-documentation for that class for all generic methods. Below are only
-those methods that are overridden or behave specially for this
-implementation.
-
-=over 4
-
-=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME])
-
-This will override the Wallet::Object::Duo class with the information
-needed to create a specific integration type in Duo. It creates a new
-object with the given TYPE and NAME (TYPE is normally C<duo-ldap> and
-must be for the rest of the wallet system to use the right class, but
-this module doesn't check for ease of subclassing), using DBH as the
-handle to the wallet metadata database. PRINCIPAL, HOSTNAME, and
-DATETIME are stored as history information. PRINCIPAL should be the
-user who is creating the object. If DATETIME isn't given, the current
-time is used.
-
-When a new Duo integration object is created, a new integration will be
-created in the configured Duo account and the integration key will be
-stored in the wallet object. If the integration already exists, create()
-will fail.
-
-If create() fails, it throws an exception.
-
-=item get(PRINCIPAL, HOSTNAME [, DATETIME])
-
-Retrieves the configuration information for the Duo integration and
-returns that information in the format expected by the configuration file
-for the Duo UNIX integration. Returns undef on failure. The caller
-should call error() to get the error message if get() returns undef.
-
-The returned configuration look look like:
-
- [ldap_server_challenge]
- ikey = <integration-key>
- skey = <secret-key>
- api_host = <api-hostname>
-
-The C<host> parameter will be taken from the configuration file pointed
-to by the DUO_KEY_FILE configuration variable.
-
-PRINCIPAL, HOSTNAME, and DATETIME are stored as history information.
-PRINCIPAL should be the user who is downloading the keytab. If DATETIME
-isn't given, the current time is used.
-
-=back
-
-=head1 LIMITATIONS
-
-Only one Duo account is supported for a given wallet implementation.
-
-=head1 SEE ALSO
-
-Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8)
-
-This module is part of the wallet system. The current version is
-available from L<http://www.eyrie.org/~eagle/software/wallet/>.
-
-=head1 AUTHORS
-
-Jon Robertson <jonrober@stanford.edu>
-
-=cut
diff --git a/perl/lib/Wallet/Object/Duo/PAM.pm b/perl/lib/Wallet/Object/Duo/PAM.pm
deleted file mode 100644
index 6f90ba1..0000000
--- a/perl/lib/Wallet/Object/Duo/PAM.pm
+++ /dev/null
@@ -1,205 +0,0 @@
-# Wallet::Object::Duo::PAM -- Duo PAM int. object implementation for wallet
-#
-# Written by Russ Allbery <eagle@eyrie.org>
-# Jon Robertson <jonrober@stanford.edu>
-# Copyright 2014
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-##############################################################################
-# Modules and declarations
-##############################################################################
-
-package Wallet::Object::Duo::PAM;
-require 5.006;
-
-use strict;
-use warnings;
-use vars qw(@ISA $VERSION);
-
-use JSON;
-use Net::Duo::Admin;
-use Net::Duo::Admin::Integration;
-use Perl6::Slurp qw(slurp);
-use Wallet::Config ();
-use Wallet::Object::Duo;
-
-@ISA = qw(Wallet::Object::Duo);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
-
-##############################################################################
-# Core methods
-##############################################################################
-
-# Override create to provide the specific Duo integration type that will be
-# used in the remote Duo record.
-sub create {
- my ($class, $type, $name, $schema, $creator, $host, $time) = @_;
-
- $time ||= time;
- my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host,
- $time, 'unix');
- return $self;
-}
-
-# Override get to output the data in a specific format used by Duo's PAM
-# module.
-sub get {
- my ($self, $user, $host, $time) = @_;
- $time ||= time;
-
- # Check that the object isn't locked.
- my $id = $self->{type} . ':' . $self->{name};
- if ($self->flag_check ('locked')) {
- $self->error ("cannot get $id: object is locked");
- return;
- }
-
- # Retrieve the integration from Duo.
- my $key;
- eval {
- my %search = (du_name => $self->{name});
- my $row = $self->{schema}->resultset ('Duo')->find (\%search);
- $key = $row->get_column ('du_key');
- };
- if ($@) {
- $self->error ($@);
- return;
- }
- my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key);
-
- # We also need the admin server name, which we can get from the Duo object
- # configuration with a bit of JSON decoding.
- my $json = JSON->new->utf8 (1)->relaxed (1);
- my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE);
-
- # Construct the returned file.
- my $output = "[duo]\n";
- $output .= "ikey = $key\n";
- $output .= 'skey = ' . $integration->secret_key . "\n";
- $output .= "host = $config->{api_hostname}\n";
-
- # Log the action and return.
- $self->log_action ('get', $user, $host, $time);
- return $output;
-}
-
-1;
-__END__
-
-##############################################################################
-# Documentation
-##############################################################################
-
-=for stopwords
-Allbery Duo integration DBH keytab
-
-=head1 NAME
-
-Wallet::Object::Duo::PAM -- Duo PAM int. object implementation for wallet
-
-=head1 SYNOPSIS
-
- my @name = qw(duo-pam host.example.com);
- my @trace = ($user, $host, time);
- my $object = Wallet::Object::Duo::PAM->create (@name, $schema, @trace);
- my $config = $object->get (@trace);
- $object->destroy (@trace);
-
-=head1 DESCRIPTION
-
-Wallet::Object::Duo::PAM is a representation of Duo integrations with
-the wallet, specifically to output Duo integrations in a format that
-can easily be pulled into configuring the Duo PAM interface. It
-implements the wallet object API and provides the necessary glue to
-create a Duo integration, return a configuration file containing the key
-and API information for that integration, and delete the integration from
-Duo when the wallet object is destroyed.
-
-The integration information is always returned in the configuration file
-format expected by the Duo UNIX integration. The results of retrieving
-this object will be text, suitable for putting in the UNIX integration
-configuration file, containing the integration key, secret key, and admin
-hostname for that integration.
-
-This object can be retrieved repeatedly without changing the secret key,
-matching Duo's native behavior with integrations. To change the keys of
-the integration, delete it and recreate it.
-
-To use this object, at least one configuration parameter must be set. See
-L<Wallet::Config> for details on supported configuration parameters and
-information about how to set wallet configuration.
-
-=head1 METHODS
-
-This object mostly inherits from Wallet::Object::Duo. See the
-documentation for that class for all generic methods. Below are only
-those methods that are overridden or behave specially for this
-implementation.
-
-=over 4
-
-=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME])
-
-This will override the Wallet::Object::Duo class with the information
-needed to create a specific integration type in Duo. It creates a new
-object with the given TYPE and NAME (TYPE is normally C<duo-pam> and must
-be for the rest of the wallet system to use the right class, but this
-module doesn't check for ease of subclassing), using DBH as the handle
-to the wallet metadata database. PRINCIPAL, HOSTNAME, and DATETIME are
-stored as history information. PRINCIPAL should be the user who is
-creating the object. If DATETIME isn't given, the current time is
-used.
-
-When a new Duo integration object is created, a new integration will be
-created in the configured Duo account and the integration key will be
-stored in the wallet object. If the integration already exists, create()
-will fail.
-
-If create() fails, it throws an exception.
-
-=item get(PRINCIPAL, HOSTNAME [, DATETIME])
-
-Retrieves the configuration information for the Duo integration and
-returns that information in the format expected by the configuration file
-for the Duo UNIX integration. Returns undef on failure. The caller
-should call error() to get the error message if get() returns undef.
-
-The returned configuration look look like:
-
- [duo]
- ikey = <integration-key>
- skey = <secret-key>
- host = <api-hostname>
-
-The C<host> parameter will be taken from the configuration file pointed
-to by the DUO_KEY_FILE configuration variable.
-
-PRINCIPAL, HOSTNAME, and DATETIME are stored as history information.
-PRINCIPAL should be the user who is downloading the keytab. If DATETIME
-isn't given, the current time is used.
-
-=back
-
-=head1 LIMITATIONS
-
-Only one Duo account is supported for a given wallet implementation.
-
-=head1 SEE ALSO
-
-Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8)
-
-This module is part of the wallet system. The current version is
-available from L<http://www.eyrie.org/~eagle/software/wallet/>.
-
-=head1 AUTHORS
-
-Russ Allbery <eagle@eyrie.org>
-Jon Robertson <eagle@eyrie.org>
-
-=cut
diff --git a/perl/lib/Wallet/Object/Duo/RDP.pm b/perl/lib/Wallet/Object/Duo/RDP.pm
deleted file mode 100644
index 2e975fc..0000000
--- a/perl/lib/Wallet/Object/Duo/RDP.pm
+++ /dev/null
@@ -1,204 +0,0 @@
-# Wallet::Object::Duo::RDP -- Duo RDP int. object implementation for wallet
-#
-# Written by Russ Allbery <eagle@eyrie.org>
-# Jon Robertson <jonrober@stanford.edu>
-# Copyright 2014
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-##############################################################################
-# Modules and declarations
-##############################################################################
-
-package Wallet::Object::Duo::RDP;
-require 5.006;
-
-use strict;
-use warnings;
-use vars qw(@ISA $VERSION);
-
-use JSON;
-use Net::Duo::Admin;
-use Net::Duo::Admin::Integration;
-use Perl6::Slurp qw(slurp);
-use Wallet::Config ();
-use Wallet::Object::Duo;
-
-@ISA = qw(Wallet::Object::Duo);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
-
-##############################################################################
-# Core methods
-##############################################################################
-
-# Override create to provide the specific Duo integration type that will be
-# used in the remote Duo record.
-sub create {
- my ($class, $type, $name, $schema, $creator, $host, $time) = @_;
-
- $time ||= time;
- my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host,
- $time, 'rdp');
- return $self;
-}
-
-# Override get to output the data in a specific format used by Duo's RDP
-# module.
-sub get {
- my ($self, $user, $host, $time) = @_;
- $time ||= time;
-
- # Check that the object isn't locked.
- my $id = $self->{type} . ':' . $self->{name};
- if ($self->flag_check ('locked')) {
- $self->error ("cannot get $id: object is locked");
- return;
- }
-
- # Retrieve the integration from Duo.
- my $key;
- eval {
- my %search = (du_name => $self->{name});
- my $row = $self->{schema}->resultset ('Duo')->find (\%search);
- $key = $row->get_column ('du_key');
- };
- if ($@) {
- $self->error ($@);
- return;
- }
- my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key);
-
- # We also need the admin server name, which we can get from the Duo object
- # configuration with a bit of JSON decoding.
- my $json = JSON->new->utf8 (1)->relaxed (1);
- my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE);
-
- # Construct the returned file.
- my $output;
- $output .= "Integration key: $key\n";
- $output .= 'Secret key: ' . $integration->secret_key . "\n";
- $output .= "Host: $config->{api_hostname}\n";
-
- # Log the action and return.
- $self->log_action ('get', $user, $host, $time);
- return $output;
-}
-
-1;
-__END__
-
-##############################################################################
-# Documentation
-##############################################################################
-
-=for stopwords
-Allbery Duo integration DBH keytab RDP
-
-=head1 NAME
-
-Wallet::Object::Duo::RDP -- Duo RDP int. object implementation for wallet
-
-=head1 SYNOPSIS
-
- my @name = qw(duo-rdp host.example.com);
- my @trace = ($user, $host, time);
- my $object = Wallet::Object::Duo::RDP->create (@name, $schema, @trace);
- my $config = $object->get (@trace);
- $object->destroy (@trace);
-
-=head1 DESCRIPTION
-
-Wallet::Object::Duo::RDP is a representation of Duo integrations with
-the wallet, specifically to output Duo integrations to set up an RDP
-integration. This can be used to set up remote logins, or all Windows
-logins period if so selected in Duo's software. It implements the
-wallet object API and provides the necessary glue to create a Duo
-integration, return a configuration file containing the key and API
-information for that integration, and delete the integration from Duo
-when the wallet object is destroyed.
-
-Because the Duo RDP software is configured by a GUI, the information
-returned for a get operation is a simple set that's readable but not
-useful for directly plugging into a config file. The values would need
-to be cut and pasted into the GUI.
-
-This object can be retrieved repeatedly without changing the secret key,
-matching Duo's native behavior with integrations. To change the keys of
-the integration, delete it and recreate it.
-
-To use this object, at least one configuration parameter must be set. See
-L<Wallet::Config> for details on supported configuration parameters and
-information about how to set wallet configuration.
-
-=head1 METHODS
-
-This object mostly inherits from Wallet::Object::Duo. See the
-documentation for that class for all generic methods. Below are only
-those methods that are overridden or behave specially for this
-implementation.
-
-=over 4
-
-=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME])
-
-This will override the Wallet::Object::Duo class with the information
-needed to create a specific integration type in Duo. It creates a new
-object with the given TYPE and NAME (TYPE is normally C<duo-pam> and must
-be for the rest of the wallet system to use the right class, but this
-module doesn't check for ease of subclassing), using DBH as the handle
-to the wallet metadata database. PRINCIPAL, HOSTNAME, and DATETIME are
-stored as history information. PRINCIPAL should be the user who is
-creating the object. If DATETIME isn't given, the current time is
-used.
-
-When a new Duo integration object is created, a new integration will be
-created in the configured Duo account and the integration key will be
-stored in the wallet object. If the integration already exists, create()
-will fail.
-
-If create() fails, it throws an exception.
-
-=item get(PRINCIPAL, HOSTNAME [, DATETIME])
-
-Retrieves the configuration information for the Duo integration and
-returns that information in the format expected by the configuration file
-for the Duo UNIX integration. Returns undef on failure. The caller
-should call error() to get the error message if get() returns undef.
-
-The returned configuration look look like:
-
- Integration key: <integration-key>
- Secret key: <secret-key>
- Host: <api-hostname>
-
-The C<host> parameter will be taken from the configuration file pointed
-to by the DUO_KEY_FILE configuration variable.
-
-PRINCIPAL, HOSTNAME, and DATETIME are stored as history information.
-PRINCIPAL should be the user who is downloading the keytab. If DATETIME
-isn't given, the current time is used.
-
-=back
-
-=head1 LIMITATIONS
-
-Only one Duo account is supported for a given wallet implementation.
-
-=head1 SEE ALSO
-
-Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8)
-
-This module is part of the wallet system. The current version is
-available from L<http://www.eyrie.org/~eagle/software/wallet/>.
-
-=head1 AUTHORS
-
-Russ Allbery <eagle@eyrie.org>
-Jon Robertson <eagle@eyrie.org>
-
-=cut
diff --git a/perl/lib/Wallet/Object/Duo/RadiusProxy.pm b/perl/lib/Wallet/Object/Duo/RadiusProxy.pm
deleted file mode 100644
index faa0c2f..0000000
--- a/perl/lib/Wallet/Object/Duo/RadiusProxy.pm
+++ /dev/null
@@ -1,204 +0,0 @@
-# Wallet::Object::Duo::RadiusProxy -- Duo auth proxy integration for radius
-#
-# Written by Jon Robertson <jonrober@stanford.edu>
-# Copyright 2014
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-##############################################################################
-# Modules and declarations
-##############################################################################
-
-package Wallet::Object::Duo::RadiusProxy;
-require 5.006;
-
-use strict;
-use warnings;
-use vars qw(@ISA $VERSION);
-
-use JSON;
-use Net::Duo::Admin;
-use Net::Duo::Admin::Integration;
-use Perl6::Slurp qw(slurp);
-use Wallet::Config ();
-use Wallet::Object::Duo;
-
-@ISA = qw(Wallet::Object::Duo);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
-
-##############################################################################
-# Core methods
-##############################################################################
-
-# Override create to provide the specific Duo integration type that will be
-# used in the remote Duo record.
-sub create {
- my ($class, $type, $name, $schema, $creator, $host, $time) = @_;
-
- $time ||= time;
- my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host,
- $time, 'radius');
- return $self;
-}
-
-# Override get to output the data in a specific format used for Duo radius
-# integration
-sub get {
- my ($self, $user, $host, $time) = @_;
- $time ||= time;
-
- # Check that the object isn't locked.
- my $id = $self->{type} . ':' . $self->{name};
- if ($self->flag_check ('locked')) {
- $self->error ("cannot get $id: object is locked");
- return;
- }
-
- # Retrieve the integration from Duo.
- my $key;
- eval {
- my %search = (du_name => $self->{name});
- my $row = $self->{schema}->resultset ('Duo')->find (\%search);
- $key = $row->get_column ('du_key');
- };
- if ($@) {
- $self->error ($@);
- return;
- }
- my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key);
-
- # We also need the admin server name, which we can get from the Duo object
- # configuration with a bit of JSON decoding.
- my $json = JSON->new->utf8 (1)->relaxed (1);
- my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE);
-
- # Construct the returned file.
- my $output = "[radius_server_challenge]\n";
- $output .= "ikey = $key\n";
- $output .= 'skey = ' . $integration->secret_key . "\n";
- $output .= "api_host = $config->{api_hostname}\n";
- $output .= "client = radius_client\n";
-
- # Log the action and return.
- $self->log_action ('get', $user, $host, $time);
- return $output;
-}
-
-1;
-__END__
-
-##############################################################################
-# Documentation
-##############################################################################
-
-=for stopwords
-Allbery Duo integration DBH keytab auth
-
-=head1 NAME
-
-Wallet::Object::Duo::RadiusProxy -- Duo auth proxy integration for RADIUS
-
-=head1 SYNOPSIS
-
- my @name = qw(duo-radius host.example.com);
- my @trace = ($user, $host, time);
- my $object = Wallet::Object::Duo::RadiusProxy->create (@name, $schema, @trace);
- my $config = $object->get (@trace);
- $object->destroy (@trace);
-
-=head1 DESCRIPTION
-
-Wallet::Object::Duo::RadiusProxy is a representation of Duo
-integrations with the wallet, specifically to output Duo integrations
-in a format that can easily be pulled into configuring the Duo
-Authentication Proxy for Radius. It implements the wallet object API
-and provides the necessary glue to create a Duo integration, return a
-configuration file containing the key and API information for that
-integration, and delete the integration from Duo when the wallet object
-is destroyed.
-
-The integration information is always returned in the configuration file
-format expected by the Authentication Proxy for Duo in configuring it
-for Radius.
-
-This object can be retrieved repeatedly without changing the secret key,
-matching Duo's native behavior with integrations. To change the keys of
-the integration, delete it and recreate it.
-
-To use this object, at least one configuration parameter must be set. See
-L<Wallet::Config> for details on supported configuration parameters and
-information about how to set wallet configuration.
-
-=head1 METHODS
-
-This object mostly inherits from Wallet::Object::Duo. See the
-documentation for that class for all generic methods. Below are only
-those methods that are overridden or behave specially for this
-implementation.
-
-=over 4
-
-=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME])
-
-This will override the Wallet::Object::Duo class with the information
-needed to create a specific integration type in Duo. It creates a new
-object with the given TYPE and NAME (TYPE is normally C<duo-radius> and
-must be for the rest of the wallet system to use the right class, but
-this module doesn't check for ease of subclassing), using DBH as the
-handle to the wallet metadata database. PRINCIPAL, HOSTNAME, and
-DATETIME are stored as history information. PRINCIPAL should be the
-user who is creating the object. If DATETIME isn't given, the current
-time is used.
-
-When a new Duo integration object is created, a new integration will be
-created in the configured Duo account and the integration key will be
-stored in the wallet object. If the integration already exists, create()
-will fail.
-
-If create() fails, it throws an exception.
-
-=item get(PRINCIPAL, HOSTNAME [, DATETIME])
-
-Retrieves the configuration information for the Duo integration and
-returns that information in the format expected by the configuration file
-for the Duo UNIX integration. Returns undef on failure. The caller
-should call error() to get the error message if get() returns undef.
-
-The returned configuration look look like:
-
- [radius_server_challenge]
- ikey = <integration-key>
- skey = <secret-key>
- api_host = <api-hostname>
- client = radius_client
-
-The C<host> parameter will be taken from the configuration file pointed
-to by the DUO_KEY_FILE configuration variable.
-
-PRINCIPAL, HOSTNAME, and DATETIME are stored as history information.
-PRINCIPAL should be the user who is downloading the keytab. If DATETIME
-isn't given, the current time is used.
-
-=back
-
-=head1 LIMITATIONS
-
-Only one Duo account is supported for a given wallet implementation.
-
-=head1 SEE ALSO
-
-Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8)
-
-This module is part of the wallet system. The current version is
-available from L<http://www.eyrie.org/~eagle/software/wallet/>.
-
-=head1 AUTHORS
-
-Jon Robertson <jonrober@stanford.edu>
-
-=cut
diff --git a/perl/lib/Wallet/Object/File.pm b/perl/lib/Wallet/Object/File.pm
index 226e32c..9452ff4 100644
--- a/perl/lib/Wallet/Object/File.pm
+++ b/perl/lib/Wallet/Object/File.pm
@@ -1,6 +1,7 @@
-# Wallet::Object::File -- File object implementation for the wallet.
+# Wallet::Object::File -- File object implementation for the wallet
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2008, 2010, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,23 +12,18 @@
##############################################################################
package Wallet::Object::File;
-require 5.006;
+use 5.006;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Digest::MD5 qw(md5_hex);
use File::Copy qw(move);
-use Wallet::Config ();
+use Wallet::Config;
use Wallet::Object::Base;
-@ISA = qw(Wallet::Object::Base);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.03';
+our @ISA = qw(Wallet::Object::Base);
+our $VERSION = '1.03';
##############################################################################
# File naming
diff --git a/perl/lib/Wallet/Object/Keytab.pm b/perl/lib/Wallet/Object/Keytab.pm
index 975179b..f276b3f 100644
--- a/perl/lib/Wallet/Object/Keytab.pm
+++ b/perl/lib/Wallet/Object/Keytab.pm
@@ -1,6 +1,7 @@
-# Wallet::Object::Keytab -- Keytab object implementation for the wallet.
+# Wallet::Object::Keytab -- Keytab object implementation for the wallet
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2008, 2009, 2010, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,22 +12,48 @@
##############################################################################
package Wallet::Object::Keytab;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
-use Wallet::Config ();
-use Wallet::Object::Base;
+use Wallet::Config;
use Wallet::Kadmin;
+use Wallet::Object::Base;
-@ISA = qw(Wallet::Object::Base);
+our @ISA = qw(Wallet::Object::Base);
+our $VERSION = '1.03';
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.09';
+##############################################################################
+# Shared methods
+##############################################################################
+
+# Generate a keytab into a temporary file and then return that as the return
+# value. Used by both get and update, as the only difference is how we
+# handle the unchanging flag.
+sub retrieve {
+ my ($self, $operation, $user, $host, $time) = @_;
+ $time ||= time;
+ my $id = $self->{type} . ':' . $self->{name};
+ if ($self->flag_check ('locked')) {
+ $self->error ("cannot get $id: object is locked");
+ return;
+ }
+ my $kadmin = $self->{kadmin};
+ my $result;
+ if ($operation eq 'get' && $self->flag_check ('unchanging')) {
+ $result = $kadmin->keytab ($self->{name});
+ } else {
+ my @enctypes = $self->attr ('enctypes');
+ $result = $kadmin->keytab_rekey ($self->{name}, @enctypes);
+ }
+ if (defined $result) {
+ $self->log_action ($operation, $user, $host, $time);
+ } else {
+ $self->error ($kadmin->error);
+ }
+ return $result;
+}
##############################################################################
# Enctype restriction
@@ -314,25 +341,15 @@ sub destroy {
# return that as the return value.
sub get {
my ($self, $user, $host, $time) = @_;
- $time ||= time;
- my $id = $self->{type} . ':' . $self->{name};
- if ($self->flag_check ('locked')) {
- $self->error ("cannot get $id: object is locked");
- return;
- }
- my $kadmin = $self->{kadmin};
- my $result;
- if ($self->flag_check ('unchanging')) {
- $result = $kadmin->keytab ($self->{name});
- } else {
- my @enctypes = $self->attr ('enctypes');
- $result = $kadmin->keytab_rekey ($self->{name}, @enctypes);
- }
- if (defined $result) {
- $self->log_action ('get', $user, $host, $time);
- } else {
- $self->error ($kadmin->error);
- }
+ my $result = $self->retrieve ('get', $user, $host, $time);
+ return $result;
+}
+
+# Our update implementation. Generate a new keytab regardless of the
+# unchanging flag.
+sub update {
+ my ($self, $user, $host, $time) = @_;
+ my $result = $self->retrieve ('update', $user, $host, $time);
return $result;
}
diff --git a/perl/lib/Wallet/Object/Password.pm b/perl/lib/Wallet/Object/Password.pm
new file mode 100644
index 0000000..1db53f3
--- /dev/null
+++ b/perl/lib/Wallet/Object/Password.pm
@@ -0,0 +1,224 @@
+# Wallet::Object::Password -- Password object implementation for the wallet
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2015
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::Object::Password;
+
+use 5.008;
+use strict;
+use warnings;
+
+use Crypt::GeneratePassword qw(chars);
+use Digest::MD5 qw(md5_hex);
+use Wallet::Config;
+use Wallet::Object::File;
+
+our @ISA = qw(Wallet::Object::File);
+our $VERSION = '1.03';
+
+##############################################################################
+# File naming
+##############################################################################
+
+# Returns the path into which that password object will be stored or undef on
+# error. On error, sets the internal error.
+sub file_path {
+ my ($self) = @_;
+ my $name = $self->{name};
+ unless ($Wallet::Config::PWD_FILE_BUCKET) {
+ $self->error ('password support not configured');
+ return;
+ }
+ unless ($name) {
+ $self->error ('password objects may not have empty names');
+ return;
+ }
+ my $hash = substr (md5_hex ($name), 0, 2);
+ $name =~ s/([^\w-])/sprintf ('%%%02X', ord ($1))/ge;
+ my $parent = "$Wallet::Config::PWD_FILE_BUCKET/$hash";
+ unless (-d $parent || mkdir ($parent, 0700)) {
+ $self->error ("cannot create password bucket $hash: $!");
+ return;
+ }
+ return "$Wallet::Config::PWD_FILE_BUCKET/$hash/$name";
+}
+
+##############################################################################
+# Shared methods
+##############################################################################
+
+# Return the contents of the file.
+sub retrieve {
+ my ($self, $operation, $user, $host, $time) = @_;
+ $time ||= time;
+ my $id = $self->{type} . ':' . $self->{name};
+ if ($self->flag_check ('locked')) {
+ $self->error ("cannot get $id: object is locked");
+ return;
+ }
+ my $path = $self->file_path;
+ return unless $path;
+
+ # If nothing is yet stored, or we have requested an update, generate a
+ # random password and save it to the file.
+ my $schema = $self->{schema};
+ my %search = (ob_type => $self->{type},
+ ob_name => $self->{name});
+ my $object = $schema->resultset('Object')->find (\%search);
+ if (!$object->ob_stored_on || $operation eq 'update') {
+ unless (open (FILE, '>', $path)) {
+ $self->error ("cannot store initial settings for $id: $!\n");
+ return;
+ }
+ my $pass = chars ($Wallet::Config::PWD_LENGTH_MIN,
+ $Wallet::Config::PWD_LENGTH_MAX);
+ print FILE $pass;
+ $self->log_action ('store', $user, $host, $time);
+ unless (close FILE) {
+ $self->error ("cannot get $id: $!");
+ return;
+ }
+ }
+
+ unless (open (FILE, '<', $path)) {
+ $self->error ("cannot get $id: object has not been stored");
+ return;
+ }
+ local $/;
+ my $data = <FILE>;
+ unless (close FILE) {
+ $self->error ("cannot get $id: $!");
+ return;
+ }
+ $self->log_action ($operation, $user, $host, $time);
+ return $data;
+}
+
+##############################################################################
+# Core methods
+##############################################################################
+
+# Return the contents of the file.
+sub get {
+ my ($self, $user, $host, $time) = @_;
+ my $result = $self->retrieve ('get', $user, $host, $time);
+ return $result;
+}
+
+# Return the contents of the file after resetting them to a random string.
+sub update {
+ my ($self, $user, $host, $time) = @_;
+ my $result = $self->retrieve ('update', $user, $host, $time);
+ return $result;
+}
+
+1;
+__END__
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=head1 NAME
+
+Wallet::Object::Password - Password object implementation for wallet
+
+=for stopwords
+API HOSTNAME DATETIME keytab remctld backend nul Allbery wallet-backend
+
+=head1 SYNOPSIS
+
+ my @name = qw(file mysql-lsdb)
+ my @trace = ($user, $host, time);
+ my $object = Wallet::Object::Password->create (@name, $schema, @trace);
+ unless ($object->store ("the-password\n")) {
+ die $object->error, "\n";
+ }
+ my $password = $object->get (@trace);
+ $object->destroy (@trace);
+
+=head1 DESCRIPTION
+
+Wallet::Object::Password is an extension of Wallet::Object::File,
+acting as a representation of simple file objects in the wallet. The
+difference between the two is that if there is no data stored in a
+password object when a user tries to get it for the first time, then a
+random string suited for a password will be generated and put into the
+object data.
+
+It implements the wallet object API and provides the necessary
+glue to store a file on the wallet server, retrieve it later, and delete
+it when the password object is deleted.
+
+To use this object, the configuration option specifying where on the
+wallet server to store password objects must be set. See
+L<Wallet::Config> for details on this configuration parameter and
+information about how to set wallet configuration.
+
+=head1 METHODS
+
+This object mostly inherits from Wallet::Object::File. See the
+documentation for that class for all generic methods. Below are only
+those methods that are overridden or behave specially for this
+implementation.
+
+=over 4
+
+=item get(PRINCIPAL, HOSTNAME [, DATETIME])
+
+Retrieves the current contents of the file object or undef on error.
+store() must be called before get() will be successful. The caller should
+call error() to get the error message if get() returns undef. PRINCIPAL,
+HOSTNAME, and DATETIME are stored as history information. PRINCIPAL
+should be the user who is downloading the keytab. If DATETIME isn't
+given, the current time is used.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item PWD_FILE_BUCKET/<hash>/<file>
+
+Password files are stored on the wallet server under the directory
+PWD_FILE_BUCKET as set in the wallet configuration. <hash> is the
+first two characters of the hex-encoded MD5 hash of the wallet password
+object name, used to not put too many files in the same directory.
+<file> is the name of the password object with all characters other
+than alphanumerics, underscores, and dashes replaced by C<%> and the
+hex code of the character.
+
+=back
+
+=head1 LIMITATIONS
+
+The wallet implementation itself can handle arbitrary password object
+names. However, due to limitations in the B<remctld> server usually
+used to run B<wallet-backend>, password object names containing nul
+characters (ASCII 0) may not be permitted. The file system used for
+storing file objects may impose a length limitation on the
+password object name.
+
+=head1 SEE ALSO
+
+remctld(8), Wallet::Config(3), Wallet::Object::File(3),
+wallet-backend(8)
+
+This module is part of the wallet system. The current version is
+available from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+=head1 AUTHOR
+
+Jon Robertson <jonrober@stanford.edu>
+
+=cut
diff --git a/perl/lib/Wallet/Object/WAKeyring.pm b/perl/lib/Wallet/Object/WAKeyring.pm
index 3e80300..3c99785 100644
--- a/perl/lib/Wallet/Object/WAKeyring.pm
+++ b/perl/lib/Wallet/Object/WAKeyring.pm
@@ -1,6 +1,7 @@
-# Wallet::Object::WAKeyring -- WebAuth keyring object implementation.
+# Wallet::Object::WAKeyring -- WebAuth keyring object implementation
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2012, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,24 +12,19 @@
##############################################################################
package Wallet::Object::WAKeyring;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(@ISA $VERSION);
use Digest::MD5 qw(md5_hex);
use Fcntl qw(LOCK_EX);
-use Wallet::Config ();
+use Wallet::Config;
use Wallet::Object::Base;
use WebAuth 3.06 qw(WA_KEY_AES WA_AES_128);
-@ISA = qw(Wallet::Object::Base);
-
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.01';
+our @ISA = qw(Wallet::Object::Base);
+our $VERSION = '1.03';
##############################################################################
# File naming
diff --git a/perl/lib/Wallet/Policy/Stanford.pm b/perl/lib/Wallet/Policy/Stanford.pm
index a392476..efb9d28 100644
--- a/perl/lib/Wallet/Policy/Stanford.pm
+++ b/perl/lib/Wallet/Policy/Stanford.pm
@@ -1,7 +1,8 @@
-# Wallet::Policy::Stanford -- Stanford's wallet naming and ownership policy.
+# Wallet::Policy::Stanford -- Stanford's wallet naming and ownership policy
#
# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+# Copyright 2013, 2014, 2015
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -25,8 +26,8 @@ our (@EXPORT_OK, $VERSION);
# against circular module loading (not that we load any modules, but
# consistency is good).
BEGIN {
- $VERSION = '1.00';
- @EXPORT_OK = qw(default_owner verify_name);
+ $VERSION = '1.03';
+ @EXPORT_OK = qw(default_owner verify_name is_for_host);
}
##############################################################################
@@ -66,8 +67,9 @@ our %FILE_TYPE = (
'password-root' => { host => 1 },
'password-tivoli' => { host => 1 },
properties => { extra => 1 },
- 'ssh-dsa' => { host => 1 },
- 'ssh-rsa' => { host => 1 },
+ 'ssh-dsa' => { host => 1, extra => 1 },
+ 'ssh-rsa' => { host => 1, extra => 1 },
+ 'ssl-chain' => { host => 1, extra => 1 },
'ssl-key' => { host => 1, extra => 1 },
'ssl-keypair' => { host => 1, extra => 1 },
'ssl-keystore' => { extra => 1 },
@@ -75,6 +77,29 @@ our %FILE_TYPE = (
'tivoli-key' => { host => 1 },
);
+# Password object types. Most of these mimic file object types (which should
+# be gradually phased out).
+our %PASSWORD_TYPE = (
+ 'ipmi' => { host => 1 },
+ 'root' => { host => 1 },
+ 'tivoli' => { host => 1 },
+ 'system' => { host => 1, extra => 1, need_extra => 1 },
+ 'app' => { host => 1, extra => 1, need_extra => 1 },
+ 'service' => { extra => 1, need_extra => 1 },
+);
+
+# Mappings that let us determine the host for a host-based object, if any.
+our %HOST_FOR = (
+ 'keytab' => \&_host_for_keytab,
+ 'file' => \&_host_for_file,
+ 'password' => \&_host_for_password,
+ 'duo' => \&_host_for_duo,
+ 'duo-pam' => \&_host_for_duo,
+ 'duo-radius' => \&_host_for_duo,
+ 'duo-ldap' => \&_host_for_duo,
+ 'duo-rdp' => \&_host_for_duo,
+);
+
# Host-based file object types for the legacy file object naming scheme.
our @FILE_HOST_LEGACY = qw(htpasswd ssh-rsa ssh-dsa ssl-key tivoli-key);
@@ -144,6 +169,17 @@ sub _host_for_file_legacy {
return $host;
}
+# Map a password object name to a hostname. Returns undef if this password
+# object name doesn't map to a hostname.
+sub _host_for_password {
+ my ($name) = @_;
+
+ # Parse the name and check whether this is a host-based object.
+ my ($type, $host) = split('/', $name);
+ return if !$PASSWORD_TYPE{$type}{host};
+ return $host;
+}
+
# Map a file object name to a hostname. Returns undef if this file object
# name doesn't map to a hostname.
sub _host_for_file {
@@ -181,6 +217,23 @@ sub _host_for_duo {
return $name;
}
+# Take a object type and name, along with a host name, and use these to
+# decide if the given object is host-based and matches the given host.
+sub is_for_host {
+ my ($type, $name, $host) = @_;
+
+ # If we have a possible host mapping, get the host and see if it matches.
+ if (defined($HOST_FOR{$type})) {
+ my $object_host = $HOST_FOR{$type}->($name);
+ return 0 unless $object_host;
+ if ($host eq $object_host) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
# The default owner of host-based objects should be the host keytab and the
# NetDB ACL for that host, with one twist. If the creator of a new node is
# using a root instance, we want to require everyone managing that node be
@@ -188,20 +241,9 @@ sub _host_for_duo {
sub default_owner {
my ($type, $name) = @_;
- # How to determine the host for host-based objects.
- my %host_for = (
- 'keytab' => \&_host_for_keytab,
- 'file' => \&_host_for_file,
- 'duo' => \&_host_for_duo,
- 'duo-pam' => \&_host_for_duo,
- 'duo-radius' => \&_host_for_duo,
- 'duo-ldap' => \&_host_for_duo,
- 'duo-rdp' => \&_host_for_duo,
- );
-
# If we have a possible host mapping, see if we can use that.
- if (defined($host_for{$type})) {
- my $host = $host_for{$type}->($name);
+ if (defined($HOST_FOR{$type})) {
+ my $host = $HOST_FOR{$type}->($name);
if ($host) {
my $acl_name = "host/$host";
my @acl;
@@ -242,7 +284,7 @@ sub default_owner {
# hostnames, limit the acceptable characters for service/* keytabs, and
# enforce our naming constraints on */cgi principals.
#
-# Also use this function to require that IDG staff always do implicit object
+# Also use this function to require that ACS staff always do implicit object
# creation using a */root instance.
sub verify_name {
my ($type, $name, $user) = @_;
@@ -363,6 +405,8 @@ sub verify_name {
return "missing component in $name";
}
return;
+
+
} else {
# Legacy naming scheme.
my %groups = map { $_ => 1 } @GROUPS_LEGACY;
@@ -380,6 +424,71 @@ sub verify_name {
}
}
+ # Check password object naming conventions.
+ if ($type eq 'password') {
+ if ($name =~ m{ / }xms) {
+ my @name = split('/', $name);
+
+ # Names have between two and four components and all must be
+ # non-empty.
+ if (@name > 4) {
+ return "too many components in $name";
+ }
+ if (@name < 2) {
+ return "too few components in $name";
+ }
+ if (grep { $_ eq q{} } @name) {
+ return "empty component in $name";
+ }
+
+ # All objects start with the type. First check if this is a
+ # host-based type.
+ my $type = shift @name;
+ if ($PASSWORD_TYPE{$type} && $PASSWORD_TYPE{$type}{host}) {
+ my ($host, $extra) = @name;
+ if ($host !~ m{ [.] }xms) {
+ return "host name $host is not fully qualified";
+ }
+ if (defined($extra) && !$PASSWORD_TYPE{$type}{extra}) {
+ return "extraneous component at end of $name";
+ }
+ if (!defined($extra) && $PASSWORD_TYPE{$type}{need_extra}) {
+ return "missing component in $name";
+ }
+ return;
+ }
+
+ # Otherwise, the name is group-based. There be at least two
+ # remaining components.
+ if (@name < 2) {
+ return "too few components in $name";
+ }
+ my ($group, $service, $extra) = @name;
+
+ # Check the group.
+ if (!$ACL_FOR_GROUP{$group}) {
+ return "unknown group $group";
+ }
+
+ # Check the type. Be sure it's not host-based.
+ if (!$PASSWORD_TYPE{$type}) {
+ return "unknown type $type";
+ }
+ if ($PASSWORD_TYPE{$type}{host}) {
+ return "bad name for host-based file type $type";
+ }
+
+ # Check the extra data.
+ if (defined($extra) && !$PASSWORD_TYPE{$type}{extra}) {
+ return "extraneous component at end of $name";
+ }
+ if (!defined($extra) && $PASSWORD_TYPE{$type}{need_extra}) {
+ return "missing component in $name";
+ }
+ return;
+ }
+ }
+
# Check the naming conventions for all Duo object types. The object
# should simply be the host name for now.
if ($type =~ m{^duo(-\w+)?$}) {
diff --git a/perl/lib/Wallet/Report.pm b/perl/lib/Wallet/Report.pm
index bf48308..3d59bf8 100644
--- a/perl/lib/Wallet/Report.pm
+++ b/perl/lib/Wallet/Report.pm
@@ -1,6 +1,7 @@
-# Wallet::Report -- Wallet system reporting interface.
+# Wallet::Report -- Wallet system reporting interface
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2008, 2009, 2010, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,19 +12,15 @@
##############################################################################
package Wallet::Report;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw($VERSION);
use Wallet::ACL;
use Wallet::Schema;
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.04';
+our $VERSION = '1.03';
##############################################################################
# Constructor, destructor, and accessors
@@ -175,6 +172,20 @@ sub objects_unused {
return (\%search, \%options);
}
+# Return the SQL statement to find all fiel objects that have been created
+# but have never had information stored (via store).
+sub objects_unstored {
+ my ($self) = @_;
+ my @objects;
+
+ my %search = (ob_stored_on => undef,
+ ob_type => 'file');
+ my %options = (order_by => [ qw/ob_type ob_name/ ],
+ select => [ qw/ob_type ob_name/ ]);
+
+ return (\%search, \%options);
+}
+
# Returns a list of all objects stored in the wallet database in the form of
# type and name pairs. On error and for an empty database, the empty list
# will be returned. To distinguish between an empty list and an error, call
@@ -190,7 +201,7 @@ sub objects {
if (!defined $type || $type eq '') {
($search_ref, $options_ref) = $self->objects_all;
} else {
- if ($type ne 'unused' && @args != 1) {
+ if ($type ne 'unused' && $type ne 'unstored' && @args != 1) {
$self->error ("object searches require one argument to search");
} elsif ($type eq 'type') {
($search_ref, $options_ref) = $self->objects_type (@args);
@@ -202,6 +213,8 @@ sub objects {
($search_ref, $options_ref) = $self->objects_acl (@args);
} elsif ($type eq 'unused') {
($search_ref, $options_ref) = $self->objects_unused (@args);
+ } elsif ($type eq 'unstored') {
+ ($search_ref, $options_ref) = $self->objects_unstored (@args);
} else {
$self->error ("do not know search type: $type");
}
@@ -226,12 +239,124 @@ sub objects {
return @objects;
}
+# Returns a list of all object_history records stored in the wallet database
+# including all of their fields. On error and for an empty database, the
+# empty list will be returned. To distinguish between an empty list and an
+# error, call error(), which will return undef if there was no error.
+# Farms out specific statement to another subroutine for specific search
+# types, but each case should return ob_type and ob_name in that order.
+sub objects_history {
+ my ($self, $search_type, @args) = @_;
+ undef $self->{error};
+
+ # All fields in the order we want to see them.
+ my @fields = ('oh_on', 'oh_by', 'oh_type', 'oh_name', 'oh_action',
+ 'oh_from');
+
+ # Get the search and options array refs from specific functions.
+ my %search = ();
+ my %options = (order_by => \@fields,
+ select => \@fields);
+
+ # Perform the search and return on any errors.
+ my @objects;
+ my $schema = $self->{schema};
+ eval {
+ my @objects_rs
+ = $schema->resultset('ObjectHistory')->search (\%search,
+ \%options);
+ for my $object_rs (@objects_rs) {
+ my @rec;
+ for my $field (@fields) {
+ push (@rec, $object_rs->get_column($field));
+ }
+ push (@objects, \@rec);
+ }
+ };
+ if ($@) {
+ $self->error ("cannot list objects: $@");
+ return;
+ }
+
+ return @objects;
+}
+
+# Returns a list of all objects stored in the wallet database in the form of
+# type and name pairs. On error and for an empty database, the empty list
+# will be returned. To distinguish between an empty list and an error, call
+# error(), which will return undef if there was no error. Farms out specific
+# statement to another subroutine for specific search types, but each case
+# should return ob_type and ob_name in that order.
+sub objects_hostname {
+ my ($self, $type, $hostname) = @_;
+ undef $self->{error};
+
+ # Make sure we have a given hostname.
+ if (!$hostname) {
+ $self->error ("object hosts requires one argument to search");
+ return;
+ }
+
+ # If we don't have a way to get host-based object lists, quit.
+ unless (defined &Wallet::Config::is_for_host) {
+ $self->error ('no host-based policy defined');
+ return;
+ }
+
+ # Search on all objects.
+ my %search = ();
+ my %options = (order_by => [ qw/ob_type ob_name/ ],
+ select => [ qw/ob_type ob_name/ ]);
+
+ my @objects;
+ my $schema = $self->{schema};
+ eval {
+ my @objects_rs = $schema->resultset('Object')->search (\%search,
+ \%options);
+
+ # Check to see if an object is for the given host and add to list if
+ # so.
+ for my $object_rs (@objects_rs) {
+ my $type = $object_rs->ob_type;
+ my $name = $object_rs->ob_name;
+ next unless &Wallet::Config::is_for_host($type, $name, $hostname);
+ push (@objects, [ $type, $name ]);
+ }
+ };
+ if ($@) {
+ $self->error ("cannot list objects: $@");
+ return;
+ }
+
+ return @objects;
+}
+
+##############################################################################
+# Type reports
+##############################################################################
+
+# Return an alphabetical list of all valid types set up, along with the class
+# that they belong to.
+sub types {
+ my ($self) = @_;
+
+ my (@types);
+ my @types_rs = $self->{schema}->resultset('Type')->all;
+ for my $type_rs (@types_rs) {
+ my $name = $type_rs->ty_name;
+ my $class = $type_rs->ty_class;
+ push(@types, [ $name, $class ]);
+ }
+
+ @types = sort { $a->[0] cmp $b->[0] } @types;
+ return @types;
+}
+
##############################################################################
# ACL reports
##############################################################################
-# Returns the SQL statement required to find and return all ACLs in the
-# database.
+# Returns the array of all ACLs in the database.
sub acls_all {
my ($self) = @_;
my @acls;
@@ -255,7 +380,7 @@ sub acls_all {
return (@acls);
}
-# Returns the SQL statement required to find all empty ACLs in the database.
+# Returns the array of all empty ACLs in the database.
sub acls_empty {
my ($self) = @_;
my @acls;
@@ -281,9 +406,36 @@ sub acls_empty {
return (@acls);
}
-# Returns the SQL statement and the field required to find ACLs containing the
-# specified entry. The identifier is automatically surrounded by wildcards to
-# do a substring search.
+# Returns the array of ACLs that nest a given ACL.
+sub acls_nesting {
+ my ($self, $name) = @_;
+ my @acls;
+
+ my $schema = $self->{schema};
+ my %search = (ae_scheme => 'nested',
+ ae_identifier => $name);
+ my %options = (join => 'acl_entries',
+ prefetch => 'acl_entries',
+ order_by => [ qw/ac_id/ ],
+ select => [ qw/ac_id ac_name/ ]);
+
+ eval {
+ my @acls_rs = $schema->resultset('Acl')->search (\%search, \%options);
+ for my $acl_rs (@acls_rs) {
+ push (@acls, [ $acl_rs->ac_id, $acl_rs->ac_name ]);
+ }
+ };
+
+ if ($@) {
+ $self->error ("cannot list ACLs: $@");
+ return;
+ }
+ return (@acls);
+}
+
+# Returns the array of all ACLs containing the specified entry. The given
+# identifier is automatically surrounded by wildcards to do a substring
+# search.
sub acls_entry {
my ($self, $type, $identifier) = @_;
my @acls;
@@ -311,7 +463,7 @@ sub acls_entry {
return (@acls);
}
-# Returns the SQL statement required to find unused ACLs.
+# Returns the array of all unused ACLs.
sub acls_unused {
my ($self) = @_;
my @acls;
@@ -424,6 +576,13 @@ sub acls {
@acls = $self->acls_empty;
} elsif ($type eq 'unused') {
@acls = $self->acls_unused;
+ } elsif ($type eq 'nesting') {
+ if (@args == 0) {
+ $self->error ('ACL nesting search requires an ACL to search');
+ return;
+ } else {
+ @acls = $self->acls_nesting (@args);
+ }
} else {
$self->error ("unknown search type: $type");
return;
@@ -469,6 +628,23 @@ sub owners {
return @owners;
}
+# Return an alphabetical list of all valid types set up, along with the class
+# that they belong to.
+sub acl_schemes {
+ my ($self) = @_;
+
+ my (@schemes);
+ my @acls_rs = $self->{schema}->resultset('AclScheme')->all;
+ for my $acl_rs (@acls_rs) {
+ my $name = $acl_rs->as_name;
+ my $class = $acl_rs->as_class;
+ push(@schemes, [ $name, $class ]);
+ }
+
+ @schemes = sort { $a->[0] cmp $b->[0] } @schemes;
+ return @schemes;
+}
+
##############################################################################
# Auditing
##############################################################################
@@ -633,14 +809,17 @@ Returns a list of all objects matching a search type and string in the
database, or all objects in the database if no search information is
given.
-There are five types of searches currently. C<type>, with a given type,
-will return only those entries where the type matches the given type.
-C<owner>, with a given owner, will only return those objects owned by the
-given ACL name or ID. C<flag>, with a given flag name, will only return
-those items with a flag set to the given value. C<acl> operates like
-C<owner>, but will return only those objects that have the given ACL name
-or ID on any of the possible ACL settings, not just owner. C<unused> will
-return all entries for which a get command has never been issued.
+There are several types of searches. C<type>, with a given type, will
+return only those entries where the type matches the given type.
+C<owner>, with a given owner, will only return those objects owned by
+the given ACL name or ID. C<flag>, with a given flag name, will only
+return those items with a flag set to the given value. C<acl> operates
+like C<owner>, but will return only those objects that have the given
+ACL name or ID on any of the possible ACL settings, not just owner.
+C<unused> will return all entries for which a get command has never
+been issued. C<unstored> will return all entries for which a store
+command has never been issued (limited to file type since storing isn't
+needed for other types).
The return value is a list of references to pairs of type and name. For
example, if two objects existed in the database, both of type C<keytab>
@@ -654,6 +833,24 @@ empty search result, the caller should call error(). error() is
guaranteed to return the error message if there was an error and undef if
there was no error.
+=item objects_history(TYPE)
+
+Returns a dump of the entire object history table. The return value is
+a list of references to each field in that table, in the following order:
+
+ oh_on, oh_by, oh_type, oh_name, oh_action, oh_from
+
+=item objects_hostname(TYPE, HOSTNAME)
+
+Returns a list of all host-based objects for a given hostname. The
+output is identical to the general objects command, but we need to
+separate this out because the way it searches is very different.
+
+Returns the empty list on failure. To distinguish between this and an
+empty search result, the caller should call error(). error() is
+guaranteed to return the error message if there was an error and undef if
+there was no error.
+
=item owners(TYPE, NAME)
Returns a list of all ACL lines contained in owner ACLs for objects
diff --git a/perl/lib/Wallet/Schema.pm b/perl/lib/Wallet/Schema.pm
index 5b850c0..6b3de39 100644
--- a/perl/lib/Wallet/Schema.pm
+++ b/perl/lib/Wallet/Schema.pm
@@ -1,6 +1,7 @@
-# Database schema and connector for the wallet system.
+# Wallet::Schema -- Database schema and connector for the wallet system
#
# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2012, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -8,6 +9,7 @@
package Wallet::Schema;
+use 5.008;
use strict;
use warnings;
@@ -15,9 +17,9 @@ use Wallet::Config;
use base 'DBIx::Class::Schema';
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
+# Unlike all of the other wallet modules, this module's version is tied to the
+# version of the schema in the database. It should only be changed on schema
+# changes, at least until better handling of upgrades is available.
our $VERSION = '0.10';
__PACKAGE__->load_namespaces;
@@ -114,6 +116,10 @@ Holds the supported ACL schemes and their corresponding Perl classes:
insert into acl_schemes (as_name, as_class)
values ('ldap-attr', 'Wallet::ACL::LDAP::Attribute');
insert into acl_schemes (as_name, as_class)
+ values ('ldap-attr-root', 'Wallet::ACL::LDAP::Attribute::Root');
+ insert into acl_schemes (as_name, as_class)
+ values ('nested', 'Wallet::ACL::Nested');
+ insert into acl_schemes (as_name, as_class)
values ('netdb', 'Wallet::ACL::NetDB');
insert into acl_schemes (as_name, as_class)
values ('netdb-root', 'Wallet::ACL::NetDB::Root');
diff --git a/perl/lib/Wallet/Schema/Result/Acl.pm b/perl/lib/Wallet/Schema/Result/Acl.pm
index 226738a..59a628a 100644
--- a/perl/lib/Wallet/Schema/Result/Acl.pm
+++ b/perl/lib/Wallet/Schema/Result/Acl.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
ACL
diff --git a/perl/lib/Wallet/Schema/Result/AclEntry.pm b/perl/lib/Wallet/Schema/Result/AclEntry.pm
index a33a98c..ea531bd 100644
--- a/perl/lib/Wallet/Schema/Result/AclEntry.pm
+++ b/perl/lib/Wallet/Schema/Result/AclEntry.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
ACL
diff --git a/perl/lib/Wallet/Schema/Result/AclHistory.pm b/perl/lib/Wallet/Schema/Result/AclHistory.pm
index 82e18a9..dc6bed7 100644
--- a/perl/lib/Wallet/Schema/Result/AclHistory.pm
+++ b/perl/lib/Wallet/Schema/Result/AclHistory.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
__PACKAGE__->load_components("InflateColumn::DateTime");
=for stopwords
diff --git a/perl/lib/Wallet/Schema/Result/AclScheme.pm b/perl/lib/Wallet/Schema/Result/AclScheme.pm
index 91a58b2..004e5d2 100644
--- a/perl/lib/Wallet/Schema/Result/AclScheme.pm
+++ b/perl/lib/Wallet/Schema/Result/AclScheme.pm
@@ -12,6 +12,9 @@ use strict;
use warnings;
use base 'DBIx::Class::Core';
+
+our $VERSION = '1.03';
+
__PACKAGE__->load_components (qw//);
=for stopwords
@@ -36,6 +39,10 @@ By default it contains the following entries:
insert into acl_schemes (as_name, as_class)
values ('ldap-attr', 'Wallet::ACL::LDAP::Attribute');
insert into acl_schemes (as_name, as_class)
+ values ('ldap-attr-root', 'Wallet::ACL::LDAP::Attribute::Root');
+ insert into acl_schemes (as_name, as_class)
+ values ('nested', 'Wallet::ACL::Nested');
+ insert into acl_schemes (as_name, as_class)
values ('netdb', 'Wallet::ACL::NetDB');
insert into acl_schemes (as_name, as_class)
values ('netdb-root', 'Wallet::ACL::NetDB::Root');
diff --git a/perl/lib/Wallet/Schema/Result/Duo.pm b/perl/lib/Wallet/Schema/Result/Duo.pm
index 6ad61e9..b5328bb 100644
--- a/perl/lib/Wallet/Schema/Result/Duo.pm
+++ b/perl/lib/Wallet/Schema/Result/Duo.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
keytab enctype
diff --git a/perl/lib/Wallet/Schema/Result/Enctype.pm b/perl/lib/Wallet/Schema/Result/Enctype.pm
index 5733669..f1f42a9 100644
--- a/perl/lib/Wallet/Schema/Result/Enctype.pm
+++ b/perl/lib/Wallet/Schema/Result/Enctype.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
Kerberos
diff --git a/perl/lib/Wallet/Schema/Result/Flag.pm b/perl/lib/Wallet/Schema/Result/Flag.pm
index e223ff8..84e3ee3 100644
--- a/perl/lib/Wallet/Schema/Result/Flag.pm
+++ b/perl/lib/Wallet/Schema/Result/Flag.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=head1 NAME
Wallet::Schema::Result::Flag - Wallet schema for object flags
diff --git a/perl/lib/Wallet/Schema/Result/KeytabEnctype.pm b/perl/lib/Wallet/Schema/Result/KeytabEnctype.pm
index daea724..2a16af8 100644
--- a/perl/lib/Wallet/Schema/Result/KeytabEnctype.pm
+++ b/perl/lib/Wallet/Schema/Result/KeytabEnctype.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
keytab enctype
diff --git a/perl/lib/Wallet/Schema/Result/KeytabSync.pm b/perl/lib/Wallet/Schema/Result/KeytabSync.pm
index ca84277..bd57310 100644
--- a/perl/lib/Wallet/Schema/Result/KeytabSync.pm
+++ b/perl/lib/Wallet/Schema/Result/KeytabSync.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
keytab
diff --git a/perl/lib/Wallet/Schema/Result/Object.pm b/perl/lib/Wallet/Schema/Result/Object.pm
index fd64e1b..fdec3b8 100644
--- a/perl/lib/Wallet/Schema/Result/Object.pm
+++ b/perl/lib/Wallet/Schema/Result/Object.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
__PACKAGE__->load_components("InflateColumn::DateTime");
=head1 NAME
diff --git a/perl/lib/Wallet/Schema/Result/ObjectHistory.pm b/perl/lib/Wallet/Schema/Result/ObjectHistory.pm
index 5e9c8bd..2fe687e 100644
--- a/perl/lib/Wallet/Schema/Result/ObjectHistory.pm
+++ b/perl/lib/Wallet/Schema/Result/ObjectHistory.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
__PACKAGE__->load_components("InflateColumn::DateTime");
=head1 NAME
diff --git a/perl/lib/Wallet/Schema/Result/SyncTarget.pm b/perl/lib/Wallet/Schema/Result/SyncTarget.pm
index 4300a54..ab8ea47 100644
--- a/perl/lib/Wallet/Schema/Result/SyncTarget.pm
+++ b/perl/lib/Wallet/Schema/Result/SyncTarget.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=head1 NAME
Wallet::Schema::Result::SyncTarget - Wallet schema for synchronization targets
diff --git a/perl/lib/Wallet/Schema/Result/Type.pm b/perl/lib/Wallet/Schema/Result/Type.pm
index 748a8a8..abc7017 100644
--- a/perl/lib/Wallet/Schema/Result/Type.pm
+++ b/perl/lib/Wallet/Schema/Result/Type.pm
@@ -13,6 +13,8 @@ use warnings;
use base 'DBIx::Class::Core';
+our $VERSION = '1.03';
+
=for stopwords
APIs
diff --git a/perl/lib/Wallet/Server.pm b/perl/lib/Wallet/Server.pm
index f6ea342..552ba9d 100644
--- a/perl/lib/Wallet/Server.pm
+++ b/perl/lib/Wallet/Server.pm
@@ -1,6 +1,7 @@
-# Wallet::Server -- Wallet system server implementation.
+# Wallet::Server -- Wallet system server implementation
#
# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
# Copyright 2007, 2008, 2010, 2011, 2013, 2014
# The Board of Trustees of the Leland Stanford Junior University
#
@@ -11,20 +12,16 @@
##############################################################################
package Wallet::Server;
-require 5.006;
+use 5.008;
use strict;
use warnings;
-use vars qw(%MAPPING $VERSION);
use Wallet::ACL;
use Wallet::Config;
use Wallet::Schema;
-# This version should be increased on any code change to this module. Always
-# use two digits for the minor version with a leading zero if necessary so
-# that it will sort properly.
-$VERSION = '0.11';
+our $VERSION = '1.03';
##############################################################################
# Utility methods
@@ -154,8 +151,8 @@ sub create_check {
$self->error ($acl->error);
return;
}
- @entries = sort { $$a[0] cmp $$b[0] && $$a[1] cmp $$b[1] } @entries;
- @acl = sort { $$a[0] cmp $$b[0] && $$a[1] cmp $$b[1] } @acl;
+ @entries = sort { $$a[0] cmp $$b[0] || $$a[1] cmp $$b[1] } @entries;
+ @acl = sort { $$a[0] cmp $$b[0] || $$a[1] cmp $$b[1] } @acl;
my $okay = 1;
if (@entries != @acl) {
$okay = 0;
@@ -516,6 +513,21 @@ sub get {
return $result;
}
+# Retrieve the information associated with an object, updating the current
+# information if we are of a type that allows autogenerated information.
+# Returns undef and sets the internal error if the retrieval fails or if the
+# user isn't authorized. If the object doesn't exist, attempts dynamic
+# creation of the object using the default ACL mappings (if any).
+sub update {
+ my ($self, $type, $name) = @_;
+ my $object = $self->retrieve ($type, $name);
+ return unless defined $object;
+ return unless $self->acl_verify ($object, 'get');
+ my $result = $object->update ($self->{user}, $self->{host});
+ $self->error ($object->error) unless defined $result;
+ return $result;
+}
+
# Store new data in an object, or returns undef and sets the internal error if
# the object can't be found or if the user isn't authorized. Also don't
# permit storing undef, although storing the empty string is fine. If the
@@ -734,6 +746,36 @@ sub acl_rename {
return 1;
}
+# Move all ACLs owned by one ACL to another, or return undef and set the
+# internal error.
+sub acl_replace {
+ my ($self, $old_id, $replace_id) = @_;
+ unless ($self->{admin}->check ($self->{user})) {
+ $self->acl_error ($old_id, 'replace');
+ return;
+ }
+ my $acl = eval { Wallet::ACL->new ($old_id, $self->{schema}) };
+ if ($@) {
+ $self->error ($@);
+ return;
+ }
+ if ($acl->name eq 'ADMIN') {
+ $self->error ('cannot replace the ADMIN ACL');
+ return;
+ }
+ my $replace_acl = eval { Wallet::ACL->new ($replace_id, $self->{schema}) };
+ if ($@) {
+ $self->error ($@);
+ return;
+ }
+
+ unless ($acl->replace ($replace_id, $self->{user}, $self->{host})) {
+ $self->error ($acl->error);
+ return;
+ }
+ return 1;
+}
+
# Destroy an ACL, deleting it out of the database. Returns true on success.
# On failure, returns undef, setting the internal error.
sub acl_destroy {
@@ -942,6 +984,14 @@ either the current name or the numeric ID. NEW must not be all-numeric.
To rename an ACL, the current user must be authorized by the ADMIN ACL.
Returns true on success and false on failure.
+=item acl_replace(OLD, NEW)
+
+Moves any object owned by the ACL identified by OLD to be instead owned by
+NEW. This goes through all objects owned by OLD and individually changes
+the owner, along with history updates. OLD and NEW may be either the name
+or the numeric ID. To replace an ACL, the current user must be authorized
+by the ADMIN ACL. Returns true on success and false on failure.
+
=item acl_show(ID)
Returns a human-readable description, including membership, of the ACL
diff --git a/perl/sql/Wallet-Schema-0.10-MySQL.sql b/perl/sql/Wallet-Schema-0.10-MySQL.sql
index c0b7fcc..ba73062 100644
--- a/perl/sql/Wallet-Schema-0.10-MySQL.sql
+++ b/perl/sql/Wallet-Schema-0.10-MySQL.sql
@@ -2,6 +2,29 @@
-- Created by SQL::Translator::Producer::MySQL
-- Created on Thu Oct 9 20:54:55 2014
--
+-- Copyright 2014
+-- The Board of Trustees of the Leland Stanford Junior University
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included
+-- in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--
+
SET foreign_key_checks=0;
DROP TABLE IF EXISTS `acl_history`;
diff --git a/perl/sql/Wallet-Schema-0.10-PostgreSQL.sql b/perl/sql/Wallet-Schema-0.10-PostgreSQL.sql
index 3bcb0ae..d1658dd 100644
--- a/perl/sql/Wallet-Schema-0.10-PostgreSQL.sql
+++ b/perl/sql/Wallet-Schema-0.10-PostgreSQL.sql
@@ -2,6 +2,29 @@
-- Created by SQL::Translator::Producer::PostgreSQL
-- Created on Thu Oct 9 20:54:56 2014
--
+-- Copyright 2014
+-- The Board of Trustees of the Leland Stanford Junior University
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included
+-- in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--
+
--
-- Table: acl_history.
--
diff --git a/perl/sql/Wallet-Schema-0.10-SQLite.sql b/perl/sql/Wallet-Schema-0.10-SQLite.sql
index 94a185c..c13bc29 100644
--- a/perl/sql/Wallet-Schema-0.10-SQLite.sql
+++ b/perl/sql/Wallet-Schema-0.10-SQLite.sql
@@ -1,6 +1,28 @@
--
-- Created by SQL::Translator::Producer::SQLite
-- Created on Thu Oct 9 20:51:25 2014
+--
+-- Copyright 2014
+-- The Board of Trustees of the Leland Stanford Junior University
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included
+-- in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--
BEGIN TRANSACTION;
diff --git a/perl/sql/wallet-1.3-update-duo.sql b/perl/sql/wallet-1.3-update-duo.sql
new file mode 100644
index 0000000..affadcd
--- /dev/null
+++ b/perl/sql/wallet-1.3-update-duo.sql
@@ -0,0 +1,9 @@
+--
+-- Run on installing wallet 1.3 in order to update what the Duo types
+-- point to for modules.
+--
+
+UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-ldap';
+UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-pam';
+UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-radius';
+UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-rdp';
diff --git a/perl/t/data/acl-command b/perl/t/data/acl-command
new file mode 100755
index 0000000..b7c3066
--- /dev/null
+++ b/perl/t/data/acl-command
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# An external ACL implementation. Checks that the first argument is
+# eagle@eyrie.org, the second argument is "test", and then returns success,
+# failure, or reports an error based on whether the second argument is
+# success, failure, or error.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+#
+# See LICENSE for licensing terms.
+
+set -e
+
+# Check the initial principal argument.
+if [ "$1" != 'eagle@eyrie.org' ]; then
+ echo 'incorrect principal' >&2
+ exit 1
+fi
+
+# Check that the second and third arguments are file test (the test object).
+if [ "$2" != 'file' ]; then
+ echo 'incorrect second argument' >&2
+ exit 1
+fi
+if [ "$3" != 'test' ]; then
+ echo 'incorrect third argument' >&2
+ exit 1
+fi
+
+# Process the fourth argument.
+case $4 in
+ 'test success')
+ exit 0
+ ;;
+ 'test failure')
+ exit 1
+ ;;
+ 'test error')
+ echo 'some error' >&2
+ exit 1
+ ;;
+ *)
+ echo 'unknown fourth argument' >&2
+ exit 1
+ ;;
+esac
diff --git a/perl/t/general/acl.t b/perl/t/general/acl.t
index 1dd5c53..4de7493 100755
--- a/perl/t/general/acl.t
+++ b/perl/t/general/acl.t
@@ -12,11 +12,11 @@ use strict;
use warnings;
use POSIX qw(strftime);
-use Test::More tests => 101;
+use Test::More tests => 115;
use Wallet::ACL;
use Wallet::Admin;
-use Wallet::Server;
+use Wallet::Object::Base;
use lib 't/lib';
use Util;
@@ -46,7 +46,7 @@ $acl = eval { Wallet::ACL->create (3, $schema, @trace) };
ok (!defined ($acl), 'Creating with a numeric name');
is ($@, "ACL name may not be all numbers\n", ' with the right error message');
$acl = eval { Wallet::ACL->create ('test', $schema, @trace) };
-ok (!defined ($acl), 'Creating a duplicate object');
+ok (!defined ($acl), 'Creating a duplicate acl');
like ($@, qr/^cannot create ACL test: /, ' with the right error message');
$acl = eval { Wallet::ACL->new ('test2', $schema) };
ok (!defined ($acl), 'Searching for a non-existent ACL');
@@ -62,32 +62,6 @@ is ($@, '', ' with no exceptions');
ok ($acl->isa ('Wallet::ACL'), ' and the right class');
is ($acl->name, 'test', ' and the right name');
-# Test rename.
-if ($acl->rename ('example', @trace)) {
- ok (1, 'Renaming the ACL');
-} else {
- is ($acl->error, '', 'Renaming the ACL');
-}
-is ($acl->name, 'example', ' and the new name is right');
-is ($acl->id, 2, ' and the ID did not change');
-$acl = eval { Wallet::ACL->new ('test', $schema) };
-ok (!defined ($acl), ' and it cannot be found under the old name');
-is ($@, "ACL test not found\n", ' with the right error message');
-$acl = eval { Wallet::ACL->new ('example', $schema) };
-ok (defined ($acl), ' and it can be found with the new name');
-is ($@, '', ' with no exceptions');
-is ($acl->name, 'example', ' and the right name');
-is ($acl->id, 2, ' and the right ID');
-$acl = eval { Wallet::ACL->new (2, $schema) };
-ok (defined ($acl), ' and it can still found by ID');
-is ($@, '', ' with no exceptions');
-is ($acl->name, 'example', ' and the right name');
-is ($acl->id, 2, ' and the right ID');
-ok (! $acl->rename ('ADMIN', @trace),
- ' but renaming to an existing name fails');
-like ($acl->error, qr/^cannot rename ACL 2 to ADMIN: /,
- ' with the right error');
-
# Test add, check, remove, list, and show.
my @entries = $acl->list;
is (scalar (@entries), 0, 'ACL starts empty');
@@ -124,14 +98,14 @@ is ($entries[0][1], $user1, ' and the right identifier for 1');
is ($entries[1][0], 'krb5', ' and the right scheme for 2');
is ($entries[1][1], $user2, ' and the right identifier for 2');
my $expected = <<"EOE";
-Members of ACL example (id: 2) are:
+Members of ACL test (id: 2) are:
krb5 $user1
krb5 $user2
EOE
is ($acl->show, $expected, ' and show returns correctly');
ok (! $acl->remove ('krb5', $admin, @trace),
'Removing a nonexistent entry fails');
-is ($acl->error, "cannot remove krb5:$admin from 2: entry not found in ACL",
+is ($acl->error, "cannot remove krb5:$admin from test: entry not found in ACL",
' with the right error');
if ($acl->remove ('krb5', $user1, @trace)) {
ok (1, ' but removing the first user works');
@@ -145,7 +119,7 @@ is (scalar (@entries), 1, ' and now there is one entry');
is ($entries[0][0], 'krb5', ' with the right scheme');
is ($entries[0][1], $user2, ' and the right identifier');
ok (! $acl->add ('krb5', $user2), 'Adding the same entry again fails');
-like ($acl->error, qr/^cannot add \Qkrb5:$user2\E to 2: /,
+like ($acl->error, qr/^cannot add \Qkrb5:$user2\E to test: /,
' with the right error');
if ($acl->add ('krb5', '', @trace)) {
ok (1, 'Adding a bad entry works');
@@ -159,7 +133,7 @@ is ($entries[0][1], '', ' and the right identifier for 1');
is ($entries[1][0], 'krb5', ' and the right scheme for 2');
is ($entries[1][1], $user2, ' and the right identifier for 2');
$expected = <<"EOE";
-Members of ACL example (id: 2) are:
+Members of ACL test (id: 2) are:
krb5
krb5 $user2
EOE
@@ -187,17 +161,50 @@ if ($acl->remove ('krb5', '', @trace)) {
}
@entries = $acl->list;
is (scalar (@entries), 0, ' and now there are no entries');
-is ($acl->show, "Members of ACL example (id: 2) are:\n", ' and show concurs');
+is ($acl->show, "Members of ACL test (id: 2) are:\n", ' and show concurs');
is ($acl->check ($user2), 0, ' and the second user check fails');
is (scalar ($acl->check_errors), '', ' with no error message');
+# Test rename.
+my $acl_nest = eval { Wallet::ACL->create ('test-nesting', $schema, @trace) };
+ok (defined ($acl_nest), 'ACL creation for setting up nested');
+if ($acl_nest->add ('nested', 'test', @trace)) {
+ ok (1, ' and adding the nesting');
+} else {
+ is ($acl_nest->error, '', ' and adding the nesting');
+}
+if ($acl->rename ('example', @trace)) {
+ ok (1, 'Renaming the ACL');
+} else {
+ is ($acl->error, '', 'Renaming the ACL');
+}
+is ($acl->name, 'example', ' and the new name is right');
+is ($acl->id, 2, ' and the ID did not change');
+$acl = eval { Wallet::ACL->new ('test', $schema) };
+ok (!defined ($acl), ' and it cannot be found under the old name');
+is ($@, "ACL test not found\n", ' with the right error message');
+$acl = eval { Wallet::ACL->new ('example', $schema) };
+ok (defined ($acl), ' and it can be found with the new name');
+is ($@, '', ' with no exceptions');
+is ($acl->name, 'example', ' and the right name');
+is ($acl->id, 2, ' and the right ID');
+$acl = eval { Wallet::ACL->new (2, $schema) };
+ok (defined ($acl), ' and it can still found by ID');
+is ($@, '', ' with no exceptions');
+is ($acl->name, 'example', ' and the right name');
+is ($acl->id, 2, ' and the right ID');
+ok (! $acl->rename ('ADMIN', @trace),
+ ' but renaming to an existing name fails');
+like ($acl->error, qr/^cannot rename ACL example to ADMIN: /,
+ ' with the right error');
+@entries = $acl_nest->list;
+is ($entries[0][1], 'example', ' and the name in a nested ACL updated');
+
# Test history.
my $date = strftime ('%Y-%m-%d %H:%M:%S', localtime $trace[2]);
my $history = <<"EOO";
$date create
by $admin from $host
-$date rename from test
- by $admin from $host
$date add krb5 $user1
by $admin from $host
$date add krb5 $user2
@@ -210,14 +217,24 @@ $date remove krb5 $user2
by $admin from $host
$date remove krb5
by $admin from $host
+$date rename from test
+ by $admin from $host
EOO
is ($acl->history, $history, 'History is correct');
# Test destroy.
+$acl->destroy (@trace);
+is ($acl->error, 'cannot destroy ACL example: ACL is nested in ACL test-nesting',
+ 'Destroying a nested ACL fails');
+if ($acl_nest->remove ('nested', 'example', @trace)) {
+ ok (1, ' and removing the nesting succeeds');
+} else {
+ is ($acl_nest->error, '', 'and removing the nesting succeeds');
+}
if ($acl->destroy (@trace)) {
- ok (1, 'Destroying the ACL works');
+ ok (1, ' and now destroying the ACL works');
} else {
- is ($acl->error, '', 'Destroying the ACL works');
+ is ($acl->error, '', ' and now destroying the ACL works');
}
$acl = eval { Wallet::ACL->new ('example', $schema) };
ok (!defined ($acl), ' and now cannot be found');
@@ -225,11 +242,71 @@ is ($@, "ACL example not found\n", ' with the right error message');
$acl = eval { Wallet::ACL->new (2, $schema) };
ok (!defined ($acl), ' or by ID');
is ($@, "ACL 2 not found\n", ' with the right error message');
+@entries = $acl_nest->list;
+is (scalar (@entries), 0, ' and it is no longer a nested entry');
$acl = eval { Wallet::ACL->create ('example', $schema, @trace) };
ok (defined ($acl), ' and creating another with the same name works');
is ($@, '', ' with no exceptions');
is ($acl->name, 'example', ' and the right name');
-like ($acl->id, qr{\A[23]\z}, ' and an ID of 2 or 3');
+like ($acl->id, qr{\A[34]\z}, ' and an ID of 3 or 4');
+
+# Test replace. by creating three acls, then assigning two objects to the
+# first, one to the second, and another to the third. Then replace the first
+# acl with the second, so that we can verify that multiple objects are moved,
+# that an object already belonging to the new acl is okay, and that the
+# objects with unrelated ACL are unaffected.
+my ($acl_old, $acl_new, $acl_other, $obj_old_one, $obj_old_two, $obj_new,
+ $obj_unrelated);
+eval {
+ $acl_old = Wallet::ACL->create ('example-old', $schema, @trace);
+ $acl_new = Wallet::ACL->create ('example-new', $schema, @trace);
+ $acl_other = Wallet::ACL->create ('example-other', $schema, @trace);
+};
+is ($@, '', 'ACLs needed for testing replace are created');
+eval {
+ $obj_old_one = Wallet::Object::Base->create ('keytab',
+ 'service/test1@EXAMPLE.COM',
+ $schema, @trace);
+ $obj_old_two = Wallet::Object::Base->create ('keytab',
+ 'service/test2@EXAMPLE.COM',
+ $schema, @trace);
+ $obj_new = Wallet::Object::Base->create ('keytab',
+ 'service/test3@EXAMPLE.COM',
+ $schema, @trace);
+ $obj_unrelated = Wallet::Object::Base->create ('keytab',
+ 'service/test4@EXAMPLE.COM',
+ $schema, @trace);
+};
+is ($@, '', ' and so were needed objects');
+if ($obj_old_one->owner ('example-old', @trace)
+ && $obj_old_two->owner ('example-old', @trace)
+ && $obj_new->owner ('example-new', @trace)
+ && $obj_unrelated->owner ('example-other', @trace)) {
+
+ ok (1, ' and setting initial ownership on the objects succeeds');
+}
+is ($acl_old->replace('example-new', @trace), 1,
+ ' and replace ran successfully');
+eval {
+ $obj_old_one = Wallet::Object::Base->new ('keytab',
+ 'service/test1@EXAMPLE.COM',
+ $schema);
+ $obj_old_two = Wallet::Object::Base->new ('keytab',
+ 'service/test2@EXAMPLE.COM',
+ $schema);
+ $obj_new = Wallet::Object::Base->new ('keytab',
+ 'service/test3@EXAMPLE.COM',
+ $schema);
+ $obj_unrelated = Wallet::Object::Base->new ('keytab',
+ 'service/test4@EXAMPLE.COM',
+ $schema);
+};
+is ($obj_old_one->owner, 'example-new', ' and first replace is correct');
+is ($obj_old_two->owner, 'example-new', ' and second replace is correct');
+is ($obj_new->owner, 'example-new',
+ ' and object already with new acl is correct');
+is ($obj_unrelated->owner, 'example-other',
+ ' and unrelated object ownership is correct');
# Clean up.
$setup->destroy;
diff --git a/perl/t/general/report.t b/perl/t/general/report.t
index 8d348ed..e47cdc6 100755
--- a/perl/t/general/report.t
+++ b/perl/t/general/report.t
@@ -11,7 +11,7 @@
use strict;
use warnings;
-use Test::More tests => 197;
+use Test::More tests => 223;
use Wallet::Admin;
use Wallet::Report;
@@ -41,6 +41,32 @@ is (scalar (@acls), 1, 'One ACL in the database');
is ($acls[0][0], 1, ' and that is ACL ID 1');
is ($acls[0][1], 'ADMIN', ' with the right name');
+# Check to see that we have all types that we expect.
+my @types = $report->types;
+is (scalar (@types), 10, 'There are ten types created');
+is ($types[0][0], 'base', ' and the first member is correct');
+is ($types[1][0], 'duo', ' and the second member is correct');
+is ($types[2][0], 'duo-ldap', ' and the third member is correct');
+is ($types[3][0], 'duo-pam', ' and the fourth member is correct');
+is ($types[4][0], 'duo-radius', ' and the fifth member is correct');
+is ($types[5][0], 'duo-rdp', ' and the sixth member is correct');
+is ($types[6][0], 'file', ' and the seventh member is correct');
+is ($types[7][0], 'keytab', ' and the eighth member is correct');
+is ($types[8][0], 'password', ' and the nineth member is correct');
+is ($types[9][0], 'wa-keyring', ' and the tenth member is correct');
+
+# And that we have all schemes that we expect.
+my @schemes = $report->acl_schemes;
+is (scalar (@schemes), 8, 'There are seven acl schemes created');
+is ($schemes[0][0], 'base', ' and the first member is correct');
+is ($schemes[1][0], 'krb5', ' and the second member is correct');
+is ($schemes[2][0], 'krb5-regex', ' and the third member is correct');
+is ($schemes[3][0], 'ldap-attr', ' and the fourth member is correct');
+is ($schemes[4][0], 'ldap-attr-root', ' and the fifth member is correct');
+is ($schemes[5][0], 'nested', ' and the sixth member is correct');
+is ($schemes[6][0], 'netdb', ' and the seventh member is correct');
+is ($schemes[7][0], 'netdb-root', ' and the eighth member is correct');
+
# Create an object.
my $server = eval { Wallet::Server->new ('admin@EXAMPLE.COM', 'localhost') };
is ($@, '', 'Creating a server instance did not die');
@@ -257,6 +283,22 @@ is (scalar (@lines), 1, 'Searching for ACL naming violations finds one');
is ($lines[0][0], 3, ' and the first has the right ID');
is ($lines[0][1], 'second', ' and the right name');
+# Set a host-based object matching script so that we can test the host report.
+# The deactivation trick isn't needed here.
+package Wallet::Config;
+sub is_for_host {
+ my ($type, $name, $host) = @_;
+ my ($service, $principal) = split ('/', $name, 2);
+ return 0 unless $service && $principal;
+ return 1 if $host eq $principal;
+ return 0;
+}
+package main;
+@lines = $report->objects_hostname ('host', 'admin');
+is (scalar (@lines), 1, 'Searching for host-based objects finds one');
+is ($lines[0][0], 'base', ' and the first has the right type');
+is ($lines[0][1], 'service/admin', ' and the right name');
+
# Set up a file bucket so that we can create an object we can retrieve.
system ('rm -rf test-files') == 0 or die "cannot remove test-files\n";
mkdir 'test-files' or die "cannot create test-files: $!\n";
@@ -325,6 +367,13 @@ is ($server->acl_add ('third', 'base', 'baz'), 1,
is (scalar (@acls), 0, 'There are no duplicate ACLs');
is ($report->error, undef, ' and no error');
+# See if the acl nesting report works correctly.
+is ($server->acl_add ('fourth', 'nested', 'second'), 1,
+ 'Adding an ACL as a nested entry for another works');
+@acls = $report->acls ('nesting', 'second');
+is (scalar (@acls), 1, ' and the nested report shows one nesting');
+is ($acls[0][1], 'fourth', ' with the correct ACL nesting it');
+
# Clean up.
$admin->destroy;
system ('rm -r test-files') == 0 or die "cannot remove test-files\n";
diff --git a/perl/t/general/server.t b/perl/t/general/server.t
index 0a527a5..8f4c16c 100755
--- a/perl/t/general/server.t
+++ b/perl/t/general/server.t
@@ -89,7 +89,7 @@ is ($server->acl_rename ('empty', 'test'), undef,
is ($server->error, 'ACL empty not found', ' and returns the right error');
is ($server->acl_rename ('test', 'test2'), undef,
' and cannot rename to an existing name');
-like ($server->error, qr/^cannot rename ACL 6 to test2: /,
+like ($server->error, qr/^cannot rename ACL test to test2: /,
' and returns the right error');
is ($server->acl_rename ('test', 'empty'), 1, 'Renaming does work');
is ($server->acl_rename ('test', 'empty'), undef, ' but not twice');
@@ -138,7 +138,7 @@ is ($server->error, 'ACL test not found', ' and returns the right error');
is ($server->acl_remove ('empty', 'krb5', $user2), undef,
' and removing an entry not there fails');
is ($server->error,
- "cannot remove krb5:$user2 from 6: entry not found in ACL",
+ "cannot remove krb5:$user2 from empty: entry not found in ACL",
' and returns the right error');
is ($server->acl_show ('empty'),
"Members of ACL empty (id: 6) are:\n krb5 $user1\n",
@@ -148,7 +148,7 @@ is ($server->acl_remove ('empty', 'krb5', $user1), 1,
is ($server->acl_remove ('empty', 'krb5', $user1), undef,
' but does not work twice');
is ($server->error,
- "cannot remove krb5:$user1 from 6: entry not found in ACL",
+ "cannot remove krb5:$user1 from empty: entry not found in ACL",
' and returns the right error');
is ($server->acl_show ('empty'), "Members of ACL empty (id: 6) are:\n",
' and show returns the correct status');
@@ -168,7 +168,7 @@ is ($server->acl_remove ('ADMIN', 'krb5', $user1), 1, ' and then remove it');
is ($server->acl_remove ('ADMIN', 'krb5', $user1), undef,
' and remove a user not on it');
is ($server->error,
- "cannot remove krb5:$user1 from 1: entry not found in ACL",
+ "cannot remove krb5:$user1 from ADMIN: entry not found in ACL",
' and get the right error');
# Now, create a few objects to use for testing and test the object API while
@@ -994,7 +994,7 @@ is ($server->owner ('base', 'service/acl-user', 'test-destroy'), 1,
is ($server->acl_destroy ('test-destroy'), undef,
' and now we cannot destroy that ACL');
is ($server->error,
- 'cannot destroy ACL 9: ACL in use by base:service/acl-user',
+ 'cannot destroy ACL test-destroy: ACL in use by base:service/acl-user',
' with the right error');
is ($server->owner ('base', 'service/acl-user', ''), 1,
' but after we clear the owner');
diff --git a/perl/t/object/base.t b/perl/t/object/base.t
index ee9ff4b..8fedd64 100755
--- a/perl/t/object/base.t
+++ b/perl/t/object/base.t
@@ -12,7 +12,7 @@ use strict;
use warnings;
use POSIX qw(strftime);
-use Test::More tests => 137;
+use Test::More tests => 139;
use Wallet::ACL;
use Wallet::Admin;
@@ -208,6 +208,9 @@ is ($object->flag_clear ('locked', @trace), 1, 'Clearing locked succeeds');
eval { $object->get (@trace) };
is ($@, "Do not instantiate Wallet::Object::Base directly\n",
'Get fails with the right error');
+ok (!$object->update (@trace), 'Update fails');
+is ($object->error, 'update is not supported for this type, use get instead',
+ ' with the right error');
ok (! $object->store ("Some data", @trace), 'Store fails');
is ($object->error, "cannot store keytab:$princ: object type is immutable",
' with the right error');
diff --git a/perl/t/object/duo-ldap.t b/perl/t/object/duo-ldap.t
index 3648eba..8a00dbb 100644
--- a/perl/t/object/duo-ldap.t
+++ b/perl/t/object/duo-ldap.t
@@ -26,7 +26,7 @@ BEGIN {
BEGIN {
use_ok('Wallet::Admin');
use_ok('Wallet::Config');
- use_ok('Wallet::Object::Duo::LDAPProxy');
+ use_ok('Wallet::Object::Duo');
}
use lib 't/lib';
@@ -53,15 +53,14 @@ my $mock = Net::Duo::Mock::Agent->new ({ key_file => 't/data/duo/keys.json' });
# Test error handling in the absence of configuration.
my $object = eval {
- Wallet::Object::Duo::LDAPProxy->new ('duo-ldap', 'test', $schema);
+ Wallet::Object::Duo->new ('duo-ldap', 'test', $schema);
};
-is ($object, undef, 'Wallet::Object::Duo::LDAPProxy new with no config failed');
+is ($object, undef, 'Wallet::Object::Duo new with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
$object = eval {
- Wallet::Object::Duo::LDAPProxy->create ('duo-ldap', 'test', $schema,
- @trace);
+ Wallet::Object::Duo->create ('duo-ldap', 'test', $schema, @trace);
};
-is ($object, undef, 'Wallet::Object::Duo::LDAPProxy creation with no config failed');
+is ($object, undef, 'Wallet::Object::Duo creation with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
# Set up the Duo configuration.
@@ -83,9 +82,8 @@ $mock->expect (
response_file => 't/data/duo/integration.json',
}
);
-$object = Wallet::Object::Duo::LDAPProxy->create ('duo-ldap', 'test', $schema,
- @trace);
-isa_ok ($object, 'Wallet::Object::Duo::LDAPProxy');
+$object = Wallet::Object::Duo->create ('duo-ldap', 'test', $schema, @trace);
+isa_ok ($object, 'Wallet::Object::Duo');
# Check the metadata about the new wallet object.
$expected = <<"EOO";
@@ -127,7 +125,7 @@ is ($object->flag_clear ('locked', @trace), 1,
'...and clearing locked flag works');
# Create a new object by wallet type and name.
-$object = Wallet::Object::Duo::LDAPProxy->new ('duo-ldap', 'test', $schema);
+$object = Wallet::Object::Duo->new ('duo-ldap', 'test', $schema);
# Test deleting an integration. We can't test this entirely properly because
# currently Net::Duo::Mock::Agent doesn't support stacking multiple expected
@@ -144,8 +142,7 @@ TODO: {
local $TODO = 'Net::Duo::Mock::Agent not yet capable';
is ($object->destroy (@trace), 1, 'Duo object deletion succeeded');
- $object = eval { Wallet::Object::Duo::LDAPProxy->new ('duo-ldap', 'test',
- $schema) };
+ $object = eval { Wallet::Object::Duo->new ('duo-ldap', 'test', $schema) };
is ($object, undef, '...and now object cannot be retrieved');
is ($@, "cannot find duo:test\n", '...with correct error');
}
diff --git a/perl/t/object/duo-pam.t b/perl/t/object/duo-pam.t
index 7b88787..047343e 100644
--- a/perl/t/object/duo-pam.t
+++ b/perl/t/object/duo-pam.t
@@ -26,7 +26,7 @@ BEGIN {
BEGIN {
use_ok('Wallet::Admin');
use_ok('Wallet::Config');
- use_ok('Wallet::Object::Duo::PAM');
+ use_ok('Wallet::Object::Duo');
}
use lib 't/lib';
@@ -53,14 +53,14 @@ my $mock = Net::Duo::Mock::Agent->new ({ key_file => 't/data/duo/keys.json' });
# Test error handling in the absence of configuration.
my $object = eval {
- Wallet::Object::Duo::PAM->new ('duo-pam', 'test', $schema);
+ Wallet::Object::Duo->new ('duo-pam', 'test', $schema);
};
-is ($object, undef, 'Wallet::Object::Duo::PAM new with no config failed');
+is ($object, undef, 'Wallet::Object::Duo new with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
$object = eval {
- Wallet::Object::Duo::PAM->create ('duo-pam', 'test', $schema, @trace);
+ Wallet::Object::Duo->create ('duo-pam', 'test', $schema, @trace);
};
-is ($object, undef, 'Wallet::Object::Duo::PAM creation with no config failed');
+is ($object, undef, 'Wallet::Object::Duo creation with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
# Set up the Duo configuration.
@@ -82,9 +82,8 @@ $mock->expect (
response_file => 't/data/duo/integration.json',
}
);
-$object = Wallet::Object::Duo::PAM->create ('duo-pam', 'test', $schema,
- @trace);
-isa_ok ($object, 'Wallet::Object::Duo::PAM');
+$object = Wallet::Object::Duo->create ('duo-pam', 'test', $schema, @trace);
+isa_ok ($object, 'Wallet::Object::Duo');
# Check the metadata about the new wallet object.
$expected = <<"EOO";
@@ -126,7 +125,7 @@ is ($object->flag_clear ('locked', @trace), 1,
'...and clearing locked flag works');
# Create a new object by wallet type and name.
-$object = Wallet::Object::Duo::PAM->new ('duo-pam', 'test', $schema);
+$object = Wallet::Object::Duo->new ('duo-pam', 'test', $schema);
# Test deleting an integration. We can't test this entirely properly because
# currently Net::Duo::Mock::Agent doesn't support stacking multiple expected
@@ -143,8 +142,7 @@ TODO: {
local $TODO = 'Net::Duo::Mock::Agent not yet capable';
is ($object->destroy (@trace), 1, 'Duo object deletion succeeded');
- $object = eval { Wallet::Object::Duo::PAM->new ('duo-pam', 'test',
- $schema) };
+ $object = eval { Wallet::Object::Duo->new ('duo-pam', 'test', $schema) };
is ($object, undef, '...and now object cannot be retrieved');
is ($@, "cannot find duo:test\n", '...with correct error');
}
diff --git a/perl/t/object/duo-radius.t b/perl/t/object/duo-radius.t
index f258518..55cbb9d 100644
--- a/perl/t/object/duo-radius.t
+++ b/perl/t/object/duo-radius.t
@@ -26,7 +26,7 @@ BEGIN {
BEGIN {
use_ok('Wallet::Admin');
use_ok('Wallet::Config');
- use_ok('Wallet::Object::Duo::RadiusProxy');
+ use_ok('Wallet::Object::Duo');
}
use lib 't/lib';
@@ -53,17 +53,16 @@ my $mock = Net::Duo::Mock::Agent->new ({ key_file => 't/data/duo/keys.json' });
# Test error handling in the absence of configuration.
my $object = eval {
- Wallet::Object::Duo::RadiusProxy->new ('duo-raduys', 'test', $schema);
+ Wallet::Object::Duo->new ('duo-radius', 'test', $schema);
};
is ($object, undef,
- 'Wallet::Object::Duo::RadiusProxy new with no config failed');
+ 'Wallet::Object::Duo new with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
$object = eval {
- Wallet::Object::Duo::RadiusProxy->create ('duo-radius', 'test', $schema,
- @trace);
+ Wallet::Object::Duo->create ('duo-radius', 'test', $schema, @trace);
};
is ($object, undef,
- 'Wallet::Object::Duo::RadiusProxy creation with no config failed');
+ 'Wallet::Object::Duo creation with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
# Set up the Duo configuration.
@@ -85,9 +84,8 @@ $mock->expect (
response_file => 't/data/duo/integration-radius.json',
}
);
-$object = Wallet::Object::Duo::RadiusProxy->create ('duo-radius', 'test',
- $schema, @trace);
-isa_ok ($object, 'Wallet::Object::Duo::RadiusProxy');
+$object = Wallet::Object::Duo->create ('duo-radius', 'test', $schema, @trace);
+isa_ok ($object, 'Wallet::Object::Duo');
# Check the metadata about the new wallet object.
$expected = <<"EOO";
@@ -130,8 +128,7 @@ is ($object->flag_clear ('locked', @trace), 1,
'...and clearing locked flag works');
# Create a new object by wallet type and name.
-$object = Wallet::Object::Duo::RadiusProxy->new ('duo-radius', 'test',
- $schema);
+$object = Wallet::Object::Duo->new ('duo-radius', 'test', $schema);
# Test deleting an integration. We can't test this entirely properly because
# currently Net::Duo::Mock::Agent doesn't support stacking multiple expected
@@ -149,7 +146,7 @@ TODO: {
is ($object->destroy (@trace), 1, 'Duo object deletion succeeded');
$object = eval {
- Wallet::Object::Duo::RadiusProxy->new ('duo-radius', 'test', $schema);
+ Wallet::Object::Duo->new ('duo-radius', 'test', $schema);
};
is ($object, undef, '...and now object cannot be retrieved');
is ($@, "cannot find duo:test\n", '...with correct error');
diff --git a/perl/t/object/duo-rdp.t b/perl/t/object/duo-rdp.t
index 9b2d566..25060ac 100644
--- a/perl/t/object/duo-rdp.t
+++ b/perl/t/object/duo-rdp.t
@@ -26,7 +26,7 @@ BEGIN {
BEGIN {
use_ok('Wallet::Admin');
use_ok('Wallet::Config');
- use_ok('Wallet::Object::Duo::RDP');
+ use_ok('Wallet::Object::Duo');
}
use lib 't/lib';
@@ -53,14 +53,14 @@ my $mock = Net::Duo::Mock::Agent->new ({ key_file => 't/data/duo/keys.json' });
# Test error handling in the absence of configuration.
my $object = eval {
- Wallet::Object::Duo::RDP->new ('duo-rdp', 'test', $schema);
+ Wallet::Object::Duo->new ('duo-rdp', 'test', $schema);
};
-is ($object, undef, 'Wallet::Object::Duo::RDP new with no config failed');
+is ($object, undef, 'Wallet::Object::Duo new with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
$object = eval {
- Wallet::Object::Duo::RDP->create ('duo-rdp', 'test', $schema, @trace);
+ Wallet::Object::Duo->create ('duo-rdp', 'test', $schema, @trace);
};
-is ($object, undef, 'Wallet::Object::Duo::RDP creation with no config failed');
+is ($object, undef, 'Wallet::Object::Duo creation with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
# Set up the Duo configuration.
@@ -82,9 +82,8 @@ $mock->expect (
response_file => 't/data/duo/integration-rdp.json',
}
);
-$object = Wallet::Object::Duo::RDP->create ('duo-rdp', 'test', $schema,
- @trace);
-isa_ok ($object, 'Wallet::Object::Duo::RDP');
+$object = Wallet::Object::Duo->create ('duo-rdp', 'test', $schema, @trace);
+isa_ok ($object, 'Wallet::Object::Duo');
# Check the metadata about the new wallet object.
$expected = <<"EOO";
@@ -125,7 +124,7 @@ is ($object->flag_clear ('locked', @trace), 1,
'...and clearing locked flag works');
# Create a new object by wallet type and name.
-$object = Wallet::Object::Duo::RDP->new ('duo-rdp', 'test', $schema);
+$object = Wallet::Object::Duo->new ('duo-rdp', 'test', $schema);
# Test deleting an integration. We can't test this entirely properly because
# currently Net::Duo::Mock::Agent doesn't support stacking multiple expected
@@ -142,8 +141,7 @@ TODO: {
local $TODO = 'Net::Duo::Mock::Agent not yet capable';
is ($object->destroy (@trace), 1, 'Duo object deletion succeeded');
- $object = eval { Wallet::Object::Duo::RDP->new ('duo-rdp', 'test',
- $schema) };
+ $object = eval { Wallet::Object::Duo->new ('duo-rdp', 'test', $schema) };
is ($object, undef, '...and now object cannot be retrieved');
is ($@, "cannot find duo:test\n", '...with correct error');
}
diff --git a/perl/t/object/keytab.t b/perl/t/object/keytab.t
index 69db438..111b7d0 100755
--- a/perl/t/object/keytab.t
+++ b/perl/t/object/keytab.t
@@ -12,7 +12,7 @@ use strict;
use warnings;
use POSIX qw(strftime);
-use Test::More tests => 141;
+use Test::More tests => 142;
BEGIN { $Wallet::Config::KEYTAB_TMP = '.' }
@@ -25,15 +25,28 @@ use Wallet::Object::Keytab;
use lib 't/lib';
use Util;
-# Mapping of klist -ke encryption type names to the strings that Kerberos uses
-# internally. It's very annoying to have to maintain this, and it probably
-# breaks with Heimdal.
+# Mapping of klist -ke output from old MIT Kerberos implementations to to the
+# strings that Kerberos uses internally. It's very annoying to have to
+# maintain this, and it probably breaks with Heimdal.
+#
+# Newer versions of MIT Kerberos just print out the canonical enctype names
+# and don't need this logic, but the current test requires that they still
+# have entries. That's why the second set where the key and value are the
+# same.
my %enctype =
('triple des cbc mode with hmac/sha1' => 'des3-cbc-sha1',
'des cbc mode with crc-32' => 'des-cbc-crc',
'des cbc mode with rsa-md5' => 'des-cbc-md5',
+ 'aes-128 cts mode with 96-bit sha-1 hmac' => 'aes128-cts-hmac-sha1-96',
'aes-256 cts mode with 96-bit sha-1 hmac' => 'aes256-cts-hmac-sha1-96',
- 'arcfour with hmac/md5' => 'rc4-hmac');
+ 'arcfour with hmac/md5' => 'rc4-hmac',
+
+ 'des3-cbc-sha1' => 'des3-cbc-sha1',
+ 'des-cbc-crc' => 'des-cbc-crc',
+ 'des-cbc-md5' => 'des-cbc-md5',
+ 'aes128-cts-hmac-sha1-96' => 'aes128-cts-hmac-sha1-96',
+ 'aes256-cts-hmac-sha1-96' => 'aes256-cts-hmac-sha1-96',
+ 'rc4-hmac' => 'rc4-hmac');
# Some global defaults to use.
my $user = 'admin@EXAMPLE.COM';
@@ -159,7 +172,7 @@ my $date = strftime ('%Y-%m-%d %H:%M:%S', localtime $trace[2]);
# Basic keytab creation and manipulation tests.
SKIP: {
- skip 'no keytab configuration', 52 unless -f 't/data/test.keytab';
+ skip 'no keytab configuration', 53 unless -f 't/data/test.keytab';
# Set up our configuration.
$Wallet::Config::KEYTAB_FILE = 't/data/test.keytab';
@@ -296,6 +309,7 @@ EOO
@trace)
};
ok (defined ($object), 'Creating good principal succeeds');
+ is ($@, '', ' with no error');
ok (created ('wallet/one'), ' and the principal was created');
SKIP: {
skip 'no kadmin program test for Heimdal', 2
diff --git a/perl/t/object/password.t b/perl/t/object/password.t
new file mode 100644
index 0000000..306d82b
--- /dev/null
+++ b/perl/t/object/password.t
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+#
+# Tests for the password object implementation. Only includes tests that are
+# basic or different from the file object implementation.
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2015
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+use strict;
+use warnings;
+
+use POSIX qw(strftime);
+use Test::More tests => 33;
+
+use Wallet::Admin;
+use Wallet::Config;
+use Wallet::Object::Password;
+
+use lib 't/lib';
+use Util;
+
+# Some global defaults to use.
+my $user = 'admin@EXAMPLE.COM';
+my $host = 'localhost';
+my @trace = ($user, $host, time);
+
+# Flush all output immediately.
+$| = 1;
+
+# Use Wallet::Admin to set up the database.
+system ('rm -rf test-files') == 0 or die "cannot remove test-files\n";
+db_setup;
+my $admin = eval { Wallet::Admin->new };
+is ($@, '', 'Database connection succeeded');
+is ($admin->reinitialize ($user), 1, 'Database initialization succeeded');
+my $schema = $admin->schema;
+
+# Use this to accumulate the history traces so that we can check history.
+my $history = '';
+my $date = strftime ('%Y-%m-%d %H:%M:%S', localtime $trace[2]);
+
+$Wallet::Config::PWD_FILE_BUCKET = undef;
+
+# Test error handling in the absence of configuration.
+my $object = eval {
+ Wallet::Object::Password->create ('password', 'test', $schema, @trace)
+ };
+ok (defined ($object), 'Creating a basic password object succeeds');
+ok ($object->isa ('Wallet::Object::Password'), ' and is the right class');
+is ($object->get (@trace), undef, ' and get fails');
+is ($object->error, 'password support not configured',
+ ' with the right error');
+is ($object->store (@trace), undef, ' and store fails');
+is ($object->error, 'password support not configured',
+ ' with the right error');
+is ($object->destroy (@trace), 1, ' but destroy succeeds');
+
+# Set up our configuration.
+mkdir 'test-files' or die "cannot create test-files: $!\n";
+$Wallet::Config::PWD_FILE_BUCKET = 'test-files';
+$Wallet::Config::PWD_LENGTH_MIN = 10;
+$Wallet::Config::PWD_LENGTH_MAX = 10;
+
+# Okay, now we can test. First, the basic object without store.
+$object = eval {
+ Wallet::Object::Password->create ('password', 'test', $schema, @trace)
+ };
+ok (defined ($object), 'Creating a basic password object succeeds');
+ok ($object->isa ('Wallet::Object::Password'), ' and is the right class');
+my $pwd = $object->get (@trace);
+like ($pwd, qr{^.{$Wallet::Config::PWD_LENGTH_MIN}$},
+ ' and get creates a random password string of the right length');
+ok (-d 'test-files/09', ' and the hash bucket was created');
+ok (-f 'test-files/09/test', ' and the file exists');
+is (contents ('test-files/09/test'), $pwd, ' with the right contents');
+my $pwd2 = $object->get (@trace);
+is ($pwd, $pwd2, ' and getting again gives the same string');
+is ($object->destroy (@trace), 1, ' and destroying the object succeeds');
+
+# Now check to see if the password length is adjusted.
+$Wallet::Config::PWD_LENGTH_MIN = 20;
+$Wallet::Config::PWD_LENGTH_MAX = 20;
+$object = eval {
+ Wallet::Object::Password->create ('password', 'test', $schema, @trace)
+ };
+ok (defined ($object), 'Recreating the object succeeds');
+$pwd = $object->get (@trace);
+like ($pwd, qr{^.{$Wallet::Config::PWD_LENGTH_MIN}$},
+ ' and get creates a random password string of a longer length');
+is ($object->destroy (@trace), 1, ' and destroying the object succeeds');
+
+# Now store something and be sure that we get something reasonable.
+$object = eval {
+ Wallet::Object::Password->create ('password', 'test', $schema, @trace)
+ };
+ok (defined ($object), 'Recreating the object succeeds');
+is ($object->store ("foo\n", @trace), 1, ' and storing data in it succeeds');
+ok (-f 'test-files/09/test', ' and the file exists');
+is (contents ('test-files/09/test'), 'foo', ' with the right contents');
+is ($object->get (@trace), "foo\n", ' and get returns correctly');
+unlink 'test-files/09/test';
+is ($object->get (@trace), undef,
+ ' and get will not autocreate a password if there used to be data');
+is ($object->error, 'cannot get password:test: object has not been stored',
+ ' as if it had not been stored');
+is ($object->store ("bar\n\0baz\n", @trace), 1, ' but storing again works');
+ok (-f 'test-files/09/test', ' and the file exists');
+is (contents ('test-files/09/test'), 'bar', ' with the right contents');
+is ($object->get (@trace), "bar\n\0baz\n", ' and get returns correctly');
+
+# And check to make sure update changes the contents.
+$pwd = $object->update (@trace);
+isnt ($pwd, "bar\n\0baz\n", 'Update changes the contents');
+like ($pwd, qr{^.{$Wallet::Config::PWD_LENGTH_MIN}$},
+ ' to a random password string of the right length');
+
+# Clean up.
+$admin->destroy;
+END {
+ system ('rm -r test-files') == 0 or die "cannot remove test-files\n";
+ unlink ('wallet-db');
+}
diff --git a/perl/t/policy/stanford.t b/perl/t/policy/stanford.t
index 555086c..d2727c8 100755
--- a/perl/t/policy/stanford.t
+++ b/perl/t/policy/stanford.t
@@ -16,7 +16,7 @@ use 5.008;
use strict;
use warnings;
-use Test::More tests => 101;
+use Test::More tests => 130;
use lib 't/lib';
use Util;
@@ -24,10 +24,16 @@ use Util;
# Load the naming policy module.
BEGIN {
use_ok('Wallet::Admin');
- use_ok('Wallet::Policy::Stanford', qw(default_owner verify_name));
+ use_ok('Wallet::Policy::Stanford',
+ qw(default_owner verify_name is_for_host));
use_ok('Wallet::Server');
}
+# Set up our configuration for netdb, needed for the netdb verifier.
+$Wallet::Config::NETDB_REALM = 'stanford.edu';
+$Wallet::Config::NETDB_REMCTL_CACHE = $ENV{KRB5CCNAME};
+$Wallet::Config::NETDB_REMCTL_HOST = 'netdb-node-roles-rc.stanford.edu';
+
# Various valid keytab names.
my @VALID_KEYTABS = qw(host/example.stanford.edu HTTP/example.stanford.edu
service/example example/cgi class-example01/cgi dept-01example/cgi
@@ -101,160 +107,209 @@ for my $name (@INVALID_FILES) {
isnt(verify_name('file', $name), undef, "Invalid file $name");
}
-# Now we need an actual database. Use Wallet::Admin to set it up.
-db_setup;
-my $setup = eval { Wallet::Admin->new };
-is($@, q{}, 'Database initialization did not die');
-is($setup->reinitialize($ADMIN), 1, 'Database initialization succeeded');
-my $server = eval { Wallet::Server->new(@TRACE) };
-is($@, q{}, 'Server creation did not die');
+# Now test a few cases for checking to see if a file is host-based. We don't
+# test the legacy examples because they're more complicated and less obvious.
+for my $name (@VALID_KEYTABS) {
+ my $hostname = 'example.stanford.edu';
+ if ($name =~ m{\b$hostname\b}) {
+ is(is_for_host('keytab', $name, $hostname), 1,
+ "Keytab $name belongs to $hostname");
+ } else {
+ is(is_for_host('keytab', $name, $hostname), 0,
+ "Keytab $name doesn't belong to $hostname");
+ }
+}
+for my $name (@VALID_FILES) {
+ my $hostname = 'example.stanford.edu';
+ if ($name =~ m{\b$hostname\b}) {
+ is(is_for_host('file', $name, $hostname), 1,
+ "File $name belongs to $hostname");
+ } else {
+ is(is_for_host('file', $name, $hostname), 0,
+ "File $name doesn't belong to $hostname");
+ }
+}
-# Create a host/example.stanford.edu ACL that uses the netdb ACL type.
-is($server->acl_create('host/example.stanford.edu'), 1, 'Created netdb ACL');
-is(
- $server->acl_add('host/example.stanford.edu', 'netdb',
- 'example.stanford.edu'),
- 1,
- '...with netdb ACL line'
-);
-is(
- $server->acl_add('host/example.stanford.edu', 'krb5',
- 'host/example.stanford.edu@stanford.edu'),
- 1,
- '...and krb5 ACL line'
-);
+# Now we need an actual database. Use Wallet::Admin to set it up. These
+# remaining tests require creating NetDB ACLs, so need a Stanford Kerberos
+# principal currently.
+my $klist = `klist 2>&1` || '';
+SKIP: {
+ skip "tests useful only with Stanford Kerberos tickets", 27
+ unless ($klist =~ /^(Default p|\s+P)rincipal: \S+\@stanford\.edu$/m);
-# Likewise for host/foo.example.edu with the netdb-root ACL type.
-is($server->acl_create('host/foo.stanford.edu'), 1, 'Created netdb-root ACL');
-is(
- $server->acl_add('host/foo.stanford.edu', 'netdb-root',
- 'foo.stanford.edu'),
- 1,
- '...with netdb-root ACL line'
-);
-is(
- $server->acl_add('host/foo.stanford.edu', 'krb5',
- 'host/foo.stanford.edu@stanford.edu'),
- 1,
- '...and krb5 ACL line'
-);
+ db_setup;
+ my $setup = eval { Wallet::Admin->new };
+ is($@, q{}, 'Database initialization did not die');
+ is($setup->reinitialize($ADMIN), 1, 'Database initialization succeeded');
+ my $server = eval { Wallet::Server->new(@TRACE) };
+ is($@, q{}, 'Server creation did not die');
-# Create a group/its-idg ACL, which will be used for autocreation of file
-# objects.
-is($server->acl_create('group/its-idg'), 1, 'Created group/its-idg ACL');
-is($server->acl_add('group/its-idg', 'krb5', $ADMIN), 1, '...with member');
+ # Create a host/example.stanford.edu ACL that uses the netdb ACL type.
+ is(
+ $server->acl_create('host/example.stanford.edu'),
+ 1,
+ 'Created netdb ACL'
+ );
+ is($server->error, undef, ' with no error');
+ is(
+ $server->acl_add('host/example.stanford.edu', 'netdb',
+ 'example.stanford.edu'),
+ 1,
+ '...with netdb ACL line'
+ );
+ is($server->error, undef, ' with no error');
+ is(
+ $server->acl_add('host/example.stanford.edu', 'krb5',
+ 'host/example.stanford.edu@stanford.edu'),
+ 1,
+ '...and krb5 ACL line'
+ );
+ is($server->error, undef, ' with no error');
-# Now we can test default ACLs. First, without a root instance.
-local $ENV{REMOTE_USER} = $ADMIN;
-is_deeply(
- [default_owner('keytab', 'host/bar.stanford.edu')],
- [
- 'host/bar.stanford.edu',
- ['netdb', 'bar.stanford.edu'],
- ['krb5', 'host/bar.stanford.edu@stanford.edu']
- ],
- 'Correct default owner for host-based keytab'
-);
-is_deeply(
- [default_owner('keytab', 'HTTP/example.stanford.edu')],
- [
- 'host/example.stanford.edu',
- ['netdb', 'example.stanford.edu'],
- ['krb5', 'host/example.stanford.edu@stanford.edu']
- ],
- '...and when netdb ACL already exists'
-);
-is_deeply(
- [default_owner('keytab', 'webauth/foo.stanford.edu')],
- [
- 'host/foo.stanford.edu',
- ['netdb-root', 'foo.stanford.edu'],
- ['krb5', 'host/foo.stanford.edu@stanford.edu']
- ],
- '...and when netdb-root ACL already exists'
-);
+ # Likewise for host/foo.example.edu with the netdb-root ACL type.
+ is(
+ $server->acl_create('host/foo.stanford.edu'),
+ 1,
+ 'Created netdb-root ACL'
+ );
+ is(
+ $server->acl_add('host/foo.stanford.edu', 'netdb-root',
+ 'foo.stanford.edu'),
+ 1,
+ '...with netdb-root ACL line'
+ );
+ is(
+ $server->acl_add('host/foo.stanford.edu', 'krb5',
+ 'host/foo.stanford.edu@stanford.edu'),
+ 1,
+ '...and krb5 ACL line'
+ );
-# Now with a root instance.
-local $ENV{REMOTE_USER} = 'admin/root@stanford.edu';
-is_deeply(
- [default_owner('keytab', 'host/bar.stanford.edu')],
- [
- 'host/bar.stanford.edu',
- ['netdb-root', 'bar.stanford.edu'],
- ['krb5', 'host/bar.stanford.edu@stanford.edu']
- ],
- 'Correct default owner for host-based keytab for /root'
-);
-is_deeply(
- [default_owner('keytab', 'HTTP/example.stanford.edu')],
- [
- 'host/example.stanford.edu',
- ['netdb-root', 'example.stanford.edu'],
- ['krb5', 'host/example.stanford.edu@stanford.edu']
- ],
- '...and when netdb ACL already exists'
-);
-is_deeply(
- [default_owner('keytab', 'webauth/foo.stanford.edu')],
- [
- 'host/foo.stanford.edu',
- ['netdb-root', 'foo.stanford.edu'],
- ['krb5', 'host/foo.stanford.edu@stanford.edu']
- ],
- '...and when netdb-root ACL already exists'
-);
+ # Create a group/its-idg ACL, which will be used for autocreation of file
+ # objects.
+ is($server->acl_create('group/its-idg'), 1, 'Created group/its-idg ACL');
+ is($server->acl_add('group/its-idg', 'krb5', $ADMIN), 1, '...with member');
-# Check for a type that isn't host-based.
-is(default_owner('keytab', 'service/foo'), undef,
- 'No default owner for service/foo');
+ # Now we can test default ACLs. First, without a root instance.
+ local $ENV{REMOTE_USER} = $ADMIN;
+ is_deeply(
+ [default_owner('keytab', 'host/bar.stanford.edu')],
+ [
+ 'host/bar.stanford.edu',
+ ['netdb', 'bar.stanford.edu'],
+ ['krb5', 'host/bar.stanford.edu@stanford.edu']
+ ],
+ 'Correct default owner for host-based keytab'
+ );
+ is_deeply(
+ [default_owner('keytab', 'HTTP/example.stanford.edu')],
+ [
+ 'host/example.stanford.edu',
+ ['netdb', 'example.stanford.edu'],
+ ['krb5', 'host/example.stanford.edu@stanford.edu']
+ ],
+ '...and when netdb ACL already exists'
+ );
+ is_deeply(
+ [default_owner('keytab', 'webauth/foo.stanford.edu')],
+ [
+ 'host/foo.stanford.edu',
+ ['netdb-root', 'foo.stanford.edu'],
+ ['krb5', 'host/foo.stanford.edu@stanford.edu']
+ ],
+ '...and when netdb-root ACL already exists'
+ );
-# Check for an unknown object type.
-is(default_owner('unknown', 'foo'), undef,
- 'No default owner for unknown type');
+ # Now with a root instance.
+ local $ENV{REMOTE_USER} = 'admin/root@stanford.edu';
+ is_deeply(
+ [default_owner('keytab', 'host/bar.stanford.edu')],
+ [
+ 'host/bar.stanford.edu',
+ ['netdb-root', 'bar.stanford.edu'],
+ ['krb5', 'host/bar.stanford.edu@stanford.edu']
+ ],
+ 'Correct default owner for host-based keytab for /root'
+ );
+ is_deeply(
+ [default_owner('keytab', 'HTTP/example.stanford.edu')],
+ [
+ 'host/example.stanford.edu',
+ ['netdb-root', 'example.stanford.edu'],
+ ['krb5', 'host/example.stanford.edu@stanford.edu']
+ ],
+ '...and when netdb ACL already exists'
+ );
+ is_deeply(
+ [default_owner('keytab', 'webauth/foo.stanford.edu')],
+ [
+ 'host/foo.stanford.edu',
+ ['netdb-root', 'foo.stanford.edu'],
+ ['krb5', 'host/foo.stanford.edu@stanford.edu']
+ ],
+ '...and when netdb-root ACL already exists'
+ );
-# Check for autocreation mappings for host-based file objects.
-is_deeply(
- [default_owner('file', 'ssl-key/example.stanford.edu')],
- [
- 'host/example.stanford.edu',
- ['netdb-root', 'example.stanford.edu'],
- ['krb5', 'host/example.stanford.edu@stanford.edu']
- ],
- 'Default owner for file ssl-key/example.stanford.edu',
-);
-is_deeply(
- [default_owner('file', 'ssl-key/example.stanford.edu/mysql')],
- [
- 'host/example.stanford.edu',
- ['netdb-root', 'example.stanford.edu'],
- ['krb5', 'host/example.stanford.edu@stanford.edu']
- ],
- 'Default owner for file ssl-key/example.stanford.edu/mysql',
-);
+ # Check for a type that isn't host-based.
+ is(
+ default_owner('keytab', 'service/foo'),
+ undef,
+ 'No default owner for service/foo'
+ );
-# Check for a file object that isn't host-based.
-is_deeply(
- [default_owner('file', 'config/its-idg/example/foo')],
- ['group/its-idg', ['krb5', $ADMIN]],
- 'Default owner for file config/its-idg/example/foo',
-);
+ # Check for an unknown object type.
+ is(
+ default_owner('unknown', 'foo'),
+ undef,
+ 'No default owner for unknown type'
+ );
-# Check for legacy autocreation mappings for file objects.
-for my $type (qw(htpasswd ssh-rsa ssh-dsa ssl-key tivoli-key)) {
- my $name = "idg-example-$type";
+ # Check for autocreation mappings for host-based file objects.
is_deeply(
- [default_owner('file', $name)],
+ [default_owner('file', 'ssl-key/example.stanford.edu')],
[
'host/example.stanford.edu',
['netdb-root', 'example.stanford.edu'],
['krb5', 'host/example.stanford.edu@stanford.edu']
],
- "Default owner for file $name",
+ 'Default owner for file ssl-key/example.stanford.edu',
);
+ is_deeply(
+ [default_owner('file', 'ssl-key/example.stanford.edu/mysql')],
+ [
+ 'host/example.stanford.edu',
+ ['netdb-root', 'example.stanford.edu'],
+ ['krb5', 'host/example.stanford.edu@stanford.edu']
+ ],
+ 'Default owner for file ssl-key/example.stanford.edu/mysql',
+ );
+
+ # Check for a file object that isn't host-based.
+ is_deeply(
+ [default_owner('file', 'config/its-idg/example/foo')],
+ ['group/its-idg', ['krb5', $ADMIN]],
+ 'Default owner for file config/its-idg/example/foo',
+ );
+
+ # Check for legacy autocreation mappings for file objects.
+ for my $type (qw(htpasswd ssh-rsa ssh-dsa ssl-key tivoli-key)) {
+ my $name = "idg-example-$type";
+ is_deeply(
+ [default_owner('file', $name)],
+ [
+ 'host/example.stanford.edu',
+ ['netdb-root', 'example.stanford.edu'],
+ ['krb5', 'host/example.stanford.edu@stanford.edu']
+ ],
+ "Default owner for file $name",
+ );
+ }
+
+ # Clean up.
+ $setup->destroy;
}
-# Clean up.
-$setup->destroy;
END {
unlink 'wallet-db';
}
diff --git a/perl/t/verifier/external.t b/perl/t/verifier/external.t
new file mode 100755
index 0000000..d1438de
--- /dev/null
+++ b/perl/t/verifier/external.t
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+#
+# Tests for the external wallet ACL verifier.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2016 Russ Allbery <eagle@eyrie.org>
+#
+# See LICENSE for licensing terms.
+
+use strict;
+use warnings;
+
+use Test::More tests => 9;
+
+use Wallet::ACL::External;
+use Wallet::Config;
+
+# Configure the external ACL verifier.
+$Wallet::Config::EXTERNAL_COMMAND = 't/data/acl-command';
+
+# Check a few verifications.
+my $verifier = Wallet::ACL::External->new;
+ok (defined $verifier, 'Wallet::ACL::External creation');
+ok ($verifier->isa ('Wallet::ACL::External'), ' and class verification');
+is ($verifier->check ('eagle@eyrie.org', 'test success', 'file', 'test'),
+ 1, 'Success');
+is ($verifier->check ('eagle@eyrie.org', 'test failure', 'file', 'test'),
+ 0, 'Failure');
+is ($verifier->error, undef, 'No error set');
+is ($verifier->check ('eagle@eyrie.org', 'test error', 'file', 'test'),
+ undef, 'Error');
+is ($verifier->error, 'some error', ' and right error');
+is ($verifier->check (undef, 'eagle@eyrie.org', 'file', 'test'), undef,
+ 'Undefined principal');
+is ($verifier->error, 'no principal specified', ' and right error');
diff --git a/perl/t/verifier/ldap-attr.t b/perl/t/verifier/ldap-attr.t
index 3c132e2..cff3b63 100755
--- a/perl/t/verifier/ldap-attr.t
+++ b/perl/t/verifier/ldap-attr.t
@@ -24,16 +24,18 @@ plan skip_all => 'LDAP verifier tests only run for maintainer'
unless $ENV{RRA_MAINTAINER_TESTS};
# Declare a plan.
-plan tests => 10;
+plan tests => 22;
require_ok ('Wallet::ACL::LDAP::Attribute');
+require_ok ('Wallet::ACL::LDAP::Attribute::Root');
-my $host = 'ldap.stanford.edu';
-my $base = 'cn=people,dc=stanford,dc=edu';
-my $filter = 'uid';
-my $user = 'rra@stanford.edu';
-my $attr = 'suPrivilegeGroup';
-my $value = 'stanford:stanford';
+my $host = 'ldap.stanford.edu';
+my $base = 'cn=people,dc=stanford,dc=edu';
+my $filter = 'uid';
+my $user = 'jonrober@stanford.edu';
+my $rootuser = 'jonrober/root@stanford.edu';
+my $attr = 'suPrivilegeGroup';
+my $value = 'stanford:stanford';
# Remove the realm from principal names.
package Wallet::Config;
@@ -68,7 +70,28 @@ SKIP: {
is ($verifier->check ($user, "BOGUS=$value"), undef,
"Checking BOGUS=$value fails with error");
is ($verifier->error,
- 'cannot check LDAP attribute BOGUS for rra: Undefined attribute type',
+ 'cannot check LDAP attribute BOGUS for jonrober: Undefined attribute type',
+ '...with correct error');
+ is ($verifier->check ('user-does-not-exist', "$attr=$value"), 0,
+ "Checking for nonexistent user fails");
+ is ($verifier->error, undef, '...with no error');
+
+ # Then also test the root version.
+ $verifier = eval { Wallet::ACL::LDAP::Attribute::Root->new };
+ isa_ok ($verifier, 'Wallet::ACL::LDAP::Attribute::Root');
+ is ($verifier->check ($user, "$attr=$value"), 0,
+ "Checking as a non /root user fails");
+ is ($verifier->error, undef, '...with no error');
+ is ($verifier->check ($rootuser, "$attr=$value"), 1,
+ "Checking $attr=$value succeeds");
+ is ($verifier->error, undef, '...with no error');
+ is ($verifier->check ($rootuser, "$attr=BOGUS"), 0,
+ "Checking $attr=BOGUS fails");
+ is ($verifier->error, undef, '...with no error');
+ is ($verifier->check ($rootuser, "BOGUS=$value"), undef,
+ "Checking BOGUS=$value fails with error");
+ is ($verifier->error,
+ 'cannot check LDAP attribute BOGUS for jonrober: Undefined attribute type',
'...with correct error');
is ($verifier->check ('user-does-not-exist', "$attr=$value"), 0,
"Checking for nonexistent user fails");
diff --git a/perl/t/verifier/nested.t b/perl/t/verifier/nested.t
new file mode 100755
index 0000000..ec7ce40
--- /dev/null
+++ b/perl/t/verifier/nested.t
@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+#
+# Tests for the wallet ACL nested verifier.
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2015
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+use strict;
+use warnings;
+
+use Test::More tests => 22;
+
+use Wallet::ACL::Base;
+use Wallet::ACL::Nested;
+use Wallet::Admin;
+use Wallet::Config;
+
+use lib 't/lib';
+use Util;
+
+# Some global defaults to use.
+my $admin = 'admin@EXAMPLE.COM';
+my $user1 = 'alice@EXAMPLE.COM';
+my $user2 = 'bob@EXAMPLE.COM';
+my $user3 = 'jack@EXAMPLE.COM';
+my $host = 'localhost';
+my @trace = ($admin, $host, time);
+
+# Use Wallet::Admin to set up the database.
+db_setup;
+my $setup = eval { Wallet::Admin->new };
+is ($@, '', 'Database connection succeeded');
+is ($setup->reinitialize ($setup), 1, 'Database initialization succeeded');
+my $schema = $setup->schema;
+
+# Create a few ACLs for later testing.
+my $acl = eval { Wallet::ACL->create ('test', $schema, @trace) };
+ok (defined ($acl), 'ACL creation');
+my $acl_nesting = eval { Wallet::ACL->create ('nesting', $schema, @trace) };
+ok (defined ($acl), ' and another');
+my $acl_deep = eval { Wallet::ACL->create ('deepnesting', $schema, @trace) };
+ok (defined ($acl), ' and another');
+
+# Create an verifier to make sure that works
+my $verifier = Wallet::ACL::Nested->new ('test', $schema);
+ok (defined $verifier, 'Wallet::ACL::Nested creation');
+ok ($verifier->isa ('Wallet::ACL::Nested'), ' and class verification');
+is ($verifier->syntax_check ('notcreated'), 0,
+ ' and it rejects a nested name that is not already an ACL');
+is ($verifier->syntax_check ('test'), 1,
+ ' and accepts one that already exists');
+
+# Add a few entries to one ACL and then see if they validate.
+ok ($acl->add ('krb5', $user1, @trace), 'Added test scheme');
+ok ($acl->add ('krb5', $user2, @trace), ' and another');
+ok ($acl_nesting->add ('nested', 'test', @trace), ' and then nested it');
+ok ($acl_nesting->add ('krb5', $user3, @trace),
+ ' and added a non-nesting user');
+is ($acl_nesting->check ($user1), 1, ' so check of nested succeeds');
+is ($acl_nesting->check ($user3), 1, ' so check of non-nested succeeds');
+is (scalar ($acl_nesting->list), 2,
+ ' and the acl has the right number of items');
+
+# Add a recursive nesting to make sure it doesn't send us into loop.
+ok ($acl_deep->add ('nested', 'test', @trace),
+ 'Adding deep nesting for one nest succeeds');
+ok ($acl_deep->add ('nested', 'nesting', @trace), ' and another');
+ok ($acl_deep->add ('krb5', $user3, @trace),
+ ' and added a non-nesting user');
+is ($acl_deep->check ($user1), 1, ' so check of nested succeeds');
+is ($acl_deep->check ($user3), 1, ' so check of non-nested succeeds');
+
+# Test getting an error in adding an invalid group to an ACL object itself.
+isnt ($acl->add ('nested', 'doesnotexist', @trace), 1,
+ 'Adding bad nested acl fails');
+
+# Clean up.
+$setup->destroy;
+END {
+ unlink 'wallet-db';
+}
diff --git a/portable/asprintf.c b/portable/asprintf.c
index eb2b713..9693842 100644
--- a/portable/asprintf.c
+++ b/portable/asprintf.c
@@ -19,6 +19,7 @@
*/
#include <config.h>
+#include <portable/macros.h>
#include <portable/system.h>
#include <errno.h>
@@ -28,11 +29,14 @@
* with the system versions.
*/
#if TESTING
+# undef asprintf
+# undef vasprintf
# define asprintf test_asprintf
# define vasprintf test_vasprintf
int test_asprintf(char **, const char *, ...)
__attribute__((__format__(printf, 2, 3)));
-int test_vasprintf(char **, const char *, va_list);
+int test_vasprintf(char **, const char *, va_list)
+ __attribute__((__format__(printf, 2, 0)));
#endif
diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c
index b1c8b8d..c8309a4 100644
--- a/portable/krb5-extra.c
+++ b/portable/krb5-extra.c
@@ -22,6 +22,7 @@
#include <config.h>
#include <portable/krb5.h>
+#include <portable/macros.h>
#include <portable/system.h>
#include <errno.h>
@@ -33,6 +34,8 @@
# include <ibm_svc/krb5_svc.h>
# elif defined(HAVE_ET_COM_ERR_H)
# include <et/com_err.h>
+# elif defined(HAVE_KERBEROSV5_COM_ERR_H)
+# include <kerberosv5/com_err.h>
# else
# include <com_err.h>
# endif
diff --git a/portable/krb5.h b/portable/krb5.h
index 6dfffd5..34f960e 100644
--- a/portable/krb5.h
+++ b/portable/krb5.h
@@ -42,8 +42,10 @@
#endif
#include <portable/macros.h>
-#ifdef HAVE_KRB5_H
+#if defined(HAVE_KRB5_H)
# include <krb5.h>
+#elif defined(HAVE_KERBEROSV5_KRB5_H)
+# include <kerberosv5/krb5.h>
#else
# include <krb5/krb5.h>
#endif
diff --git a/portable/macros.h b/portable/macros.h
index b5093f5..d4cc2cc 100644
--- a/portable/macros.h
+++ b/portable/macros.h
@@ -37,7 +37,8 @@
* variadic macro support.
*/
#if !defined(__attribute__) && !defined(__alloc_size__)
-# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)) \
+ && !defined(__clang__)
# define __alloc_size__(spec, args...) /* empty */
# endif
#endif
diff --git a/portable/mkstemp.c b/portable/mkstemp.c
index 2cbfe08..7c733a4 100644
--- a/portable/mkstemp.c
+++ b/portable/mkstemp.c
@@ -23,7 +23,10 @@
#include <errno.h>
#include <fcntl.h>
-#include <sys/time.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <time.h>
/*
* If we're running the test suite, rename mkstemp to avoid conflicts with the
diff --git a/portable/reallocarray.c b/portable/reallocarray.c
index 7d40a7a..e9404e9 100644
--- a/portable/reallocarray.c
+++ b/portable/reallocarray.c
@@ -1,10 +1,10 @@
/*
* Replacement for a missing reallocarray.
*
- * Provides the same functionality as the OpenBSD library function reallocrray
- * for those systems that don't have it. This function is the same as
- * realloc, but takes the size arguments in the same form as calloc and checks
- * for overflow so that the caller doesn't need to.
+ * Provides the same functionality as the OpenBSD library function
+ * reallocarray for those systems that don't have it. This function is the
+ * same as realloc, but takes the size arguments in the same form as calloc
+ * and checks for overflow so that the caller doesn't need to.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
diff --git a/portable/setenv.c b/portable/setenv.c
index 472282a..f1f6db4 100644
--- a/portable/setenv.c
+++ b/portable/setenv.c
@@ -26,6 +26,7 @@
* the system version.
*/
#if TESTING
+# undef setenv
# define setenv test_setenv
int test_setenv(const char *, const char *, int);
#endif
@@ -34,29 +35,22 @@ int
setenv(const char *name, const char *value, int overwrite)
{
char *envstring;
- size_t size;
+ /* Do nothing if not overwriting and the variable is already set. */
if (!overwrite && getenv(name) != NULL)
return 0;
/*
- * Allocate memory for the environment string. We intentionally don't use
- * the xmalloc family of allocation routines here, since the intention is
- * to provide a replacement for the standard library function that sets
- * errno and returns in the event of a memory allocation failure.
- */
- size = strlen(name) + 1 + strlen(value) + 1;
- envstring = malloc(size);
- if (envstring == NULL)
- return -1;
-
- /*
* Build the environment string and add it to the environment using
* putenv. Systems without putenv lose, but XPG4 requires it.
+ *
+ * We intentionally don't use the xmalloc family of allocation routines
+ * here, since the intention is to provide a replacement for the standard
+ * library function that sets errno and returns in the event of a memory
+ * allocation failure.
*/
- strlcpy(envstring, name, size);
- strlcat(envstring, "=", size);
- strlcat(envstring, value, size);
+ if (asprintf(&envstring, "%s=%s", name, value) < 0)
+ return -1;
return putenv(envstring);
/*
diff --git a/portable/snprintf.c b/portable/snprintf.c
index c35ad80..9818acd 100644
--- a/portable/snprintf.c
+++ b/portable/snprintf.c
@@ -19,6 +19,8 @@
* conflicts with the system version.
*/
#if TESTING
+# undef snprintf
+# undef vsnprintf
# define snprintf test_snprintf
# define vsnprintf test_vsnprintf
#endif
diff --git a/portable/strlcat.c b/portable/strlcat.c
deleted file mode 100644
index 9c8686d..0000000
--- a/portable/strlcat.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Replacement for a missing strlcat.
- *
- * Provides the same functionality as the *BSD function strlcat, originally
- * developed by Todd Miller and Theo de Raadt. strlcat works similarly to
- * strncat, except simpler. The result is always nul-terminated even if the
- * source string is longer than the space remaining in the destination string,
- * and the total space required is returned. The third argument is the total
- * space available in the destination buffer, not just the amount of space
- * remaining.
- *
- * The canonical version of this file is maintained in the rra-c-util package,
- * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
- *
- * Written by Russ Allbery <eagle@eyrie.org>
- *
- * The authors hereby relinquish any claim to any copyright that they may have
- * in this work, whether granted under contract or by operation of law or
- * international treaty, and hereby commit to the public, at large, that they
- * shall not, at any time in the future, seek to enforce any copyright in this
- * work against any person or entity, or prevent any person or entity from
- * copying, publishing, distributing or creating derivative works of this
- * work.
- */
-
-#include <config.h>
-#include <portable/system.h>
-
-/*
- * If we're running the test suite, rename strlcat to avoid conflicts with
- * the system version.
- */
-#if TESTING
-# define strlcat test_strlcat
-size_t test_strlcat(char *, const char *, size_t);
-#endif
-
-size_t
-strlcat(char *dst, const char *src, size_t size)
-{
- size_t used, length, copy;
-
- used = strlen(dst);
- length = strlen(src);
- if (size > 0 && used < size - 1) {
- copy = (length >= size - used) ? size - used - 1 : length;
- memcpy(dst + used, src, copy);
- dst[used + copy] = '\0';
- }
- return used + length;
-}
diff --git a/portable/strlcpy.c b/portable/strlcpy.c
deleted file mode 100644
index 592e3ee..0000000
--- a/portable/strlcpy.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Replacement for a missing strlcpy.
- *
- * Provides the same functionality as the *BSD function strlcpy, originally
- * developed by Todd Miller and Theo de Raadt. strlcpy works similarly to
- * strncpy, except saner and simpler. The result is always nul-terminated
- * even if the source string is longer than the destination string, and the
- * total space required is returned. The destination string is not nul-filled
- * like strncpy does, just nul-terminated.
- *
- * The canonical version of this file is maintained in the rra-c-util package,
- * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
- *
- * Written by Russ Allbery <eagle@eyrie.org>
- *
- * The authors hereby relinquish any claim to any copyright that they may have
- * in this work, whether granted under contract or by operation of law or
- * international treaty, and hereby commit to the public, at large, that they
- * shall not, at any time in the future, seek to enforce any copyright in this
- * work against any person or entity, or prevent any person or entity from
- * copying, publishing, distributing or creating derivative works of this
- * work.
- */
-
-#include <config.h>
-#include <portable/system.h>
-
-/*
- * If we're running the test suite, rename strlcpy to avoid conflicts with
- * the system version.
- */
-#if TESTING
-# define strlcpy test_strlcpy
-size_t test_strlcpy(char *, const char *, size_t);
-#endif
-
-size_t
-strlcpy(char *dst, const char *src, size_t size)
-{
- size_t length, copy;
-
- length = strlen(src);
- if (size > 0) {
- copy = (length >= size) ? size - 1 : length;
- memcpy(dst, src, copy);
- dst[copy] = '\0';
- }
- return length;
-}
diff --git a/portable/system.h b/portable/system.h
index 544b2de..581e46c 100644
--- a/portable/system.h
+++ b/portable/system.h
@@ -136,10 +136,10 @@ extern void *reallocarray(void *, size_t, size_t);
#if !HAVE_SETENV
extern int setenv(const char *, const char *, int);
#endif
-#if !HAVE_STRLCAT
+#if !HAVE_DECL_STRLCAT
extern size_t strlcat(char *, const char *, size_t);
#endif
-#if !HAVE_STRLCPY
+#if !HAVE_DECL_STRLCPY
extern size_t strlcpy(char *, const char *, size_t);
#endif
diff --git a/server/keytab-backend b/server/keytab-backend
index bd5a3f9..6e47331 100755
--- a/server/keytab-backend
+++ b/server/keytab-backend
@@ -16,10 +16,7 @@
#
# The keytab for the extracted principal will be printed to standard output.
-##############################################################################
-# Declarations and site configuration
-##############################################################################
-
+use 5.008;
use strict;
use warnings;
diff --git a/server/keytab-backend.8 b/server/keytab-backend.8
index b143e46..aaeabab 100644
--- a/server/keytab-backend.8
+++ b/server/keytab-backend.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "KEYTAB-BACKEND 8"
-.TH KEYTAB-BACKEND 8 "2014-12-08" "1.2" "wallet"
+.TH KEYTAB-BACKEND 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
diff --git a/server/wallet-admin b/server/wallet-admin
index 7ba1021..e74b2f1 100755
--- a/server/wallet-admin
+++ b/server/wallet-admin
@@ -1,12 +1,11 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
#
# Wallet server administrative commands.
-##############################################################################
-# Declarations and site configuration
-##############################################################################
-
+use 5.008;
use strict;
+use warnings;
+
use Wallet::Admin;
##############################################################################
diff --git a/server/wallet-admin.8 b/server/wallet-admin.8
index cc35d0e..1b0b3bc 100644
--- a/server/wallet-admin.8
+++ b/server/wallet-admin.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-ADMIN 8"
-.TH WALLET-ADMIN 8 "2014-12-08" "1.2" "wallet"
+.TH WALLET-ADMIN 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
diff --git a/server/wallet-backend b/server/wallet-backend
index 8dfc952..aa83a96 100755
--- a/server/wallet-backend
+++ b/server/wallet-backend
@@ -2,10 +2,7 @@
#
# Wallet server for storing and retrieving secure data.
-##############################################################################
-# Declarations and site configuration
-##############################################################################
-
+use 5.008;
use strict;
use warnings;
@@ -173,6 +170,9 @@ sub command {
} elsif ($action eq 'rename') {
check_args (2, 2, [], @args);
$server->acl_rename (@args) or failure ($server->error, @_);
+ } elsif ($action eq 'replace') {
+ check_args (2, 2, [], @args);
+ $server->acl_replace (@args) or failure ($server->error, @_);
} elsif ($action eq 'show') {
check_args (1, 1, [], @args);
my $output = $server->acl_show (@args);
@@ -312,6 +312,14 @@ sub command {
}
splice (@_, 3);
$server->store (@args) or failure ($server->error, @_);
+ } elsif ($command eq 'update') {
+ check_args (2, 2, [], @args);
+ my $output = $server->update (@args);
+ if (defined $output) {
+ print $output;
+ } else {
+ failure ($server->error, @_);
+ }
} else {
error "unknown command $command";
}
@@ -449,6 +457,25 @@ accidental lockout, but administrators can remove themselves from the
C<ADMIN> ACL and can leave only a non-functioning entry on the ACL. Use
caution when removing entries from the C<ADMIN> ACL.
+=item acl rename <id> <name>
+
+Renames the ACL identified by <id> to <name>. This changes the
+human-readable name, not the underlying numeric ID, so the ACL's
+associations with objects will be unchanged. The C<ADMIN> ACL may not be
+renamed. <id> may be either the current name or the numeric ID. <name>
+must not be all-numeric. To rename an ACL, the current user must be
+authorized by the C<ADMIN> ACL.
+
+=item acl replace <id> <new-id>
+
+Find any objects owned by <id>, and then change their ownership to
+<new_id> instead. <new-id> should already exist, and may already have
+some objects owned by it. <id> is not deleted afterwards, though in
+most cases that is probably your next step. The C<ADMIN> ACL may not be
+replaced from. <id> and <new-id> may be either the current name or the
+numeric ID. To replace an ACL, the current user must be authorized by
+the C<ADMIN> ACL.
+
=item acl show <id>
Display the name, numeric ID, and entries of the ACL <id>.
@@ -589,6 +616,14 @@ Stores <data> for the object identified by <type> and <name> for later
retrieval with C<get>. Not all object types support this. If <data> is
not given as an argument, it will be read from standard input.
+=item update <type> <name>
+
+Prints to standard output the data associated with the object identified
+by <type> and <name>. If the object is one that can have changing
+information, such as a keytab or password, then we generate new data for
+that object regardless of whether there is current data or the unchanging
+flag is set.
+
=back
=head1 ATTRIBUTES
diff --git a/server/wallet-backend.8 b/server/wallet-backend.8
index f1544ac..96b5b29 100644
--- a/server/wallet-backend.8
+++ b/server/wallet-backend.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-BACKEND 8"
-.TH WALLET-BACKEND 8 "2014-12-08" "1.2" "wallet"
+.TH WALLET-BACKEND 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -228,6 +228,23 @@ entry in the special \s-1ACL \s0\f(CW\*(C`ADMIN\*(C'\fR cannot be removed to pro
accidental lockout, but administrators can remove themselves from the
\&\f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0 and can leave only a non-functioning entry on the \s-1ACL. \s0 Use
caution when removing entries from the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL.\s0
+.IP "acl rename <id> <name>" 4
+.IX Item "acl rename <id> <name>"
+Renames the \s-1ACL\s0 identified by <id> to <name>. This changes the
+human-readable name, not the underlying numeric \s-1ID,\s0 so the \s-1ACL\s0's
+associations with objects will be unchanged. The \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0 may not be
+renamed. <id> may be either the current name or the numeric \s-1ID. \s0 <name>
+must not be all-numeric. To rename an \s-1ACL,\s0 the current user must be
+authorized by the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL.\s0
+.IP "acl replace <id> <new\-id>" 4
+.IX Item "acl replace <id> <new-id>"
+Find any objects owned by <id>, and then change their ownership to
+<new_id> instead. <new\-id> should already exist, and may already have
+some objects owned by it. <id> is not deleted afterwards, though in
+most cases that is probably your next step. The \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL\s0 may not be
+replaced from. <id> and <new\-id> may be either the current name or the
+numeric \s-1ID. \s0 To replace an \s-1ACL,\s0 the current user must be authorized by
+the \f(CW\*(C`ADMIN\*(C'\fR \s-1ACL.\s0
.IP "acl show <id>" 4
.IX Item "acl show <id>"
Display the name, numeric \s-1ID,\s0 and entries of the \s-1ACL\s0 <id>.
@@ -349,6 +366,13 @@ stored, and last downloaded.
Stores <data> for the object identified by <type> and <name> for later
retrieval with \f(CW\*(C`get\*(C'\fR. Not all object types support this. If <data> is
not given as an argument, it will be read from standard input.
+.IP "update <type> <name>" 4
+.IX Item "update <type> <name>"
+Prints to standard output the data associated with the object identified
+by <type> and <name>. If the object is one that can have changing
+information, such as a keytab or password, then we generate new data for
+that object regardless of whether there is current data or the unchanging
+flag is set.
.SH "ATTRIBUTES"
.IX Header "ATTRIBUTES"
Object attributes store additional properties and configuration
diff --git a/server/wallet-report b/server/wallet-report
index b5a2247..6508227 100755
--- a/server/wallet-report
+++ b/server/wallet-report
@@ -1,12 +1,11 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
#
# Wallet server reporting interface.
-##############################################################################
-# Declarations and globals
-##############################################################################
-
+use 5.008;
use strict;
+use warnings;
+
use Wallet::Report;
# The help output, sent in reply to the help command. Lists each supported
@@ -17,16 +16,22 @@ Wallet reporting help:
acls duplicate ACLs that duplicate another
acls empty All empty ACLs
acls entry <scheme> <id> ACLs containing this entry (wildcarded)
+ acls nesting <acl> ACLs containing this ACL as a nested entry
acls unused ACLs that are not referenced by any object
audit acls name ACLs failing the naming policy
audit objects name Objects failing the naming policy
objects All objects
objects acl <acl> Objects granting permissions to that ACL
objects flag <flag> Objects with that flag set
+ objects history History of all objects
+ objects host <hostname> All host-based objects for a specific host
objects owner <owner> Objects owned by that owner
objects type <type> Objects of that type
- objects unused Objects that have never been stored/gotten
+ objects unused Objects that have never been gotten
+ objects unstored Objects that have never been stored
owners <type> <name> All ACL entries owning matching objects
+ schemes All configured ACL schemes
+ types All configured wallet types
EOH
##############################################################################
@@ -74,7 +79,14 @@ sub command {
print $HELP;
} elsif ($command eq 'objects') {
die "too many arguments to objects\n" if @args > 2;
- my @objects = $report->objects (@args);
+ my @objects;
+ if (@args && $args[0] eq 'history') {
+ @objects = $report->objects_history (@args);
+ } elsif (@args && $args[0] eq 'host') {
+ @objects = $report->objects_hostname (@args);
+ } else {
+ @objects = $report->objects (@args);
+ }
if (!@objects and $report->error) {
die $report->error, "\n";
}
@@ -91,6 +103,20 @@ sub command {
for my $entry (@entries) {
print join (' ', @$entry), "\n";
}
+ } elsif ($command eq 'schemes') {
+ die "too many arguments to schemes\n" if @args > 0;
+ my @schemes = $report->acl_schemes;
+ for my $entry (@schemes) {
+ print join (' ', @$entry), "\n";
+ }
+
+ } elsif ($command eq 'types') {
+ die "too many arguments to types\n" if @args > 0;
+ my @types = $report->types;
+ for my $entry (@types) {
+ print join (' ', @$entry), "\n";
+ }
+
} else {
die "unknown command $command\n";
}
@@ -108,7 +134,7 @@ wallet-report - Wallet server reporting interface
=for stopwords
metadata ACL hostname backend acl acls wildcard SQL Allbery remctl
-MERCHANTABILITY NONINFRINGEMENT sublicense
+MERCHANTABILITY NONINFRINGEMENT sublicense unstored
=head1 SYNOPSIS
@@ -180,6 +206,10 @@ Returns all ACLs containing an entry with given scheme and identifier.
The scheme must be an exact match, but the <identifier> string will match
any identifier containing that string.
+=item acls nested <acl>
+
+Returns all ACLs that contain this ACL as a nested entry.
+
=item acls unused
Returns all ACLs that are not referenced by any of the objects in the
@@ -220,6 +250,8 @@ Displays a summary of all available commands.
=item objects unused
+=item objects unstored
+
Returns a list of objects in the database. Objects will be listed in the
form:
@@ -245,6 +277,12 @@ those where that ACL has any other, more limited permissions.
Returns all objects which have the given flag set.
+=item objects host <hostname>
+
+Returns all objects that belong to the given host. This requires adding
+local configuration to identify objects that belong to a given host. See
+L<Wallet::Config/"OBJECT HOST-BASED NAMES"> for more information.
+
=item objects owner <acl>
Returns all objects owned by the given ACL name or ID.
@@ -273,6 +311,14 @@ The output will be one line per ACL line in the form:
with duplicates suppressed.
+=item schemes
+
+Returns a list of all registered ACL schemes.
+
+=item types
+
+Returns a list of all registered object types.
+
=back
=head1 AUTHOR
@@ -281,7 +327,9 @@ Russ Allbery <eagle@eyrie.org>
=head1 COPYRIGHT AND LICENSE
-Copyright 2008, 2009, 2010, 2013 The Board of Trustees of the Leland
+Copyright 2016 Russ Allbery <eagle@eyrie.org>
+
+Copyright 2008, 2009, 2010, 2013, 2015 The Board of Trustees of the Leland
Stanford Junior University
Permission is hereby granted, free of charge, to any person obtaining a
diff --git a/server/wallet-report.8 b/server/wallet-report.8
index ac3714f..4cb759d 100644
--- a/server/wallet-report.8
+++ b/server/wallet-report.8
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "WALLET-REPORT 8"
-.TH WALLET-REPORT 8 "2014-12-08" "1.2" "wallet"
+.TH WALLET-REPORT 8 "2016-01-18" "1.3" "wallet"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -204,6 +204,9 @@ can be destroyed.
Returns all ACLs containing an entry with given scheme and identifier.
The scheme must be an exact match, but the <identifier> string will match
any identifier containing that string.
+.IP "acls nested <acl>" 4
+.IX Item "acls nested <acl>"
+Returns all ACLs that contain this \s-1ACL\s0 as a nested entry.
.IP "acls unused" 4
.IX Item "acls unused"
Returns all ACLs that are not referenced by any of the objects in the
@@ -249,6 +252,8 @@ Displays a summary of all available commands.
.IX Item "objects type <type>"
.IP "objects unused" 4
.IX Item "objects unused"
+.IP "objects unstored" 4
+.IX Item "objects unstored"
.PD
Returns a list of objects in the database. Objects will be listed in the
form:
@@ -273,6 +278,11 @@ those where that \s-1ACL\s0 has any other, more limited permissions.
.IP "objects flag <flag>" 4
.IX Item "objects flag <flag>"
Returns all objects which have the given flag set.
+.IP "objects host <hostname>" 4
+.IX Item "objects host <hostname>"
+Returns all objects that belong to the given host. This requires adding
+local configuration to identify objects that belong to a given host. See
+\&\*(L"\s-1OBJECT\s0 HOST-BASED \s-1NAMES\*(R"\s0 in Wallet::Config for more information.
.IP "objects owner <acl>" 4
.IX Item "objects owner <acl>"
Returns all objects owned by the given \s-1ACL\s0 name or \s-1ID.\s0
@@ -300,12 +310,20 @@ The output will be one line per \s-1ACL\s0 line in the form:
.Ve
.Sp
with duplicates suppressed.
+.IP "schemes" 4
+.IX Item "schemes"
+Returns a list of all registered \s-1ACL\s0 schemes.
+.IP "types" 4
+.IX Item "types"
+Returns a list of all registered object types.
.SH "AUTHOR"
.IX Header "AUTHOR"
Russ Allbery <eagle@eyrie.org>
.SH "COPYRIGHT AND LICENSE"
.IX Header "COPYRIGHT AND LICENSE"
-Copyright 2008, 2009, 2010, 2013 The Board of Trustees of the Leland
+Copyright 2016 Russ Allbery <eagle@eyrie.org>
+.PP
+Copyright 2008, 2009, 2010, 2013, 2015 The Board of Trustees of the Leland
Stanford Junior University
.PP
Permission is hereby granted, free of charge, to any person obtaining a
diff --git a/tests/TESTS b/tests/TESTS
index d947e97..76bd4ae 100644
--- a/tests/TESTS
+++ b/tests/TESTS
@@ -5,13 +5,12 @@ client/rekey
docs/pod
docs/pod-spelling
perl/minimum-version
+perl/module-version
perl/strict
portable/asprintf
portable/mkstemp
portable/setenv
portable/snprintf
-portable/strlcat
-portable/strlcpy
server/admin
server/backend
server/keytab
diff --git a/tests/data/perl.conf b/tests/data/perl.conf
index eaf7443..0c1e34e 100644
--- a/tests/data/perl.conf
+++ b/tests/data/perl.conf
@@ -1,6 +1,9 @@
# Configuration for Perl tests. -*- perl -*-
-# No special configuration yet.
+# Wallet::Schema's version number is used to version the database schema and
+# requires upgrade SQL files for each version bump. Until this is replaced
+# with some better system, exclude it from version checking.
+@MODULE_VERSION_IGNORE = qw(perl/lib/Wallet/Schema.pm);
# File must end with this line.
1;
diff --git a/tests/perl/module-version-t b/tests/perl/module-version-t
new file mode 100755
index 0000000..f1ebf0f
--- /dev/null
+++ b/tests/perl/module-version-t
@@ -0,0 +1,183 @@
+#!/usr/bin/perl
+#
+# Check or update the version of embedded Perl modules.
+#
+# Examines all module files (*.pm) under the perl/lib directory, if it exists,
+# and verifies that their $VERSION is set to the same value as the current
+# version number as determined by the NEWS file at the top level of the source
+# tree (or the current directory if not being run as a test).
+#
+# When given the --update option, instead fixes all of the Perl modules found
+# to have the correct version.
+
+use 5.006;
+use strict;
+use warnings;
+
+# SOURCE may not be set if we're running this script manually to update
+# version numbers. If it isn't, assume we're being run from the top of the
+# tree.
+BEGIN {
+ if ($ENV{SOURCE}) {
+ unshift(@INC, "$ENV{SOURCE}/tap/perl");
+ } else {
+ unshift(@INC, 'tests/tap/perl');
+ }
+}
+
+use Getopt::Long qw(GetOptions);
+use Test::RRA qw(skip_unless_automated);
+use Test::RRA::Automake qw(automake_setup);
+use Test::RRA::ModuleVersion qw(test_module_versions update_module_versions);
+
+# Return the current version and, optionally, the package name from the NEWS
+# file. Munges the version to be appropriate for Perl if necessary.
+#
+# Returns: Scalar: The version number of the latest version in NEWS
+# List: The version number and the package name
+# Throws: Text exception if NEWS is not found or doesn't contain a version
+sub news_version {
+ my ($package, $version, $news);
+ for my $path ('NEWS', '../NEWS') {
+ if (-f $path) {
+ open($news, q{<}, $path) or die "$0: cannot open $path: $!\n";
+ }
+ }
+ if (!$news) {
+ die "$0: cannot find NEWS file\n";
+ }
+ SCAN:
+ while (defined(my $line = <$news>)) {
+ ## no critic (RegularExpressions::ProhibitEscapedMetacharacters)
+ if ($line =~ m{ \A ([\w\s-]+) \s ([\d.]+) \s \( }xms) {
+ ($package, $version) = ($1, $2);
+ last SCAN;
+ }
+ ## use critic
+ }
+ close($news) or die "$0: error reading from NEWS: $!\n";
+ if (!defined($version)) {
+ die "$0: cannot find version number in NEWS\n";
+ }
+
+ # Munge the version for Perl purposes by ensuring that each component
+ # has two digits and by dropping the second period.
+ $version =~ s{ [.] (\d) (?= [.] | \z ) }{.0$1}xmsg;
+ $version =~ s{ ([.] \d+) [.] (\d+) }{$1$2}xms;
+
+ # Return the appropriate value based on context.
+ return wantarray ? ($version, $package) : $version;
+}
+
+# Parse command-line arguments.
+my $update;
+Getopt::Long::config('bundling', 'no_ignore_case');
+GetOptions('update|u' => \$update) or exit 1;
+
+# If we're not updating, set up for Automake testing. Otherwise, we assume
+# we're running from the top of the source tree.
+if (!$update) {
+ automake_setup();
+}
+
+# Get the package name and version.
+my ($version, $package) = news_version();
+
+# rra-c-util itself checks the versions of the testing support modules instead
+# of an embedded tree of Perl modules.
+my $root = ($package eq 'rra-c-util') ? 'tests/tap/perl' : 'perl/lib';
+
+# Main routine. We run as either a test suite or as a script to update all of
+# the module versions, selecting based on whether we got the -u / --update
+# command-line option.
+if ($update) {
+ update_module_versions($root, $version);
+} else {
+ skip_unless_automated('Module version tests');
+ test_module_versions($root, $version);
+}
+exit 0;
+__END__
+
+=for stopwords
+Allbery sublicense MERCHANTABILITY NONINFRINGEMENT rra-c-util
+
+=head1 NAME
+
+module-version-t - Check or update versions of embedded Perl modules
+
+=head1 SYNOPSIS
+
+B<module-version-t> [B<--update>]
+
+=head1 REQUIREMENTS
+
+Perl 5.6.2 or later.
+
+=head1 DESCRIPTION
+
+This script has a dual purpose as either a test script or a utility script.
+The intent is to assist with maintaining consistent versions between a larger
+primarily C project and any embedded Perl modules, supporting both the package
+keyword syntax introduced in Perl 5.12 or the older explicit setting of a
+$VERSION variable.
+
+As a test, it reads the current version of a package from the F<NEWS> file and
+then looks for any Perl modules in F<perl/lib>. (As a special exception, if
+the package name as determined from the F<NEWS> file is C<rra-c-util>, it
+looks for Perl modules in F<tests/tap/perl> instead.) If it finds any, it
+checks that the version number of the Perl module matches the version number
+of the package from the F<NEWS> file. These test results are reported with
+Test::More, suitable for any TAP harness.
+
+As a utility script, when run with the B<--update> option, it similarly finds
+all Perl modules in F<perl/lib> (or F<tests/tap/perl> per above) and then
+rewrites their version setting to match the version of the package as
+determined from the F<NEWS> file.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-u>, B<--update>
+
+Rather than test the Perl modules for the correct version, update all Perl
+modules found in the tree under F<perl/lib> to the current version from the
+NEWS file.
+
+=back
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2014, 2016 Russ Allbery <eagle@eyrie.org>
+
+Copyright 2013 The Board of Trustees of the Leland Stanford Junior University
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+=head1 SEE ALSO
+
+This module is maintained in the rra-c-util package. The current version is
+available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+
+=cut
diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c
index c61c14a..e556d95 100644
--- a/tests/portable/asprintf-t.c
+++ b/tests/portable/asprintf-t.c
@@ -16,6 +16,7 @@
*/
#include <config.h>
+#include <portable/macros.h>
#include <portable/system.h>
#include <tests/tap/basic.h>
diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c
index 270d2e1..cc8cf00 100644
--- a/tests/portable/snprintf-t.c
+++ b/tests/portable/snprintf-t.c
@@ -26,7 +26,9 @@
* Disable the requirement that format strings be literals. We need variable
* formats for easy testing.
*/
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2) || defined(__clang__)
+# pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
/*
* Intentionally don't add the printf attribute here since we pass a
diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c
deleted file mode 100644
index 58aba58..0000000
--- a/tests/portable/strlcat-t.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * strlcat test suite.
- *
- * The canonical version of this file is maintained in the rra-c-util package,
- * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
- *
- * Written by Russ Allbery <eagle@eyrie.org>
- *
- * The authors hereby relinquish any claim to any copyright that they may have
- * in this work, whether granted under contract or by operation of law or
- * international treaty, and hereby commit to the public, at large, that they
- * shall not, at any time in the future, seek to enforce any copyright in this
- * work against any person or entity, or prevent any person or entity from
- * copying, publishing, distributing or creating derivative works of this
- * work.
- */
-
-#include <config.h>
-#include <portable/system.h>
-
-#include <tests/tap/basic.h>
-
-size_t test_strlcat(char *, const char *, size_t);
-
-
-int
-main(void)
-{
- char buffer[10] = "";
-
- plan(27);
-
- is_int(3, test_strlcat(buffer, "foo", sizeof(buffer)),
- "strlcat into empty buffer");
- is_string("foo", buffer, "...with right output");
- is_int(7, test_strlcat(buffer, " bar", sizeof(buffer)),
- "...and append more");
- is_string("foo bar", buffer, "...and output is still correct");
- is_int(9, test_strlcat(buffer, "!!", sizeof(buffer)),
- "...and append to buffer limit");
- is_string("foo bar!!", buffer, "...output is still correct");
- is_int(10, test_strlcat(buffer, "!", sizeof(buffer)),
- "...append one more character");
- is_string("foo bar!!", buffer, "...and output didn't change");
- ok(buffer[9] == '\0', "...buffer still nul-terminated");
- buffer[0] = '\0';
- is_int(11, test_strlcat(buffer, "hello world", sizeof(buffer)),
- "append single long string");
- is_string("hello wor", buffer, "...string truncates properly");
- ok(buffer[9] == '\0', "...buffer still nul-terminated");
- buffer[0] = '\0';
- is_int(7, test_strlcat(buffer, "sausage", 5), "lie about buffer length");
- is_string("saus", buffer, "...contents are correct");
- is_int(14, test_strlcat(buffer, "bacon eggs", sizeof(buffer)),
- "...add more up to real size");
- is_string("sausbacon", buffer, "...and result is correct");
-
- /* Make sure that with a size of 0, the destination isn't changed. */
- is_int(11, test_strlcat(buffer, "!!", 0), "no change with size of 0");
- is_string("sausbacon", buffer, "...and content is the same");
-
- /* Now play with empty strings. */
- is_int(9, test_strlcat(buffer, "", 0),
- "correct count when appending empty string");
- is_string("sausbacon", buffer, "...and contents are unchanged");
- buffer[0] = '\0';
- is_int(0, test_strlcat(buffer, "", sizeof(buffer)),
- "correct count when appending empty string to empty buffer");
- is_string("", buffer, "...and buffer content is correct");
- is_int(3, test_strlcat(buffer, "foo", 2), "append to length 2 buffer");
- is_string("f", buffer, "...and got only a single character");
- ok(buffer[1] == '\0', "...and buffer is still nul-terminated");
- is_int(1, test_strlcat(buffer, "", sizeof(buffer)),
- "append an empty string");
- ok(buffer[1] == '\0', "...and buffer is still nul-terminated");
-
- return 0;
-}
diff --git a/tests/portable/strlcat.c b/tests/portable/strlcat.c
deleted file mode 100644
index 8983bd8..0000000
--- a/tests/portable/strlcat.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#define TESTING 1
-#include <portable/strlcat.c>
diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c
deleted file mode 100644
index 6652a7c..0000000
--- a/tests/portable/strlcpy-t.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * strlcpy test suite.
- *
- * The canonical version of this file is maintained in the rra-c-util package,
- * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
- *
- * Written by Russ Allbery <eagle@eyrie.org>
- *
- * The authors hereby relinquish any claim to any copyright that they may have
- * in this work, whether granted under contract or by operation of law or
- * international treaty, and hereby commit to the public, at large, that they
- * shall not, at any time in the future, seek to enforce any copyright in this
- * work against any person or entity, or prevent any person or entity from
- * copying, publishing, distributing or creating derivative works of this
- * work.
- */
-
-#include <config.h>
-#include <portable/system.h>
-
-#include <tests/tap/basic.h>
-
-size_t test_strlcpy(char *, const char *, size_t);
-
-
-int
-main(void)
-{
- char buffer[10];
-
- plan(23);
-
- is_int(3, test_strlcpy(buffer, "foo", sizeof(buffer)), "simple strlcpy");
- is_string("foo", buffer, "...result is correct");
- is_int(9, test_strlcpy(buffer, "hello wor", sizeof(buffer)),
- "strlcpy exact length of buffer");
- is_string("hello wor", buffer, "...result is correct");
- is_int(10, test_strlcpy(buffer, "world hell", sizeof(buffer)),
- "strlcpy one more than buffer length");
- is_string("world hel", buffer, "...result is correct");
- ok(buffer[9] == '\0', "...buffer is nul-terminated");
- is_int(11, test_strlcpy(buffer, "hello world", sizeof(buffer)),
- "strlcpy more than buffer length");
- is_string("hello wor", buffer, "...result is correct");
- ok(buffer[9] == '\0', "...buffer is nul-terminated");
-
- /* Make sure that with a size of 0, the destination isn't changed. */
- is_int(3, test_strlcpy(buffer, "foo", 0), "buffer unchanged if size 0");
- is_string("hello wor", buffer, "...contents still the same");
-
- /* Now play with empty strings. */
- is_int(0, test_strlcpy(buffer, "", 0), "copy empty string with size 0");
- is_string("hello wor", buffer, "...buffer unchanged");
- is_int(0, test_strlcpy(buffer, "", sizeof(buffer)),
- "copy empty string into full buffer");
- is_string("", buffer, "...buffer now empty string");
- is_int(3, test_strlcpy(buffer, "foo", 2),
- "copy string into buffer of size 2");
- is_string("f", buffer, "...got one character");
- ok(buffer[1] == '\0', "...buffer is nul-terminated");
- is_int(0, test_strlcpy(buffer, "", 1),
- "copy empty string into buffer of size 1");
- ok(buffer[0] == '\0', "...buffer is empty string");
-
- /* Finally, check using strlcpy as strlen. */
- is_int(3, test_strlcpy(NULL, "foo", 0), "use strlcpy as strlen");
- is_int(11, test_strlcpy(NULL, "hello world", 0), "...again");
-
- return 0;
-}
diff --git a/tests/portable/strlcpy.c b/tests/portable/strlcpy.c
deleted file mode 100644
index d444595..0000000
--- a/tests/portable/strlcpy.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#define TESTING 1
-#include <portable/strlcpy.c>
diff --git a/tests/runtests.c b/tests/runtests.c
index a9d2373..42a73ea 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -3,15 +3,19 @@
*
* Usage:
*
- * runtests [-b <build-dir>] [-s <source-dir>] <test-list>
- * runtests -o [-b <build-dir>] [-s <source-dir>] <test>
+ * runtests [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list>
+ * runtests [-hv] [-b <build-dir>] [-s <source-dir>] <test> [<test> ...]
+ * runtests -o [-h] [-b <build-dir>] [-s <source-dir>] <test>
*
* In the first case, expects a list of executables located in the given file,
* one line per executable. For each one, runs it as part of a test suite,
- * reporting results. Test output should start with a line containing the
- * number of tests (numbered from 1 to this number), optionally preceded by
- * "1..", although that line may be given anywhere in the output. Each
- * additional line should be in the following format:
+ * reporting results. In the second case, use the same infrastructure, but
+ * run only the tests listed on the command line.
+ *
+ * Test output should start with a line containing the number of tests
+ * (numbered from 1 to this number), optionally preceded by "1..", although
+ * that line may be given anywhere in the output. Each additional line should
+ * be in the following format:
*
* ok <number>
* not ok <number>
@@ -50,12 +54,16 @@
* directories. These paths can also be set with the -b and -s command-line
* options, which will override anything set at build time.
*
+ * If the -v option is given, or the C_TAP_VERBOSE environment variable is set,
+ * display the full output of each test as it runs rather than showing a
+ * summary of the results of each test.
+ *
* Any bug reports, bug fixes, and improvements are very much welcome and
* should be sent to the e-mail address below. This program is part of C TAP
* Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
* Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
- * 2014 Russ Allbery <eagle@eyrie.org>
+ * 2014, 2015 Russ Allbery <eagle@eyrie.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -146,6 +154,12 @@ enum test_status {
TEST_INVALID
};
+/* Really, just a boolean, but this is more self-documenting. */
+enum test_verbose {
+ CONCISE = 0,
+ VERBOSE = 1
+};
+
/* Indicates the state of our plan. */
enum plan_status {
PLAN_INIT, /* Nothing seen yet. */
@@ -192,16 +206,18 @@ struct testlist {
* split into variables to satisfy the pedantic ISO C90 limit on strings.
*/
static const char usage_message[] = "\
-Usage: %s [-b <build-dir>] [-s <source-dir>] <test> ...\n\
- %s [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\
- %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\
-\n%s";
-static const char usage_extra[] = "\
+Usage: %s [-hv] [-b <build-dir>] [-s <source-dir>] <test> ...\n\
+ %s [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\
+ %s -o [-h] [-b <build-dir>] [-s <source-dir>] <test>\n\
+\n\
Options:\n\
-b <build-dir> Set the build directory to <build-dir>\n\
+%s";
+static const char usage_extra[] = "\
-l <list> Take the list of tests to run from <test-list>\n\
-o Run a single test rather than a list of tests\n\
-s <source-dir> Set the source directory to <source-dir>\n\
+ -v Show the full output of each test\n\
\n\
runtests normally runs each test listed on the command line. With the -l\n\
option, it instead runs every test listed in a file. With the -o option,\n\
@@ -246,8 +262,10 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\
* variadic macro support.
*/
#if !defined(__attribute__) && !defined(__alloc_size__)
-# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
-# define __alloc_size__(spec, args...) /* empty */
+# if defined(__GNUC__) && !defined(__clang__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# define __alloc_size__(spec, args...) /* empty */
+# endif
# endif
#endif
@@ -591,13 +609,28 @@ resize_results(struct testset *ts, unsigned long n)
/*
+ * Report an invalid test number and set the appropriate flags. Pulled into a
+ * separate function since we do this in several places.
+ */
+static void
+invalid_test_number(struct testset *ts, long n, enum test_verbose verbose)
+{
+ if (!verbose)
+ test_backspace(ts);
+ printf("ABORTED (invalid test number %ld)\n", n);
+ ts->aborted = 1;
+ ts->reported = 1;
+}
+
+
+/*
* Read the plan line of test output, which should contain the range of test
* numbers. We may initialize the testset structure here if we haven't yet
* seen a test. Return true if initialization succeeded and the test should
* continue, false otherwise.
*/
static int
-test_plan(const char *line, struct testset *ts)
+test_plan(const char *line, struct testset *ts, enum test_verbose verbose)
{
long n;
@@ -654,10 +687,7 @@ test_plan(const char *line, struct testset *ts)
* range.
*/
if (ts->plan == PLAN_PENDING && (unsigned long) n < ts->count) {
- test_backspace(ts);
- printf("ABORTED (invalid test number %lu)\n", ts->count);
- ts->aborted = 1;
- ts->reported = 1;
+ invalid_test_number(ts, (long) ts->count, verbose);
return 0;
}
@@ -665,8 +695,8 @@ test_plan(const char *line, struct testset *ts)
* Otherwise, allocated or resize the results if needed and update count,
* and then record that we've seen a plan.
*/
- resize_results(ts, n);
- ts->count = n;
+ resize_results(ts, (unsigned long) n);
+ ts->count = (unsigned long) n;
if (ts->plan == PLAN_INIT)
ts->plan = PLAN_FIRST;
else if (ts->plan == PLAN_PENDING)
@@ -682,7 +712,8 @@ test_plan(const char *line, struct testset *ts)
* reported status.
*/
static void
-test_checkline(const char *line, struct testset *ts)
+test_checkline(const char *line, struct testset *ts,
+ enum test_verbose verbose)
{
enum test_status status = TEST_PASS;
const char *bail;
@@ -701,7 +732,8 @@ test_checkline(const char *line, struct testset *ts)
length = strlen(bail);
if (bail[length - 1] == '\n')
length--;
- test_backspace(ts);
+ if (!verbose)
+ test_backspace(ts);
printf("ABORTED (%.*s)\n", (int) length, bail);
ts->reported = 1;
}
@@ -722,14 +754,15 @@ test_checkline(const char *line, struct testset *ts)
/* If we haven't yet seen a plan, look for one. */
if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) {
- if (!test_plan(line, ts))
+ if (!test_plan(line, ts, verbose))
return;
} else if (strncmp(line, "1..", 3) == 0) {
if (ts->plan == PLAN_PENDING) {
- if (!test_plan(line, ts))
+ if (!test_plan(line, ts, verbose))
return;
} else {
- test_backspace(ts);
+ if (!verbose)
+ test_backspace(ts);
puts("ABORTED (multiple plans)");
ts->aborted = 1;
ts->reported = 1;
@@ -748,13 +781,14 @@ test_checkline(const char *line, struct testset *ts)
errno = 0;
number = strtol(line, &end, 10);
if (errno != 0 || end == line)
- number = ts->current + 1;
- current = number;
- if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) {
- test_backspace(ts);
- printf("ABORTED (invalid test number %lu)\n", current);
- ts->aborted = 1;
- ts->reported = 1;
+ current = ts->current + 1;
+ else if (number <= 0) {
+ invalid_test_number(ts, number, verbose);
+ return;
+ } else
+ current = (unsigned long) number;
+ if (current > ts->count && ts->plan == PLAN_FIRST) {
+ invalid_test_number(ts, (long) current, verbose);
return;
}
@@ -783,7 +817,8 @@ test_checkline(const char *line, struct testset *ts)
/* Make sure that the test number is in range and not a duplicate. */
if (ts->results[current - 1] != TEST_INVALID) {
- test_backspace(ts);
+ if (!verbose)
+ test_backspace(ts);
printf("ABORTED (duplicate test number %lu)\n", current);
ts->aborted = 1;
ts->reported = 1;
@@ -799,13 +834,13 @@ test_checkline(const char *line, struct testset *ts)
}
ts->current = current;
ts->results[current - 1] = status;
- if (isatty(STDOUT_FILENO)) {
+ if (!verbose && isatty(STDOUT_FILENO)) {
test_backspace(ts);
if (ts->plan == PLAN_PENDING)
outlen = printf("%lu/?", current);
else
outlen = printf("%lu/%lu", current, ts->count);
- ts->length = (outlen >= 0) ? outlen : 0;
+ ts->length = (outlen >= 0) ? (unsigned int) outlen : 0;
fflush(stdout);
}
}
@@ -821,7 +856,7 @@ test_checkline(const char *line, struct testset *ts)
* disable this).
*/
static unsigned int
-test_print_range(unsigned long first, unsigned long last, unsigned int chars,
+test_print_range(unsigned long first, unsigned long last, unsigned long chars,
unsigned int limit)
{
unsigned int needed = 0;
@@ -991,7 +1026,7 @@ test_analyze(struct testset *ts)
* false otherwise.
*/
static int
-test_run(struct testset *ts)
+test_run(struct testset *ts, enum test_verbose verbose)
{
pid_t testpid, child;
int outfd, status;
@@ -1008,12 +1043,19 @@ test_run(struct testset *ts)
sysdie("fdopen failed");
}
- /* Pass each line of output to test_checkline(). */
- while (!ts->aborted && fgets(buffer, sizeof(buffer), output))
- test_checkline(buffer, ts);
+ /*
+ * Pass each line of output to test_checkline(), and print the line if
+ * verbosity is requested.
+ */
+ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) {
+ if (verbose)
+ printf("%s", buffer);
+ test_checkline(buffer, ts, verbose);
+ }
if (ferror(output) || ts->plan == PLAN_INIT)
ts->aborted = 1;
- test_backspace(ts);
+ if (!verbose)
+ test_backspace(ts);
/*
* Consume the rest of the test output, close the output descriptor,
@@ -1021,7 +1063,8 @@ test_run(struct testset *ts)
* for eventual output.
*/
while (fgets(buffer, sizeof(buffer), output))
- ;
+ if (verbose)
+ printf("%s", buffer);
fclose(output);
child = waitpid(testpid, &ts->status, 0);
if (child == (pid_t) -1) {
@@ -1129,7 +1172,7 @@ is_valid_test(const char *path)
static char *
find_test(const char *name, const char *source, const char *build)
{
- char *path;
+ char *path = NULL;
const char *bases[3], *suffix, *base;
unsigned int i, j;
const char *suffixes[3] = { "-t", ".t", "" };
@@ -1161,7 +1204,8 @@ find_test(const char *name, const char *source, const char *build)
/*
* Read a list of tests from a file, returning the list of tests as a struct
- * testlist. Reports an error to standard error and exits if the list of
+ * testlist, or NULL if there were no tests (such as a file containing only
+ * comments). Reports an error to standard error and exits if the list of
* tests cannot be read.
*/
static struct testlist *
@@ -1171,6 +1215,7 @@ read_test_list(const char *filename)
unsigned int line;
size_t length;
char buffer[BUFSIZ];
+ const char *testname;
struct testlist *listhead, *current;
/* Create the initial container list that will hold our results. */
@@ -1193,6 +1238,15 @@ read_test_list(const char *filename)
exit(1);
}
buffer[length] = '\0';
+
+ /* Skip comments, leading spaces, and blank lines. */
+ testname = skip_whitespace(buffer);
+ if (strlen(testname) == 0)
+ continue;
+ if (testname[0] == '#')
+ continue;
+
+ /* Allocate the new testset structure. */
if (current == NULL)
current = listhead;
else {
@@ -1201,10 +1255,16 @@ read_test_list(const char *filename)
}
current->ts = xcalloc(1, sizeof(struct testset));
current->ts->plan = PLAN_INIT;
- current->ts->file = xstrdup(buffer);
+ current->ts->file = xstrdup(testname);
}
fclose(file);
+ /* If there were no tests, current is still NULL. */
+ if (current == NULL) {
+ free(listhead);
+ return NULL;
+ }
+
/* Return the results. */
return listhead;
}
@@ -1213,7 +1273,8 @@ read_test_list(const char *filename)
/*
* Build a list of tests from command line arguments. Takes the argv and argc
* representing the command line arguments and returns a newly allocated test
- * list. The caller is responsible for freeing.
+ * list, or NULL if there were no tests. The caller is responsible for
+ * freeing.
*/
static struct testlist *
build_test_list(char *argv[], int argc)
@@ -1238,6 +1299,12 @@ build_test_list(char *argv[], int argc)
current->ts->file = xstrdup(argv[i]);
}
+ /* If there were no tests, current is still NULL. */
+ if (current == NULL) {
+ free(listhead);
+ return NULL;
+ }
+
/* Return the results. */
return listhead;
}
@@ -1263,11 +1330,11 @@ free_testset(struct testset *ts)
* frees the test list that's passed in.
*/
static int
-test_batch(struct testlist *tests, const char *source, const char *build)
+test_batch(struct testlist *tests, const char *source, const char *build,
+ enum test_verbose verbose)
{
- size_t length;
- unsigned int i;
- unsigned int longest = 0;
+ size_t length, i;
+ size_t longest = 0;
unsigned int count = 0;
struct testset *ts;
struct timeval start, end;
@@ -1306,15 +1373,20 @@ test_batch(struct testlist *tests, const char *source, const char *build)
/* Print out the name of the test file. */
fputs(ts->file, stdout);
- for (i = strlen(ts->file); i < longest; i++)
- putchar('.');
+ if (verbose)
+ fputs("\n\n", stdout);
+ else
+ for (i = strlen(ts->file); i < longest; i++)
+ putchar('.');
if (isatty(STDOUT_FILENO))
fflush(stdout);
/* Run the test. */
ts->path = find_test(ts->file, source, build);
- succeeded = test_run(ts);
+ succeeded = test_run(ts, verbose);
fflush(stdout);
+ if (verbose)
+ putchar('\n');
/* Record cumulative statistics. */
aborted += ts->aborted;
@@ -1416,23 +1488,25 @@ main(int argc, char *argv[])
int option;
int status = 0;
int single = 0;
+ enum test_verbose verbose = CONCISE;
char *source_env = NULL;
char *build_env = NULL;
+ const char *program;
const char *shortlist;
const char *list = NULL;
const char *source = SOURCE;
const char *build = BUILD;
struct testlist *tests;
- while ((option = getopt(argc, argv, "b:hl:os:")) != EOF) {
+ program = argv[0];
+ while ((option = getopt(argc, argv, "b:hl:os:v")) != EOF) {
switch (option) {
case 'b':
build = optarg;
break;
case 'h':
- printf(usage_message, argv[0], argv[0], argv[0], usage_extra);
+ printf(usage_message, program, program, program, usage_extra);
exit(0);
- break;
case 'l':
list = optarg;
break;
@@ -1442,6 +1516,9 @@ main(int argc, char *argv[])
case 's':
source = optarg;
break;
+ case 'v':
+ verbose = VERBOSE;
+ break;
default:
exit(1);
}
@@ -1449,10 +1526,17 @@ main(int argc, char *argv[])
argv += optind;
argc -= optind;
if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) {
- fprintf(stderr, usage_message, argv[0], argv[0], argv[0], usage_extra);
+ fprintf(stderr, usage_message, program, program, program, usage_extra);
exit(1);
}
+ /*
+ * If C_TAP_VERBOSE is set in the environment, that also turns on verbose
+ * mode.
+ */
+ if (getenv("C_TAP_VERBOSE") != NULL)
+ verbose = VERBOSE;
+
/* Set SOURCE and BUILD environment variables. */
if (source != NULL) {
source_env = concat("SOURCE=", source, (const char *) 0);
@@ -1476,10 +1560,10 @@ main(int argc, char *argv[])
shortlist++;
printf(banner, shortlist);
tests = read_test_list(list);
- status = test_batch(tests, source, build) ? 0 : 1;
+ status = test_batch(tests, source, build, verbose) ? 0 : 1;
} else {
tests = build_test_list(argv, argc);
- status = test_batch(tests, source, build) ? 0 : 1;
+ status = test_batch(tests, source, build, verbose) ? 0 : 1;
}
/* For valgrind cleanliness, free all our memory. */
diff --git a/tests/tap/basic.c b/tests/tap/basic.c
index 92a749b..4f8be04 100644
--- a/tests/tap/basic.c
+++ b/tests/tap/basic.c
@@ -12,7 +12,8 @@
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
- * Copyright 2009, 2010, 2011, 2012, 2013, 2014 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015
+ * Russ Allbery <eagle@eyrie.org>
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
@@ -105,7 +106,7 @@ static struct cleanup_func *cleanup_funcs = NULL;
/*
* Registered diag files. Any output found in these files will be printed out
* as if it were passed to diag() before any other output we do. This allows
- * background processes to log to a file and have that output interleved with
+ * background processes to log to a file and have that output interleaved with
* the test output.
*/
struct diag_file {
@@ -192,7 +193,7 @@ check_diag_files(void)
struct diag_file *file;
fpos_t where;
size_t length;
- int incomplete;
+ int size, incomplete;
/*
* Walk through each file and read each line of output available. The
@@ -216,7 +217,8 @@ check_diag_files(void)
/* Continue until we get EOF or an incomplete line of data. */
incomplete = 0;
while (!feof(file->file) && !incomplete) {
- if (fgets(file->buffer, file->bufsize, file->file) == NULL) {
+ size = file->bufsize > INT_MAX ? INT_MAX : (int) file->bufsize;
+ if (fgets(file->buffer, size, file->file) == NULL) {
if (ferror(file->file))
sysbail("cannot read from %s", file->name);
continue;
@@ -807,7 +809,7 @@ bstrndup(const char *s, size_t n)
/* Don't assume that the source string is nul-terminated. */
for (p = s; (size_t) (p - s) < n && *p != '\0'; p++)
;
- length = p - s;
+ length = (size_t) (p - s);
copy = malloc(length + 1);
if (p == NULL)
sysbail("failed to strndup %lu bytes", (unsigned long) length);
diff --git a/tests/tap/basic.h b/tests/tap/basic.h
index c002df9..4ecaaec 100644
--- a/tests/tap/basic.h
+++ b/tests/tap/basic.h
@@ -4,7 +4,8 @@
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
- * Copyright 2009, 2010, 2011, 2012, 2013, 2014 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015
+ * Russ Allbery <eagle@eyrie.org>
* Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
@@ -72,7 +73,8 @@ void skip_all(const char *format, ...)
*/
int ok(int success, const char *format, ...)
__attribute__((__format__(printf, 2, 3)));
-int okv(int success, const char *format, va_list args);
+int okv(int success, const char *format, va_list args)
+ __attribute__((__format__(printf, 2, 0)));
void skip(const char *reason, ...)
__attribute__((__format__(printf, 1, 2)));
@@ -155,7 +157,7 @@ void test_tmpdir_free(char *path);
* registered functions will be run during atexit handling (and are therefore
* subject to all the same constraints and caveats as atexit functions).
*
- * The function must return void and will be passed two argument, an int that
+ * The function must return void and will be passed two arguments: an int that
* will be true if the test completed successfully and false otherwise, and an
* int that will be true if the cleanup function is run in the primary process
* (the one that called plan or plan_lazy) and false otherwise.
diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c
index 578a858..6a5025a 100644
--- a/tests/tap/kerberos.c
+++ b/tests/tap/kerberos.c
@@ -55,7 +55,9 @@
* Disable the requirement that format strings be literals, since it's easier
* to handle the possible patterns for kinit commands as an array.
*/
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2) || defined(__clang__)
+# pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
/*
@@ -219,6 +221,8 @@ kerberos_free(void)
free(config->userprinc);
free(config->username);
free(config->password);
+ free(config->pkinit_principal);
+ free(config->pkinit_cert);
free(config);
config = NULL;
}
@@ -351,6 +355,31 @@ kerberos_setup(enum kerberos_needs needs)
test_file_path_free(path);
/*
+ * If we have PKINIT configuration, read it and fill out the relevant
+ * members of our config struct.
+ */
+ path = test_file_path("config/pkinit-principal");
+ if (path != NULL)
+ file = fopen(path, "r");
+ if (file != NULL) {
+ if (fgets(buffer, sizeof(buffer), file) == NULL)
+ bail("cannot read %s", path);
+ if (buffer[strlen(buffer) - 1] != '\n')
+ bail("no newline in %s", path);
+ buffer[strlen(buffer) - 1] = '\0';
+ fclose(file);
+ test_file_path_free(path);
+ path = test_file_path("config/pkinit-cert");
+ if (path != NULL) {
+ config->pkinit_principal = bstrdup(buffer);
+ config->pkinit_cert = bstrdup(path);
+ }
+ }
+ test_file_path_free(path);
+ if (config->pkinit_cert == NULL && (needs & TAP_KRB_NEEDS_PKINIT) != 0)
+ skip_all("PKINIT tests not configured");
+
+ /*
* Register the cleanup function so that the caller doesn't have to do
* explicit cleanup.
*/
diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h
index 8be0add..26f45f9 100644
--- a/tests/tap/kerberos.h
+++ b/tests/tap/kerberos.h
@@ -46,17 +46,21 @@ struct kerberos_config {
char *username; /* The local (non-realm) part of principal. */
char *realm; /* The realm part of the principal. */
char *password; /* The password. */
+ char *pkinit_principal; /* Principal for PKINIT authentication. */
+ char *pkinit_cert; /* Path to certificates for PKINIT. */
};
/*
* Whether to skip all tests (by calling skip_all) in kerberos_setup if
- * certain configuration information isn't available.
+ * certain configuration information isn't available. "_BOTH" means that the
+ * tests require both keytab and password, but PKINIT is not required.
*/
enum kerberos_needs {
TAP_KRB_NEEDS_NONE = 0x00,
TAP_KRB_NEEDS_KEYTAB = 0x01,
TAP_KRB_NEEDS_PASSWORD = 0x02,
- TAP_KRB_NEEDS_BOTH = 0x01 | 0x02
+ TAP_KRB_NEEDS_BOTH = 0x01 | 0x02,
+ TAP_KRB_NEEDS_PKINIT = 0x04
};
BEGIN_DECLS
diff --git a/tests/tap/macros.h b/tests/tap/macros.h
index 04cc420..139cff0 100644
--- a/tests/tap/macros.h
+++ b/tests/tap/macros.h
@@ -8,7 +8,7 @@
* This file is part of C TAP Harness. The current version plus supporting
* documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
*
- * Copyright 2008, 2012, 2013 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2008, 2012, 2013, 2015 Russ Allbery <eagle@eyrie.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -53,8 +53,10 @@
* variadic macro support.
*/
#if !defined(__attribute__) && !defined(__alloc_size__)
-# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
-# define __alloc_size__(spec, args...) /* empty */
+# if defined(__GNUC__) && !defined(__clang__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# define __alloc_size__(spec, args...) /* empty */
+# endif
# endif
#endif
diff --git a/tests/tap/messages.c b/tests/tap/messages.c
index 45b0566..9c28789 100644
--- a/tests/tap/messages.c
+++ b/tests/tap/messages.c
@@ -8,7 +8,7 @@
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
*
- * Copyright 2002, 2004, 2005 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2002, 2004, 2005, 2015 Russ Allbery <eagle@eyrie.org>
* Copyright 2006, 2007, 2009, 2012, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
@@ -47,7 +47,7 @@ char *errors = NULL;
* An error handler that appends all errors to the errors global. Used by
* error_capture.
*/
-static void
+static void __attribute__((__format__(printf, 2, 0)))
message_log_buffer(int len UNUSED, const char *fmt, va_list args,
int error UNUSED)
{
diff --git a/tests/tap/perl/Test/RRA.pm b/tests/tap/perl/Test/RRA.pm
index bb7de7d..8608e31 100644
--- a/tests/tap/perl/Test/RRA.pm
+++ b/tests/tap/perl/Test/RRA.pm
@@ -5,31 +5,6 @@
# by both C packages with Automake and by stand-alone Perl modules. See
# Test::RRA::Automake for additional functions specifically for C Automake
# distributions.
-#
-# The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
-#
-# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013, 2014
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
package Test::RRA;
@@ -56,7 +31,7 @@ BEGIN {
# This version should match the corresponding rra-c-util release, but with
# two digits for the minor version, including a leading zero if necessary,
# so that it will sort properly.
- $VERSION = '5.05';
+ $VERSION = '5.10';
}
# Skip this test unless author tests are requested. Takes a short description
@@ -153,7 +128,7 @@ __END__
=for stopwords
Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT
-rra-c-util
+rra-c-util CPAN
=head1 NAME
@@ -176,46 +151,45 @@ Test::RRA - Support functions for Perl tests
=head1 DESCRIPTION
-This module collects utility functions that are useful for Perl test
-scripts. It assumes Russ Allbery's Perl module layout and test
-conventions and will only be useful for other people if they use the
-same conventions.
+This module collects utility functions that are useful for Perl test scripts.
+It assumes Russ Allbery's Perl module layout and test conventions and will
+only be useful for other people if they use the same conventions.
=head1 FUNCTIONS
-None of these functions are imported by default. The ones used by a
-script should be explicitly imported.
+None of these functions are imported by default. The ones used by a script
+should be explicitly imported.
=over 4
=item skip_unless_author(DESC)
-Checks whether AUTHOR_TESTING is set in the environment and skips the
-whole test (by calling C<plan skip_all> from Test::More) if it is not.
-DESC is a description of the tests being skipped. A space and C<only run
-for author> will be appended to it and used as the skip reason.
+Checks whether AUTHOR_TESTING is set in the environment and skips the whole
+test (by calling C<plan skip_all> from Test::More) if it is not. DESC is a
+description of the tests being skipped. A space and C<only run for author>
+will be appended to it and used as the skip reason.
=item skip_unless_automated(DESC)
-Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are
-set in the environment and skips the whole test (by calling C<plan
-skip_all> from Test::More) if they are not. This should be used by tests
-that should not run during end-user installs of the module, but which
-should run as part of CPAN smoke testing and release testing.
+Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are set
+in the environment and skips the whole test (by calling C<plan skip_all> from
+Test::More) if they are not. This should be used by tests that should not run
+during end-user installs of the module, but which should run as part of CPAN
+smoke testing and release testing.
DESC is a description of the tests being skipped. A space and C<normally
skipped> will be appended to it and used as the skip reason.
=item use_prereq(MODULE[, VERSION][, IMPORT ...])
-Attempts to load MODULE with the given VERSION and import arguments. If
-this fails for any reason, the test will be skipped (by calling C<plan
-skip_all> from Test::More) with a skip reason saying that MODULE is
-required for the test.
+Attempts to load MODULE with the given VERSION and import arguments. If this
+fails for any reason, the test will be skipped (by calling C<plan skip_all>
+from Test::More) with a skip reason saying that MODULE is required for the
+test.
VERSION will be passed to C<use> as a version bareword if it looks like a
-version number. The remaining IMPORT arguments will be passed as the
-value of an array.
+version number. The remaining IMPORT arguments will be passed as the value of
+an array.
=back
@@ -228,33 +202,33 @@ Russ Allbery <eagle@eyrie.org>
Copyright 2013, 2014 The Board of Trustees of the Leland Stanford Junior
University
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
=head1 SEE ALSO
Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3)
-This module is maintained in the rra-c-util package. The current version
-is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+This module is maintained in the rra-c-util package. The current version is
+available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
-The functions to control when tests are run use environment variables
-defined by the L<Lancaster
+The functions to control when tests are run use environment variables defined
+by the L<Lancaster
Consensus|https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>.
=cut
diff --git a/tests/tap/perl/Test/RRA/Automake.pm b/tests/tap/perl/Test/RRA/Automake.pm
index a064ed9..c6399ec 100644
--- a/tests/tap/perl/Test/RRA/Automake.pm
+++ b/tests/tap/perl/Test/RRA/Automake.pm
@@ -9,31 +9,6 @@
#
# All the functions here assume that BUILD and SOURCE are set in the
# environment. This is normally done via the C TAP Harness runtests wrapper.
-#
-# The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
-#
-# Written by Russ Allbery <eagle@eyrie.org>
-# Copyright 2013
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
package Test::RRA::Automake;
@@ -87,13 +62,13 @@ BEGIN {
# This version should match the corresponding rra-c-util release, but with
# two digits for the minor version, including a leading zero if necessary,
# so that it will sort properly.
- $VERSION = '5.05';
+ $VERSION = '5.10';
}
# Perl directories to skip globally for perl_dirs. We ignore the perl
# directory if it exists since, in my packages, it is treated as a Perl module
# distribution and has its own standalone test suite.
-my @GLOBAL_SKIP = qw(.git perl);
+my @GLOBAL_SKIP = qw(.git _build perl);
# The temporary directory created by test_tmpdir, if any. If this is set,
# attempt to remove the directory stored here on program exit (but ignore
@@ -126,7 +101,15 @@ sub automake_setup {
my ($vol, $dirs) = File::Spec->splitpath($start, 1);
my @dirs = File::Spec->splitdir($dirs);
pop(@dirs);
- if ($dirs[-1] eq File::Spec->updir) {
+
+ # Simplify relative paths at the end of the directory.
+ my $ups = 0;
+ my $i = $#dirs;
+ while ($i > 2 && $dirs[$i] eq File::Spec->updir) {
+ $ups++;
+ $i--;
+ }
+ for (1 .. $ups) {
pop(@dirs);
pop(@dirs);
}
@@ -196,7 +179,7 @@ sub perl_dirs {
# Build the list of top-level directories to test.
opendir(my $rootdir, q{.}) or BAIL_OUT("cannot open .: $!");
- my @dirs = grep { -d $_ && !$skip{$_} } readdir($rootdir);
+ my @dirs = grep { -d && !$skip{$_} } readdir($rootdir);
closedir($rootdir);
@dirs = File::Spec->no_upwards(@dirs);
@@ -288,8 +271,8 @@ END {
__END__
=for stopwords
-Allbery Automake Automake-aware Automake-based rra-c-util ARGS
-subdirectories sublicense MERCHANTABILITY NONINFRINGEMENT umask
+Allbery Automake Automake-aware Automake-based rra-c-util ARGS subdirectories
+sublicense MERCHANTABILITY NONINFRINGEMENT umask
=head1 NAME
@@ -309,75 +292,71 @@ Test::RRA::Automake - Automake-aware support functions for Perl tests
=head1 DESCRIPTION
This module collects utility functions that are useful for test scripts
-written in Perl and included in a C Automake-based package. They assume
-the layout of a package that uses rra-c-util and C TAP Harness for the
-test structure.
+written in Perl and included in a C Automake-based package. They assume the
+layout of a package that uses rra-c-util and C TAP Harness for the test
+structure.
Loading this module will also add the directories C<perl/blib/arch> and
-C<perl/blib/lib> to the Perl library search path, relative to BUILD if
-that environment variable is set. This is harmless for C Automake
-projects that don't contain an embedded Perl module, and for those
-projects that do, this will allow subsequent C<use> calls to find modules
-that are built as part of the package build process.
+C<perl/blib/lib> to the Perl library search path, relative to BUILD if that
+environment variable is set. This is harmless for C Automake projects that
+don't contain an embedded Perl module, and for those projects that do, this
+will allow subsequent C<use> calls to find modules that are built as part of
+the package build process.
The automake_setup() function should be called before calling any other
functions provided by this module.
=head1 FUNCTIONS
-None of these functions are imported by default. The ones used by a
-script should be explicitly imported. On failure, all of these functions
-call BAIL_OUT (from Test::More).
+None of these functions are imported by default. The ones used by a script
+should be explicitly imported. On failure, all of these functions call
+BAIL_OUT (from Test::More).
=over 4
=item automake_setup([ARGS])
-Verifies that the BUILD and SOURCE environment variables are set and
-then changes directory to the top of the source tree (which is one
-directory up from the SOURCE path, since SOURCE points to the top of
-the tests directory).
+Verifies that the BUILD and SOURCE environment variables are set and then
+changes directory to the top of the source tree (which is one directory up
+from the SOURCE path, since SOURCE points to the top of the tests directory).
-If ARGS is given, it should be a reference to a hash of configuration
-options. Only one option is supported: C<chdir_build>. If it is set
-to a true value, automake_setup() changes directories to the top of
-the build tree instead.
+If ARGS is given, it should be a reference to a hash of configuration options.
+Only one option is supported: C<chdir_build>. If it is set to a true value,
+automake_setup() changes directories to the top of the build tree instead.
=item perl_dirs([ARGS])
Returns a list of directories that may contain Perl scripts that should be
-tested by test scripts that test all Perl in the source tree (such as
-syntax or coding style checks). The paths will be simple directory names
-relative to the current directory or two-part directory names under the
-F<tests> directory. (Directories under F<tests> are broken out separately
-since it's common to want to apply different policies to different
-subdirectories of F<tests>.)
-
-If ARGS is given, it should be a reference to a hash of configuration
-options. Only one option is supported: C<skip>, whose value should be a
-reference to an array of additional top-level directories or directories
-starting with C<tests/> that should be skipped.
+tested by test scripts that test all Perl in the source tree (such as syntax
+or coding style checks). The paths will be simple directory names relative to
+the current directory or two-part directory names under the F<tests>
+directory. (Directories under F<tests> are broken out separately since it's
+common to want to apply different policies to different subdirectories of
+F<tests>.)
+
+If ARGS is given, it should be a reference to a hash of configuration options.
+Only one option is supported: C<skip>, whose value should be a reference to an
+array of additional top-level directories or directories starting with
+C<tests/> that should be skipped.
=item test_file_path(FILE)
-Given FILE, which should be a relative path, locates that file relative to
-the test directory in either the source or build tree. FILE will be
-checked for relative to the environment variable BUILD first, and then
-relative to SOURCE. test_file_path() returns the full path to FILE or
-calls BAIL_OUT if FILE could not be found.
+Given FILE, which should be a relative path, locates that file relative to the
+test directory in either the source or build tree. FILE will be checked for
+relative to the environment variable BUILD first, and then relative to SOURCE.
+test_file_path() returns the full path to FILE or calls BAIL_OUT if FILE could
+not be found.
=item test_tmpdir()
-Create a temporary directory for tests to use for transient files and
-return the path to that directory. The directory is created relative to
-the BUILD environment variable, which must be set. Permissions on the
-directory are set using the current umask. test_tmpdir() returns the full
-path to the temporary directory or calls BAIL_OUT if it could not be
-created.
+Create a temporary directory for tests to use for transient files and return
+the path to that directory. The directory is created relative to the BUILD
+environment variable, which must be set. Permissions on the directory are set
+using the current umask. test_tmpdir() returns the full path to the temporary
+directory or calls BAIL_OUT if it could not be created.
-The directory is automatically removed if possible on program exit.
-Failure to remove the directory on exit is reported with diag() and
-otherwise ignored.
+The directory is automatically removed if possible on program exit. Failure
+to remove the directory on exit is reported with diag() and otherwise ignored.
=back
@@ -387,35 +366,36 @@ Russ Allbery <eagle@eyrie.org>
=head1 COPYRIGHT AND LICENSE
-Copyright 2013 The Board of Trustees of the Leland Stanford Junior
-University
+Copyright 2014, 2015 Russ Allbery <eagle@eyrie.org>
+
+Copyright 2013 The Board of Trustees of the Leland Stanford Junior University
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
=head1 SEE ALSO
Test::More(3), Test::RRA(3), Test::RRA::Config(3)
+This module is maintained in the rra-c-util package. The current version is
+available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+
The C TAP Harness test driver and libraries for TAP-based C testing are
available from L<http://www.eyrie.org/~eagle/software/c-tap-harness/>.
-This module is maintained in the rra-c-util package. The current version
-is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
-
=cut
diff --git a/tests/tap/perl/Test/RRA/Config.pm b/tests/tap/perl/Test/RRA/Config.pm
index 3e77650..a5b0d0d 100644
--- a/tests/tap/perl/Test/RRA/Config.pm
+++ b/tests/tap/perl/Test/RRA/Config.pm
@@ -4,9 +4,6 @@
# configuration file to store some package-specific data. This module loads
# that configuration and provides the namespace for the configuration
# settings.
-#
-# The canonical version of this file is maintained in the rra-c-util package,
-# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
package Test::RRA::Config;
@@ -30,22 +27,23 @@ BEGIN {
@ISA = qw(Exporter);
@EXPORT_OK = qw(
$COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH
- $MINIMUM_VERSION %MINIMUM_VERSION @POD_COVERAGE_EXCLUDE @STRICT_IGNORE
- @STRICT_PREREQ
+ $MINIMUM_VERSION %MINIMUM_VERSION @MODULE_VERSION_IGNORE
+ @POD_COVERAGE_EXCLUDE @STRICT_IGNORE @STRICT_PREREQ
);
# This version should match the corresponding rra-c-util release, but with
# two digits for the minor version, including a leading zero if necessary,
# so that it will sort properly.
- $VERSION = '5.05';
+ $VERSION = '5.10';
}
# If BUILD or SOURCE are set in the environment, look for data/perl.conf under
# those paths for a C Automake package. Otherwise, look in t/data/perl.conf
-# for a standalone Perl module. Don't use Test::RRA::Automake since it may
-# not exist.
+# for a standalone Perl module or tests/data/perl.conf for Perl tests embedded
+# in a larger distribution. Don't use Test::RRA::Automake since it may not
+# exist.
our $PATH;
-for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't') {
+for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't', 'tests') {
next if !defined($base);
my $path = "$base/data/perl.conf";
if (-r $path) {
@@ -64,6 +62,7 @@ our @CRITIC_IGNORE;
our $LIBRARY_PATH;
our $MINIMUM_VERSION = '5.008';
our %MINIMUM_VERSION;
+our @MODULE_VERSION_IGNORE;
our @POD_COVERAGE_EXCLUDE;
our @STRICT_IGNORE;
our @STRICT_PREREQ;
@@ -78,8 +77,8 @@ if (!do($PATH)) {
__END__
=for stopwords
-Allbery rra-c-util Automake perlcritic .libs namespace subdirectory
-sublicense MERCHANTABILITY NONINFRINGEMENT
+Allbery rra-c-util Automake perlcritic .libs namespace subdirectory sublicense
+MERCHANTABILITY NONINFRINGEMENT regexes
=head1 NAME
@@ -92,19 +91,17 @@ Test::RRA::Config - Perl test configuration
=head1 DESCRIPTION
-Test::RRA::Config encapsulates per-package configuration for generic Perl
-test programs that are shared between multiple packages using the
-rra-c-util infrastructure. It handles locating and loading the test
-configuration file for both C Automake packages and stand-alone Perl
-modules.
+Test::RRA::Config encapsulates per-package configuration for generic Perl test
+programs that are shared between multiple packages using the rra-c-util
+infrastructure. It handles locating and loading the test configuration file
+for both C Automake packages and stand-alone Perl modules.
Test::RRA::Config looks for a file named F<data/perl.conf> relative to the
-root of the test directory. That root is taken from the environment
-variables BUILD or SOURCE (in that order) if set, which will be the case
-for C Automake packages using C TAP Harness. If neither is set, it
-expects the root of the test directory to be a directory named F<t>
-relative to the current directory, which will be the case for stand-alone
-Perl modules.
+root of the test directory. That root is taken from the environment variables
+BUILD or SOURCE (in that order) if set, which will be the case for C Automake
+packages using C TAP Harness. If neither is set, it expects the root of the
+test directory to be a directory named F<t> relative to the current directory,
+which will be the case for stand-alone Perl modules.
The following variables are supported:
@@ -112,70 +109,75 @@ The following variables are supported:
=item $COVERAGE_LEVEL
-The coverage level achieved by the test suite for Perl test coverage
-testing using Test::Strict, as a percentage. The test will fail if test
-coverage less than this percentage is achieved. If not given, defaults
-to 100.
+The coverage level achieved by the test suite for Perl test coverage testing
+using Test::Strict, as a percentage. The test will fail if test coverage less
+than this percentage is achieved. If not given, defaults to 100.
=item @COVERAGE_SKIP_TESTS
Directories under F<t> whose tests should be skipped when doing coverage
-testing. This can be tests that won't contribute to coverage or tests
-that don't run properly under Devel::Cover for some reason (such as ones
-that use taint checking). F<docs> and F<style> will always be skipped
-regardless of this setting.
+testing. This can be tests that won't contribute to coverage or tests that
+don't run properly under Devel::Cover for some reason (such as ones that use
+taint checking). F<docs> and F<style> will always be skipped regardless of
+this setting.
=item @CRITIC_IGNORE
-Additional directories to ignore when doing recursive perlcritic testing.
-The contents of this directory must be either top-level directory names or
+Additional directories to ignore when doing recursive perlcritic testing. The
+contents of this directory must be either top-level directory names or
directory names starting with F<tests/>.
=item $LIBRARY_PATH
Add this directory (or a F<.libs> subdirectory) relative to the top of the
-source tree to LD_LIBRARY_PATH when checking the syntax of Perl modules.
-This may be required to pick up libraries that are used by in-tree Perl
-modules so that Perl scripts can pass a syntax check.
+source tree to LD_LIBRARY_PATH when checking the syntax of Perl modules. This
+may be required to pick up libraries that are used by in-tree Perl modules so
+that Perl scripts can pass a syntax check.
=item $MINIMUM_VERSION
-Default minimum version requirement for included Perl scripts. If not
-given, defaults to 5.008.
+Default minimum version requirement for included Perl scripts. If not given,
+defaults to 5.008.
=item %MINIMUM_VERSION
Minimum version exceptions for specific directories. The keys should be
minimum versions of Perl to enforce. The value for each key should be a
-reference to an array of either top-level directory names or directory
-names starting with F<tests/>. All files in those directories will have
-that minimum Perl version constraint imposed instead of $MINIMUM_VERSION.
+reference to an array of either top-level directory names or directory names
+starting with F<tests/>. All files in those directories will have that
+minimum Perl version constraint imposed instead of $MINIMUM_VERSION.
+
+=item @MODULE_VERSION_IGNORE
+
+File names to ignore when checking that all modules in a distribution have the
+same version. Sometimes, some specific modules need separate, special version
+handling, such as modules defining database schemata for DBIx::Class, and
+can't follow the version of the larger package.
=item @POD_COVERAGE_EXCLUDE
Regexes that match method names that should be excluded from POD coverage
-testing. Normally, all methods have to be documented in the POD for a
-Perl module, but methods matching any of these regexes will be considered
-private and won't require documentation.
+testing. Normally, all methods have to be documented in the POD for a Perl
+module, but methods matching any of these regexes will be considered private
+and won't require documentation.
=item @STRICT_IGNORE
-Additional directories to ignore when doing recursive Test::Strict testing
-for C<use strict> and C<use warnings>. The contents of this directory
-must be either top-level directory names or directory names starting with
-F<tests/>.
+Additional directories to ignore when doing recursive Test::Strict testing for
+C<use strict> and C<use warnings>. The contents of this directory must be
+either top-level directory names or directory names starting with F<tests/>.
=item @STRICT_PREREQ
A list of Perl modules that have to be available in order to do meaningful
Test::Strict testing. If any of the modules cannot be loaded via C<use>,
-Test::Strict checking will be skipped. There is currently no way to
-require specific versions of the modules.
+Test::Strict checking will be skipped. There is currently no way to require
+specific versions of the modules.
=back
-No variables are exported by default, but the variables can be imported
-into the local namespace to avoid long variable names.
+No variables are exported by default, but the variables can be imported into
+the local namespace to avoid long variable names.
=head1 AUTHOR
@@ -186,31 +188,31 @@ Russ Allbery <eagle@eyrie.org>
Copyright 2013, 2014 The Board of Trustees of the Leland Stanford Junior
University
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
=head1 SEE ALSO
-perlcritic(1), Test::MinimumVersion(3), Test::RRA(3),
-Test::RRA::Automake(3), Test::Strict(3)
+perlcritic(1), Test::MinimumVersion(3), Test::RRA(3), Test::RRA::Automake(3),
+Test::Strict(3)
-This module is maintained in the rra-c-util package. The current version
-is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+This module is maintained in the rra-c-util package. The current version is
+available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
The C TAP Harness test driver and libraries for TAP-based C testing are
available from L<http://www.eyrie.org/~eagle/software/c-tap-harness/>.
diff --git a/tests/tap/perl/Test/RRA/ModuleVersion.pm b/tests/tap/perl/Test/RRA/ModuleVersion.pm
new file mode 100644
index 0000000..f02877a
--- /dev/null
+++ b/tests/tap/perl/Test/RRA/ModuleVersion.pm
@@ -0,0 +1,295 @@
+# Check Perl module versions for consistency.
+#
+# This module contains the common code for testing and updating Perl module
+# versions for consistency within a Perl module distribution and within a
+# larger package that contains both Perl modules and other code.
+
+package Test::RRA::ModuleVersion;
+
+use 5.006;
+use strict;
+use warnings;
+
+use Exporter;
+use File::Find qw(find);
+use Test::More;
+use Test::RRA::Config qw(@MODULE_VERSION_IGNORE);
+
+# For Perl 5.006 compatibility.
+## no critic (ClassHierarchies::ProhibitExplicitISA)
+
+# Declare variables that should be set in BEGIN for robustness.
+our (@EXPORT_OK, @ISA, $VERSION);
+
+# Set $VERSION and everything export-related in a BEGIN block for robustness
+# against circular module loading (not that we load any modules, but
+# consistency is good).
+BEGIN {
+ @ISA = qw(Exporter);
+ @EXPORT_OK = qw(test_module_versions update_module_versions);
+
+ # This version should match the corresponding rra-c-util release, but with
+ # two digits for the minor version, including a leading zero if necessary,
+ # so that it will sort properly.
+ $VERSION = '5.10';
+}
+
+# A regular expression matching the version string for a module using the
+# package syntax from Perl 5.12 and later. $1 will contain all of the line
+# contents prior to the actual version string, $2 will contain the version
+# itself, and $3 will contain the rest of the line.
+our $REGEX_VERSION_PACKAGE = qr{
+ ( # prefix ($1)
+ \A \s* # whitespace
+ package \s+ # package keyword
+ [\w\:\']+ \s+ # package name
+ )
+ ( v? [\d._]+ ) # the version number itself ($2)
+ ( # suffix ($3)
+ \s* ;
+ )
+}xms;
+
+# A regular expression matching a $VERSION string in a module. $1 will
+# contain all of the line contents prior to the actual version string, $2 will
+# contain the version itself, and $3 will contain the rest of the line.
+our $REGEX_VERSION_OLD = qr{
+ ( # prefix ($1)
+ \A .* # any prefix, such as "our"
+ [\$*] # scalar or typeglob
+ [\w\:\']*\b # optional package name
+ VERSION\b # version variable
+ \s* = \s* # assignment
+ )
+ [\"\']? # optional leading quote
+ ( v? [\d._]+ ) # the version number itself ($2)
+ [\"\']? # optional trailing quote
+ ( # suffix ($3)
+ \s*
+ ;
+ )
+}xms;
+
+# Find all the Perl modules shipped in this package, if any, and returns the
+# list of file names.
+#
+# $dir - The root directory to search
+#
+# Returns: List of file names
+sub _module_files {
+ my ($dir) = @_;
+ return if !-d $dir;
+ my @files;
+ my %ignore = map { $_ => 1 } @MODULE_VERSION_IGNORE;
+ my $wanted = sub {
+ if ($_ eq 'blib') {
+ $File::Find::prune = 1;
+ return;
+ }
+ if (m{ [.] pm \z }xms && !$ignore{$File::Find::name}) {
+ push(@files, $File::Find::name);
+ }
+ return;
+ };
+ find($wanted, $dir);
+ return @files;
+}
+
+# Given a module file, read it for the version value and return the value.
+#
+# $file - File to check, which should be a Perl module
+#
+# Returns: The version of the module
+# Throws: Text exception on I/O failure or inability to find version
+sub _module_version {
+ my ($file) = @_;
+ open(my $data, q{<}, $file) or die "$0: cannot open $file: $!\n";
+ while (defined(my $line = <$data>)) {
+ if ( $line =~ $REGEX_VERSION_PACKAGE
+ || $line =~ $REGEX_VERSION_OLD)
+ {
+ my ($prefix, $version, $suffix) = ($1, $2, $3);
+ close($data) or die "$0: error reading from $file: $!\n";
+ return $version;
+ }
+ }
+ close($data) or die "$0: error reading from $file: $!\n";
+ die "$0: cannot find version number in $file\n";
+}
+
+# Given a module file and the new version for that module, update the version
+# in that module to the new one.
+#
+# $file - Perl module file whose version should be updated
+# $version - The new version number
+#
+# Returns: undef
+# Throws: Text exception on I/O failure or inability to find version
+sub _update_module_version {
+ my ($file, $version) = @_;
+ open(my $in, q{<}, $file) or die "$0: cannot open $file: $!\n";
+ open(my $out, q{>}, "$file.new")
+ or die "$0: cannot create $file.new: $!\n";
+
+ # If the version starts with v, use it without quotes. Otherwise, quote
+ # it to prevent removal of trailing zeroes.
+ if ($version !~ m{ \A v }xms) {
+ $version = "'$version'";
+ }
+
+ # Scan for the version and replace it.
+ SCAN:
+ while (defined(my $line = <$in>)) {
+ if ( $line =~ s{ $REGEX_VERSION_PACKAGE }{$1$version$3}xms
+ || $line =~ s{ $REGEX_VERSION_OLD }{$1$version$3}xms)
+ {
+ print {$out} $line or die "$0: cannot write to $file.new: $!\n";
+ last SCAN;
+ }
+ print {$out} $line or die "$0: cannot write to $file.new: $!\n";
+ }
+
+ # Copy the rest of the input file to the output file.
+ print {$out} <$in> or die "$0: cannot write to $file.new: $!\n";
+ close($out) or die "$0: cannot flush $file.new: $!\n";
+ close($in) or die "$0: error reading from $file: $!\n";
+
+ # All done. Rename the new file over top of the old file.
+ rename("$file.new", $file)
+ or die "$0: cannot rename $file.new to $file: $!\n";
+ return;
+}
+
+# Act as a test suite. Find all of the Perl modules under the provided root,
+# if any, and check that the version for each module matches the version.
+# Reports results with Test::More and sets up a plan based on the number of
+# modules found.
+#
+# $root - Directory under which to look for Perl modules
+# $version - The version all those modules should have
+#
+# Returns: undef
+# Throws: Text exception on fatal errors
+sub test_module_versions {
+ my ($root, $version) = @_;
+ my @modules = _module_files($root);
+
+ # Output the plan. Skip the test if there were no modules found.
+ if (@modules) {
+ plan tests => scalar(@modules);
+ } else {
+ plan skip_all => 'No Perl modules found';
+ return;
+ }
+
+ # For each module, get the module version and compare.
+ for my $module (@modules) {
+ my $module_version = _module_version($module);
+ is($module_version, $version, "Version for $module");
+ }
+ return;
+}
+
+# Update the versions of all modules to the current distribution version.
+#
+# $root - Directory under which to look for Perl modules
+# $version - The version all those modules should have
+#
+# Returns: undef
+# Throws: Text exception on fatal errors
+sub update_module_versions {
+ my ($root, $version) = @_;
+ my @modules = _module_files($root);
+ for my $module (@modules) {
+ _update_module_version($module, $version);
+ }
+ return;
+}
+
+1;
+__END__
+
+=for stopwords
+Allbery sublicense MERCHANTABILITY NONINFRINGEMENT rra-c-util versioning
+
+=head1 NAME
+
+Test::RRA::ModuleVersion - Check Perl module versions for consistency
+
+=head1 SYNOPSIS
+
+ use Test::RRA::ModuleVersion
+ qw(test_module_versions update_module_versions);
+
+ # Ensure all modules under perl/lib have a version of 3.12.
+ test_module_versions('perl/lib', '3.12');
+
+ # Update the version of those modules to 3.12.
+ update_module_versions('perl/lib', 3.12');
+
+=head1 DESCRIPTION
+
+This module provides functions to test and update the versions of Perl
+modules. It helps with enforcing consistency of versioning across all modules
+in a Perl distribution or embedded in a larger project containing non-Perl
+code. The calling script provides the version with which to be consistent
+and the root directory under which modules are found.
+
+=head1 FUNCTIONS
+
+None of these functions are imported by default. The ones used by a script
+should be explicitly imported.
+
+=over 4
+
+=item test_module_versions(ROOT, VERSION)
+
+Tests the version of all Perl modules under ROOT to ensure they match VERSION,
+reporting the results with Test::More. If the test configuration loaded by
+Test::RRA::Config contains a @MODULE_VERSION_EXCLUDE variable, the module
+files listed there will be ignored for this test. This function also sets up
+a plan based on the number of modules, so should be the only testing function
+called in a test script.
+
+=item update_module_versions(ROOT, VERSION)
+
+Update the version of all Perl modules found under ROOT to VERSION, except for
+any listed in a @MODULE_VERSION_EXCLUDE variable set in the test configuration
+loaded by Test::RRA::Config.
+
+=back
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2016 Russ Allbery <eagle@eyrie.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+=head1 SEE ALSO
+
+Test::More(3), Test::RRA::Config(3)
+
+This module is maintained in the rra-c-util package. The current version
+is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>.
+
+=cut
diff --git a/tests/tap/process.c b/tests/tap/process.c
index 6461fb4..8c22324 100644
--- a/tests/tap/process.c
+++ b/tests/tap/process.c
@@ -47,8 +47,11 @@
# include <sys/select.h>
#endif
#include <sys/stat.h>
-#include <sys/time.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
#include <sys/wait.h>
+#include <time.h>
#include <tests/tap/basic.h>
#include <tests/tap/process.h>
@@ -229,6 +232,10 @@ process_free(struct process *process)
{
struct process **prev;
+ /* Do nothing if called with a NULL argument. */
+ if (process == NULL)
+ return;
+
/* Remove the process from the global list. */
prev = &processes;
while (*prev != NULL && *prev != process)
diff --git a/tests/tap/string.h b/tests/tap/string.h
index cc51945..d58f75d 100644
--- a/tests/tap/string.h
+++ b/tests/tap/string.h
@@ -42,7 +42,7 @@ BEGIN_DECLS
void basprintf(char **, const char *, ...)
__attribute__((__nonnull__, __format__(printf, 2, 3)));
void bvasprintf(char **, const char *, va_list)
- __attribute__((__nonnull__));
+ __attribute__((__nonnull__, __format__(printf, 2, 0)));
END_DECLS
diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c
index f60fa6a..1098314 100644
--- a/tests/util/messages-t.c
+++ b/tests/util/messages-t.c
@@ -5,7 +5,7 @@
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
*
* Written by Russ Allbery <eagle@eyrie.org>
- * Copyright 2002, 2004, 2005 Russ Allbery <eagle@eyrie.org>
+ * Copyright 2002, 2004, 2005, 2015 Russ Allbery <eagle@eyrie.org>
* Copyright 2009, 2010, 2011, 2012
* The Board of Trustees of the Leland Stanford Junior University
*
@@ -92,7 +92,8 @@ static void test11(void *data UNUSED) {
sysdie("fatal");
}
-static void log_msg(size_t len, const char *format, va_list args, int error) {
+static void __attribute__((__format__(printf, 2, 0)))
+log_msg(size_t len, const char *format, va_list args, int error) {
fprintf(stderr, "%lu %d ", (unsigned long) len, error);
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
diff --git a/tests/util/xmalloc-t b/tests/util/xmalloc-t
index d52c448..af604ed 100755
--- a/tests/util/xmalloc-t
+++ b/tests/util/xmalloc-t
@@ -99,46 +99,46 @@ ok_xmalloc "vasprintf large" 0 "" "v" "30000000" "0"
# We assume that there are enough miscellaneous allocations that an allocation
# exactly as large as the limit will always fail.
ok_xmalloc "malloc fail" 1 \
- "failed to malloc 30000000 bytes at xmalloc.c line 38" \
+ "failed to malloc 30000000 bytes at xmalloc.c line 41" \
"m" "30000000" "30000000"
ok_xmalloc "realloc fail" 1 \
- "failed to realloc 30000000 bytes at xmalloc.c line 66" \
+ "failed to realloc 30000000 bytes at xmalloc.c line 69" \
"r" "30000000" "30000000"
ok_xmalloc "reallocarray fail" 1 \
- "failed to reallocarray 30000000 bytes at xmalloc.c line 96" \
+ "failed to reallocarray 30000000 bytes at xmalloc.c line 99" \
"y" "30000000" "30000000"
ok_xmalloc "strdup fail" 1 \
- "failed to strdup 30000000 bytes at xmalloc.c line 127" \
+ "failed to strdup 30000000 bytes at xmalloc.c line 130" \
"s" "30000000" "30000000"
ok_xmalloc "strndup fail" 1 \
- "failed to strndup 30000000 bytes at xmalloc.c line 173" \
+ "failed to strndup 30000000 bytes at xmalloc.c line 176" \
"n" "30000000" "30000000"
ok_xmalloc "calloc fail" 1 \
- "failed to calloc 30000000 bytes at xmalloc.c line 197" \
+ "failed to calloc 30000000 bytes at xmalloc.c line 200" \
"c" "30000000" "30000000"
ok_xmalloc "asprintf fail" 1 \
- "failed to asprintf 30000000 bytes at xmalloc.c line 221" \
+ "failed to asprintf 30000000 bytes at xmalloc.c line 224" \
"a" "30000000" "30000000"
ok_xmalloc "vasprintf fail" 1 \
- "failed to vasprintf 30000000 bytes at xmalloc.c line 240" \
+ "failed to vasprintf 30000000 bytes at xmalloc.c line 243" \
"v" "30000000" "30000000"
# Check our custom error handler.
-ok_xmalloc "malloc custom" 1 "malloc 30000000 xmalloc.c 38" \
+ok_xmalloc "malloc custom" 1 "malloc 30000000 xmalloc.c 41" \
"M" "30000000" "30000000"
-ok_xmalloc "realloc custom" 1 "realloc 30000000 xmalloc.c 66" \
+ok_xmalloc "realloc custom" 1 "realloc 30000000 xmalloc.c 69" \
"R" "30000000" "30000000"
-ok_xmalloc "reallocarray custom" 1 "reallocarray 30000000 xmalloc.c 96" \
+ok_xmalloc "reallocarray custom" 1 "reallocarray 30000000 xmalloc.c 99" \
"Y" "30000000" "30000000"
-ok_xmalloc "strdup custom" 1 "strdup 30000000 xmalloc.c 127" \
+ok_xmalloc "strdup custom" 1 "strdup 30000000 xmalloc.c 130" \
"S" "30000000" "30000000"
-ok_xmalloc "strndup custom" 1 "strndup 30000000 xmalloc.c 173" \
+ok_xmalloc "strndup custom" 1 "strndup 30000000 xmalloc.c 176" \
"N" "30000000" "30000000"
-ok_xmalloc "calloc custom" 1 "calloc 30000000 xmalloc.c 197" \
+ok_xmalloc "calloc custom" 1 "calloc 30000000 xmalloc.c 200" \
"C" "30000000" "30000000"
-ok_xmalloc "asprintf custom" 1 "asprintf 30000000 xmalloc.c 221" \
+ok_xmalloc "asprintf custom" 1 "asprintf 30000000 xmalloc.c 224" \
"A" "30000000" "30000000"
-ok_xmalloc "vasprintf custom" 1 "vasprintf 30000000 xmalloc.c 240" \
+ok_xmalloc "vasprintf custom" 1 "vasprintf 30000000 xmalloc.c 243" \
"V" "30000000" "30000000"
# Check the smaller ones again just for grins.
diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c
index e222612..84ba081 100644
--- a/tests/util/xmalloc.c
+++ b/tests/util/xmalloc.c
@@ -34,7 +34,10 @@
#include <ctype.h>
#include <errno.h>
-#include <sys/time.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <time.h>
/* Linux requires sys/time.h be included before sys/resource.h. */
#include <sys/resource.h>
@@ -261,7 +264,7 @@ test_asprintf(size_t size)
/* Wrapper around vasprintf to do the va_list stuff. */
-static void
+static void __attribute__((__format__(printf, 2, 3)))
xvasprintf_wrapper(char **strp, const char *format, ...)
{
va_list args;
diff --git a/util/macros.h b/util/macros.h
index d071793..4a773a2 100644
--- a/util/macros.h
+++ b/util/macros.h
@@ -29,6 +29,10 @@
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)])
+/* Used to name the elements of the array passed to pipe. */
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
/* Used for unused parameters to silence gcc warnings. */
#define UNUSED __attribute__((__unused__))
diff --git a/util/messages.c b/util/messages.c
index a43d962..b5c2dba 100644
--- a/util/messages.c
+++ b/util/messages.c
@@ -225,7 +225,7 @@ message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err)
* This needs further attention on Windows. For example, it currently doesn't
* log the errno information.
*/
-static void
+static void __attribute__((__format__(printf, 3, 0)))
message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err)
{
char *buffer;
diff --git a/util/messages.h b/util/messages.h
index 8c731b7..cf91ba7 100644
--- a/util/messages.h
+++ b/util/messages.h
@@ -84,24 +84,25 @@ void message_handlers_reset(void);
* argument list, and the errno setting if any.
*/
void message_log_stdout(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_stderr(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_syslog_debug(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_syslog_info(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_syslog_notice(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_syslog_warning(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_syslog_err(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
void message_log_syslog_crit(size_t, const char *, va_list, int)
- __attribute__((__nonnull__));
+ __attribute__((__format__(printf, 2, 0), __nonnull__));
/* The type of a message handler. */
-typedef void (*message_handler_func)(size_t, const char *, va_list, int);
+typedef void (*message_handler_func)(size_t, const char *, va_list, int)
+ __attribute__((__format__(printf, 2, 0)));
/* If non-NULL, called before exit and its return value passed to exit. */
extern int (*message_fatal_cleanup)(void);
diff --git a/util/xmalloc.c b/util/xmalloc.c
index 721447a..7fb0405 100644
--- a/util/xmalloc.c
+++ b/util/xmalloc.c
@@ -12,7 +12,7 @@
* buffer = xmalloc(1024);
* xrealloc(buffer, 2048);
* free(buffer);
- * buffer = xcalloc(1024);
+ * buffer = xcalloc(1, 1024);
* free(buffer);
* buffer = xstrdup(string);
* free(buffer);
@@ -62,6 +62,7 @@
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
*
+ * Copyright 2015 Russ Allbery <eagle@eyrie.org>
* Copyright 2012, 2013, 2014
* The Board of Trustees of the Leland Stanford Junior University
* Copyright (c) 2004, 2005, 2006
@@ -258,6 +259,7 @@ x_asprintf(char **strp, const char *file, int line, const char *fmt, ...)
status = vasprintf(strp, fmt, args_copy);
va_end(args_copy);
}
+ va_end(args);
}
#else /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */
void
@@ -280,5 +282,6 @@ x_asprintf(char **strp, const char *fmt, ...)
status = vasprintf(strp, fmt, args_copy);
va_end(args_copy);
}
+ va_end(args);
}
#endif /* !(HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS) */
diff --git a/util/xmalloc.h b/util/xmalloc.h
index a4b4686..6aa9b93 100644
--- a/util/xmalloc.h
+++ b/util/xmalloc.h
@@ -90,7 +90,7 @@ char *x_strdup(const char *, const char *, int)
char *x_strndup(const char *, size_t, const char *, int)
__attribute__((__malloc__, __nonnull__));
void x_vasprintf(char **, const char *, va_list, const char *, int)
- __attribute__((__nonnull__));
+ __attribute__((__nonnull__, __format__(printf, 2, 0)));
/* asprintf special case. */
#if HAVE_C99_VAMACROS || HAVE_GNU_VAMACROS