Compare commits
38 commits
main
...
646005f031
Author | SHA1 | Date | |
---|---|---|---|
|
646005f031 | ||
|
4be13a4e6c | ||
|
c420e0ac6a | ||
|
6ecf20bc57 | ||
|
aec4e8ad16 | ||
|
335463a9e2 | ||
|
d426d89488 | ||
|
4860733e29 | ||
|
9ce6a3fc2c | ||
|
204469e6c6 | ||
|
c5252af6a8 | ||
|
debbcc1b75 | ||
|
abee687e7a | ||
|
e86565c553 | ||
|
e30d116c36 | ||
|
6b947dd00a | ||
|
3f96e13f70 | ||
|
9700f8606d | ||
|
918f37acdc | ||
|
a27043ec13 | ||
|
18ca38b81c | ||
|
590fc39338 | ||
|
9f736ec8f4 | ||
|
ed0929dcc0 | ||
|
89f5473b9d | ||
|
bdb75a6c92 | ||
|
79670fe2fb | ||
|
4685f783f4 | ||
|
c87d7f0ff0 | ||
|
efb78b5569 | ||
|
00df607198 | ||
|
c3cacd763a | ||
|
05d3310d7e | ||
|
e2fcd142ce | ||
|
60d3d1dae7 | ||
|
83162901d4 | ||
|
fd3a018fbf | ||
|
efcf4a9ec6 |
78 changed files with 1255 additions and 14981 deletions
11
CREDITS
11
CREDITS
|
@ -15,23 +15,27 @@ directly or indirectly, with patches, criticism, suggestions, or
|
|||
ideas:
|
||||
|
||||
Andrew Morgan <morgan@transmeta.com>
|
||||
Ankita Pal <pal.ankita.ankita@gmail.com>
|
||||
Baptiste Daroussin <bapt@freebsd.org>
|
||||
Brian Fundakowski Feldman <green@freebsd.org>
|
||||
Christos Zoulas <christos@netbsd.org>
|
||||
Daniel Richard G. <skunk@iskunk.org>
|
||||
Darren J. Moffat <darren.moffat@sun.com>
|
||||
Dimitry Andric <dim@freebsd.org>
|
||||
Dmitry V. Levin <ldv@altlinux.org>
|
||||
Don Lewis <truckman@freebsd.org>
|
||||
Emmanuel Dreyfus <manu@netbsd.org>
|
||||
Eric Melville <eric@freebsd.org>
|
||||
Espen Grøndahl <espegro@usit.uio.no>
|
||||
Gary Winiger <gary.winiger@sun.com>
|
||||
Gavin Atkinson <gavin@freebsd.org>
|
||||
Gleb Smirnoff <glebius@freebsd.org>
|
||||
Hubert Feyrer <hubert@feyrer.de>
|
||||
Jason Evans <jasone@freebsd.org>
|
||||
Joe Marcus Clarke <marcus@freebsd.org>
|
||||
Juli Mallett <jmallett@freebsd.org>
|
||||
Ankita Pal <pal.ankita.ankita@gmail.com>
|
||||
Jörg Sonnenberger <joerg@britannica.bec.de>
|
||||
Juli Mallett <jmallett@freebsd.org>
|
||||
Larry Baird <lab@gta.com>
|
||||
Maëlle Lesage <lesage.maelle@gmail.com>
|
||||
Mark Murray <markm@freebsd.org>
|
||||
Matthias Drochner <drochner@netbsd.org>
|
||||
|
@ -39,6 +43,7 @@ ideas:
|
|||
Mikhail Teterin <mi@aldan.algebra.com>
|
||||
Mikko Työläjärvi <mbsd@pacbell.net>
|
||||
Nick Hibma <nick@van-laarhoven.org>
|
||||
Patrick Bihan-Faou <patrick-fbsd@mindstep.com>
|
||||
Robert Watson <rwatson@freebsd.org>
|
||||
Ruslan Ermilov <ru@freebsd.org>
|
||||
Sebastian Krahmer <sebastian.krahmer@gmail.com>
|
||||
|
@ -46,5 +51,3 @@ ideas:
|
|||
Takanori Saneto <sanewo@ba2.so-net.ne.jp>
|
||||
Wojciech A. Koszek <wkoszek@freebsd.org>
|
||||
Yar Tikhiy <yar@freebsd.org>
|
||||
|
||||
$Id$
|
||||
|
|
28
HISTORY
28
HISTORY
|
@ -1,7 +1,25 @@
|
|||
OpenPAM ?????????? 2013-??-??
|
||||
OpenPAM Ourouparia 2014-09-12
|
||||
|
||||
- FEATURE: Add a pam_oath module that implements RFC 4226 (HOTP) and
|
||||
RFC 6238 (TOTP).
|
||||
- ENHANCE: When executing a chain, require at least one service
|
||||
function to succeed. This mitigates fail-open scenarios caused by
|
||||
misconfigurations or missing modules.
|
||||
|
||||
- ENHANCE: Make sure to overwrite buffers which may have contained an
|
||||
authentication token when they're no longer needed.
|
||||
|
||||
- BUGFIX: Under certain circumstances, specifying a non-existent
|
||||
module (or misspelling the name of a module) in a policy could
|
||||
result in a fail-open scenario. (CVE-2014-3879)
|
||||
|
||||
- FEATURE: Add a search path for modules. This was implemented in
|
||||
Nummularia but inadvertently left out of the release notes.
|
||||
|
||||
- BUGFIX: The is_upper() predicate only accepted the letter A as an
|
||||
upper-case character instead of the entire A-Z range. As a result,
|
||||
service and module names containing upper-case letters other than A
|
||||
would be rejected.
|
||||
============================================================================
|
||||
OpenPAM Nummularia 2013-09-07
|
||||
|
||||
- ENHANCE: Rewrite the dynamic loader to improve readability and
|
||||
reliability. Modules can now be listed without the ".so" suffix in
|
||||
|
@ -100,7 +118,7 @@ OpenPAM Lycopsida 2011-12-18
|
|||
module before loading it.
|
||||
|
||||
- ENHANCE: added / improved input validation in many cases, including
|
||||
the policy file and some function arguments.
|
||||
the policy file and some function arguments. (CVE-2011-4122)
|
||||
============================================================================
|
||||
OpenPAM Hydrangea 2007-12-21
|
||||
|
||||
|
@ -430,5 +448,3 @@ Fixed a number of bugs in the previous release, including:
|
|||
OpenPAM Calamite 2002-02-09
|
||||
|
||||
First (beta) release.
|
||||
============================================================================
|
||||
$Id$
|
||||
|
|
2
INSTALL
2
INSTALL
|
@ -54,5 +54,3 @@
|
|||
directory:
|
||||
|
||||
# make install
|
||||
|
||||
$Id$
|
||||
|
|
4
LICENSE
4
LICENSE
|
@ -1,6 +1,6 @@
|
|||
|
||||
Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
Copyright (c) 2004-2012 Dag-Erling Smørgrav
|
||||
Copyright (c) 2004-2017 Dag-Erling Smørgrav
|
||||
All rights reserved.
|
||||
|
||||
This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -31,5 +31,3 @@ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
$Id$
|
||||
|
|
17
README
17
README
|
@ -7,21 +7,4 @@ implementations disagree, OpenPAM tries to remain compatible with
|
|||
Solaris, at the expense of XSSO conformance and Linux-PAM
|
||||
compatibility.
|
||||
|
||||
These are some of OpenPAM's features:
|
||||
|
||||
- Implements the complete PAM API as described in the original PAM
|
||||
paper and in OSF-RFC 86.0; this corresponds to the full XSSO API
|
||||
except for mappings and secondary authentication. Also
|
||||
implements some extensions found in Solaris 9.
|
||||
|
||||
- Extends the API with several useful and time-saving functions.
|
||||
|
||||
- Performs strict checking of return values from service modules.
|
||||
|
||||
- Reads configuration from /etc/pam.d/, /etc/pam.conf,
|
||||
/usr/local/etc/pam.d/ and /usr/local/etc/pam.conf, in that order;
|
||||
this will be made configurable in a future release.
|
||||
|
||||
Please direct bug reports and inquiries to <des@des.no>.
|
||||
|
||||
$Id$
|
||||
|
|
11
RELNOTES
11
RELNOTES
|
@ -1,6 +1,6 @@
|
|||
|
||||
Release notes for OpenPAM ????????
|
||||
==================================
|
||||
Release notes for OpenPAM Ourouparia
|
||||
====================================
|
||||
|
||||
This release corresponds to the code used in FreeBSD HEAD as of the
|
||||
release date, and is also expected to work on almost any POSIX-like
|
||||
|
@ -17,13 +17,6 @@ The distribution consists of the following components:
|
|||
- A test application (pamtest) which can be used to test policies and
|
||||
modules.
|
||||
|
||||
- A library which implements the OATH one-time password algorithms,
|
||||
with complete API documentation.
|
||||
|
||||
- A PAM module which implements OATH-based authentication.
|
||||
|
||||
- Unit tests for limited portions of the libraries.
|
||||
|
||||
Please direct bug reports and inquiries to <des@des.no>.
|
||||
|
||||
$Id$
|
||||
|
|
10
TODO
10
TODO
|
@ -1,8 +1,5 @@
|
|||
Before the next release:
|
||||
|
||||
- Add oath_alloc_secure() which allocates memory using mmap() +
|
||||
mlock() and oath_free_secure() which wipes and frees it.
|
||||
|
||||
- Rewrite openpam_ttyconv(3).
|
||||
- mostly done, needs review.
|
||||
|
||||
|
@ -10,8 +7,9 @@ Before the next release:
|
|||
documentation are slightly incorrect, OpenPAM's pam_unix(8) is
|
||||
incorrect, all FreeBSD modules are broken)
|
||||
|
||||
- Finish pam_oath(8) and oathkey(1).
|
||||
|
||||
- Add loop detection to openpam_load_chain().
|
||||
|
||||
$Id$
|
||||
- Look into the possibility of implementing a version of (or a
|
||||
wrapper for) openpam_log() which respects the PAM_SILENT flag and
|
||||
the no_warn module option. This would eliminate the need for
|
||||
FreeBSD's _pam_verbose_error().
|
||||
|
|
14
autogen.des
14
autogen.des
|
@ -10,8 +10,20 @@ set -ex
|
|||
# autoconf prior to 2.62 has issues with zsh 4.2 and newer
|
||||
export CONFIG_SHELL=/bin/sh
|
||||
|
||||
# BullseyeCoverage needs to know exactly which compiler we're using
|
||||
if [ -z "$CC" -a -z "$CPP" -a -z "$CXX" ] ; then
|
||||
if $(which clang clang++ >/dev/null) ; then
|
||||
export CC=${CC:-clang}
|
||||
export CPP=${CPP:-clang -E}
|
||||
export CXX=${CXX:-clang++}
|
||||
elif $(which gcc g++ >/dev/null) ; then
|
||||
export CC=${CC:-gcc}
|
||||
export CPP=${CPP:-gcc -E}
|
||||
export CXX=${CXX:-g++}
|
||||
fi
|
||||
fi
|
||||
|
||||
./configure \
|
||||
--with-oath \
|
||||
--with-doc \
|
||||
--with-pam-unix \
|
||||
--with-pamtest \
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
# $Id$
|
||||
#
|
||||
|
||||
aclocal -I m4
|
||||
libtoolize --copy --force
|
||||
aclocal -I m4
|
||||
autoheader
|
||||
automake -a -c --foreign
|
||||
automake --add-missing --copy --foreign
|
||||
autoconf
|
||||
|
|
|
@ -4,4 +4,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/libpam
|
|||
|
||||
noinst_PROGRAMS = openpam_dump_policy
|
||||
openpam_dump_policy_SOURCES = openpam_dump_policy.c
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
openpam_dump_policy_LDADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
openpam_dump_policy_LDADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2011-2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -64,7 +64,7 @@ openpam_facility_index_name(pam_facility_t fclt)
|
|||
if (asprintf(&name, "PAM_%s", facility) == -1)
|
||||
return (NULL);
|
||||
for (p = name + 4; *p; ++p)
|
||||
*p = toupper(*p);
|
||||
*p = toupper((unsigned char)*p);
|
||||
return (name);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||
|
||||
bin_PROGRAMS = pamtest
|
||||
pamtest_SOURCES = pamtest.c
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
pamtest_LDADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
pamtest_LDADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
||||
dist_man1_MANS = pamtest.1
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
.Sh NAME
|
||||
.Nm pamtest
|
||||
.Nd PAM policy tester
|
||||
.Sh SYNOPSYS
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl dkMPsv
|
||||
.Op Fl H Ar rhost
|
||||
|
|
|
@ -4,6 +4,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||
|
||||
bin_PROGRAMS = su
|
||||
su_SOURCES = su.c
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
su_LDADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
su_LDADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
||||
dist_man1_MANS = su.1
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
.Sh NAME
|
||||
.Nm su
|
||||
.Nd switch user identity
|
||||
.Sh SYNOPSYS
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Ar login Op Ar ...
|
||||
.Sh DESCRIPTION
|
||||
|
|
35
configure.ac
35
configure.ac
|
@ -2,7 +2,7 @@ dnl $Id$
|
|||
|
||||
AC_PREREQ([2.62])
|
||||
AC_REVISION([$Id$])
|
||||
AC_INIT([OpenPAM], [trunk], [des@des.no], [openpam], [http://www.openpam.org/])
|
||||
AC_INIT([OpenPAM], [nooath], [des@des.no], [openpam], [http://www.openpam.org/])
|
||||
AC_CONFIG_SRCDIR([lib/libpam/pam_start.c])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
@ -62,36 +62,36 @@ AC_ARG_WITH([doc],
|
|||
AM_CONDITIONAL([WITH_DOC], [test x"$with_doc" = x"yes"])
|
||||
|
||||
AC_ARG_WITH([pam-unix],
|
||||
AC_HELP_STRING([--with-pam-unix], [compile sample pam_unix(8) module]),
|
||||
AC_HELP_STRING([--with-pam-unix], [build sample pam_unix(8) module]),
|
||||
[],
|
||||
[with_pam_unix=no])
|
||||
AM_CONDITIONAL([WITH_PAM_UNIX], [test x"$with_pam_unix" = x"yes"])
|
||||
|
||||
AC_ARG_WITH([oath],
|
||||
AC_HELP_STRING([--with-oath], [compile OATH library, module and utility]),
|
||||
[],
|
||||
[with_oath=no])
|
||||
AM_CONDITIONAL([WITH_OATH], [test x"$with_oath" = x"yes"])
|
||||
|
||||
AC_ARG_WITH(pamtest,
|
||||
AC_HELP_STRING([--with-pamtest], [compile test application]),
|
||||
AC_HELP_STRING([--with-pamtest], [build test application]),
|
||||
[],
|
||||
[with_pamtest=no])
|
||||
AM_CONDITIONAL([WITH_PAMTEST], [test x"$with_pamtest" = x"yes"])
|
||||
|
||||
AC_ARG_WITH(su,
|
||||
AC_HELP_STRING([--with-su], [compile sample su(1) implementation]),
|
||||
AC_HELP_STRING([--with-su], [build sample su(1) implementation]),
|
||||
[],
|
||||
[with_su=no])
|
||||
AM_CONDITIONAL([WITH_SU], [test x"$with_su" = x"yes"])
|
||||
|
||||
AC_ARG_WITH(system-libpam,
|
||||
AC_HELP_STRING([--with-system-libpam], [use system libpam]),
|
||||
[],
|
||||
[with_system_libpam=no])
|
||||
AM_CONDITIONAL([WITH_SYSTEM_LIBPAM], [test x"$with_system_libpam" = x"yes"])
|
||||
|
||||
AC_CHECK_HEADERS([crypt.h])
|
||||
|
||||
AC_CHECK_FUNCS([asprintf vasprintf])
|
||||
AC_CHECK_FUNCS([dlfunc fdlopen])
|
||||
AC_CHECK_FUNCS([fpurge])
|
||||
AC_CHECK_FUNCS([setlogmask])
|
||||
AC_CHECK_FUNCS([strlcat strlcmp strlcpy])
|
||||
AC_CHECK_FUNCS([strlcat strlcmp strlcpy strlset])
|
||||
|
||||
saved_LIBS="${LIBS}"
|
||||
LIBS=""
|
||||
|
@ -114,9 +114,16 @@ CRYPTO_LIBS="${LIBS}"
|
|||
LIBS="${saved_LIBS}"
|
||||
AC_SUBST(CRYPTO_LIBS)
|
||||
|
||||
saved_LIBS="${LIBS}"
|
||||
LIBS=""
|
||||
AC_CHECK_LIB([pam], [pam_start])
|
||||
SYSTEM_LIBPAM="${LIBS}"
|
||||
LIBS="${saved_LIBS}"
|
||||
AC_SUBST(SYSTEM_LIBPAM)
|
||||
|
||||
AC_ARG_ENABLE([developer-warnings],
|
||||
AS_HELP_STRING([--enable-developer-warnings], [enable strict warnings (default is NO)]),
|
||||
[CFLAGS="${CFLAGS} -Wall -Wextra"])
|
||||
[CFLAGS="${CFLAGS} -Wall -Wextra -Wcast-qual"])
|
||||
AC_ARG_ENABLE([debugging-symbols],
|
||||
AS_HELP_STRING([--enable-debugging-symbols], [enable debugging symbols (default is NO)]),
|
||||
[CFLAGS="${CFLAGS} -O0 -g -fno-inline"])
|
||||
|
@ -135,15 +142,13 @@ AC_CONFIG_FILES([
|
|||
include/Makefile
|
||||
include/security/Makefile
|
||||
lib/Makefile
|
||||
lib/liboath/Makefile
|
||||
lib/libpam/Makefile
|
||||
modules/Makefile
|
||||
modules/pam_deny/Makefile
|
||||
modules/pam_permit/Makefile
|
||||
modules/pam_return/Makefile
|
||||
modules/pam_unix/Makefile
|
||||
modules/pam_oath/Makefile
|
||||
t/Makefile
|
||||
])
|
||||
AC_CONFIG_FILES([pamgdb],[chmod +x pamgdb])
|
||||
AC_CONFIG_FILES([mkpkgng],[chmod +x mkpkgng])
|
||||
AC_OUTPUT
|
||||
|
|
|
@ -64,12 +64,13 @@ OMAN = \
|
|||
EXTRA_DIST = openpam.man pam.man
|
||||
|
||||
ALLCMAN = $(PMAN) $(MMAN) $(OMAN)
|
||||
GENMAN = $(ALLCMAN) openpam.3 pam.3
|
||||
|
||||
dist_man3_MANS = $(ALLCMAN) openpam.3 pam.3 pam_conv.3
|
||||
dist_man3_MANS = $(GENMAN) pam_conv.3
|
||||
|
||||
dist_man5_MANS = pam.conf.5
|
||||
|
||||
CLEANFILES = $(ALLCMAN) openpam.3 pam.3
|
||||
CLEANFILES = $(GENMAN)
|
||||
|
||||
GENDOC = $(top_srcdir)/misc/gendoc.pl
|
||||
|
||||
|
@ -80,10 +81,12 @@ VPATH = $(LIBSRCDIR) $(srcdir)
|
|||
SUFFIXES = .3
|
||||
|
||||
.c.3: $(GENDOC)
|
||||
perl -w $(GENDOC) $<
|
||||
perl -w $(GENDOC) $< || rm $@
|
||||
|
||||
openpam.3: $(OMAN) $(GENDOC) $(srcdir)/openpam.man
|
||||
perl -w $(GENDOC) -o $(abs_srcdir)/$(OMAN) <$(srcdir)/openpam.man
|
||||
openpam.3: $(OPENPAM_MAN) $(GENDOC) $(srcdir)/openpam.man
|
||||
perl -w $(GENDOC) -o $(OPENPAM_MAN) <$(srcdir)/openpam.man || rm $@
|
||||
|
||||
pam.3: $(PMAN) $(GENDOC) $(srcdir)/pam.man
|
||||
perl -w $(GENDOC) -p $(abs_srcdir)/$(PMAN) <$(srcdir)/pam.man
|
||||
pam.3: $(PAM_MAN) $(GENDOC) $(srcdir)/pam.man
|
||||
perl -w $(GENDOC) -p $(PAM_MAN) <$(srcdir)/pam.man || rm $@
|
||||
|
||||
$(GENMAN): $(GENDOC)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\"-
|
||||
.\" Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
.\" Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
.\" Copyright (c) 2004-2014 Dag-Erling Smørgrav
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -76,7 +76,7 @@ item.
|
|||
.Pp
|
||||
The conversation function's first argument specifies the number of
|
||||
messages (up to
|
||||
.Dv PAM_NUM_MSG )
|
||||
.Dv PAM_MAX_NUM_MSG )
|
||||
to process.
|
||||
The second argument is a pointer to an array of pointers to
|
||||
.Vt pam_message
|
||||
|
|
1179
doc/rfc/rfc1321.txt
1179
doc/rfc/rfc1321.txt
File diff suppressed because it is too large
Load diff
|
@ -1,619 +0,0 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group H. Krawczyk
|
||||
Request for Comments: 2104 IBM
|
||||
Category: Informational M. Bellare
|
||||
UCSD
|
||||
R. Canetti
|
||||
IBM
|
||||
February 1997
|
||||
|
||||
|
||||
HMAC: Keyed-Hashing for Message Authentication
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This memo provides information for the Internet community. This memo
|
||||
does not specify an Internet standard of any kind. Distribution of
|
||||
this memo is unlimited.
|
||||
|
||||
Abstract
|
||||
|
||||
This document describes HMAC, a mechanism for message authentication
|
||||
using cryptographic hash functions. HMAC can be used with any
|
||||
iterative cryptographic hash function, e.g., MD5, SHA-1, in
|
||||
combination with a secret shared key. The cryptographic strength of
|
||||
HMAC depends on the properties of the underlying hash function.
|
||||
|
||||
1. Introduction
|
||||
|
||||
Providing a way to check the integrity of information transmitted
|
||||
over or stored in an unreliable medium is a prime necessity in the
|
||||
world of open computing and communications. Mechanisms that provide
|
||||
such integrity check based on a secret key are usually called
|
||||
"message authentication codes" (MAC). Typically, message
|
||||
authentication codes are used between two parties that share a secret
|
||||
key in order to validate information transmitted between these
|
||||
parties. In this document we present such a MAC mechanism based on
|
||||
cryptographic hash functions. This mechanism, called HMAC, is based
|
||||
on work by the authors [BCK1] where the construction is presented and
|
||||
cryptographically analyzed. We refer to that work for the details on
|
||||
the rationale and security analysis of HMAC, and its comparison to
|
||||
other keyed-hash methods.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 1]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
HMAC can be used in combination with any iterated cryptographic hash
|
||||
function. MD5 and SHA-1 are examples of such hash functions. HMAC
|
||||
also uses a secret key for calculation and verification of the
|
||||
message authentication values. The main goals behind this
|
||||
construction are
|
||||
|
||||
* To use, without modifications, available hash functions.
|
||||
In particular, hash functions that perform well in software,
|
||||
and for which code is freely and widely available.
|
||||
|
||||
* To preserve the original performance of the hash function without
|
||||
incurring a significant degradation.
|
||||
|
||||
* To use and handle keys in a simple way.
|
||||
|
||||
* To have a well understood cryptographic analysis of the strength of
|
||||
the authentication mechanism based on reasonable assumptions on the
|
||||
underlying hash function.
|
||||
|
||||
* To allow for easy replaceability of the underlying hash function in
|
||||
case that faster or more secure hash functions are found or
|
||||
required.
|
||||
|
||||
This document specifies HMAC using a generic cryptographic hash
|
||||
function (denoted by H). Specific instantiations of HMAC need to
|
||||
define a particular hash function. Current candidates for such hash
|
||||
functions include SHA-1 [SHA], MD5 [MD5], RIPEMD-128/160 [RIPEMD].
|
||||
These different realizations of HMAC will be denoted by HMAC-SHA1,
|
||||
HMAC-MD5, HMAC-RIPEMD, etc.
|
||||
|
||||
Note: To the date of writing of this document MD5 and SHA-1 are the
|
||||
most widely used cryptographic hash functions. MD5 has been recently
|
||||
shown to be vulnerable to collision search attacks [Dobb]. This
|
||||
attack and other currently known weaknesses of MD5 do not compromise
|
||||
the use of MD5 within HMAC as specified in this document (see
|
||||
[Dobb]); however, SHA-1 appears to be a cryptographically stronger
|
||||
function. To this date, MD5 can be considered for use in HMAC for
|
||||
applications where the superior performance of MD5 is critical. In
|
||||
any case, implementers and users need to be aware of possible
|
||||
cryptanalytic developments regarding any of these cryptographic hash
|
||||
functions, and the eventual need to replace the underlying hash
|
||||
function. (See section 6 for more information on the security of
|
||||
HMAC.)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 2]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
2. Definition of HMAC
|
||||
|
||||
The definition of HMAC requires a cryptographic hash function, which
|
||||
we denote by H, and a secret key K. We assume H to be a cryptographic
|
||||
hash function where data is hashed by iterating a basic compression
|
||||
function on blocks of data. We denote by B the byte-length of such
|
||||
blocks (B=64 for all the above mentioned examples of hash functions),
|
||||
and by L the byte-length of hash outputs (L=16 for MD5, L=20 for
|
||||
SHA-1). The authentication key K can be of any length up to B, the
|
||||
block length of the hash function. Applications that use keys longer
|
||||
than B bytes will first hash the key using H and then use the
|
||||
resultant L byte string as the actual key to HMAC. In any case the
|
||||
minimal recommended length for K is L bytes (as the hash output
|
||||
length). See section 3 for more information on keys.
|
||||
|
||||
We define two fixed and different strings ipad and opad as follows
|
||||
(the 'i' and 'o' are mnemonics for inner and outer):
|
||||
|
||||
ipad = the byte 0x36 repeated B times
|
||||
opad = the byte 0x5C repeated B times.
|
||||
|
||||
To compute HMAC over the data `text' we perform
|
||||
|
||||
H(K XOR opad, H(K XOR ipad, text))
|
||||
|
||||
Namely,
|
||||
|
||||
(1) append zeros to the end of K to create a B byte string
|
||||
(e.g., if K is of length 20 bytes and B=64, then K will be
|
||||
appended with 44 zero bytes 0x00)
|
||||
(2) XOR (bitwise exclusive-OR) the B byte string computed in step
|
||||
(1) with ipad
|
||||
(3) append the stream of data 'text' to the B byte string resulting
|
||||
from step (2)
|
||||
(4) apply H to the stream generated in step (3)
|
||||
(5) XOR (bitwise exclusive-OR) the B byte string computed in
|
||||
step (1) with opad
|
||||
(6) append the H result from step (4) to the B byte string
|
||||
resulting from step (5)
|
||||
(7) apply H to the stream generated in step (6) and output
|
||||
the result
|
||||
|
||||
For illustration purposes, sample code based on MD5 is provided as an
|
||||
appendix.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 3]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
3. Keys
|
||||
|
||||
The key for HMAC can be of any length (keys longer than B bytes are
|
||||
first hashed using H). However, less than L bytes is strongly
|
||||
discouraged as it would decrease the security strength of the
|
||||
function. Keys longer than L bytes are acceptable but the extra
|
||||
length would not significantly increase the function strength. (A
|
||||
longer key may be advisable if the randomness of the key is
|
||||
considered weak.)
|
||||
|
||||
Keys need to be chosen at random (or using a cryptographically strong
|
||||
pseudo-random generator seeded with a random seed), and periodically
|
||||
refreshed. (Current attacks do not indicate a specific recommended
|
||||
frequency for key changes as these attacks are practically
|
||||
infeasible. However, periodic key refreshment is a fundamental
|
||||
security practice that helps against potential weaknesses of the
|
||||
function and keys, and limits the damage of an exposed key.)
|
||||
|
||||
4. Implementation Note
|
||||
|
||||
HMAC is defined in such a way that the underlying hash function H can
|
||||
be used with no modification to its code. In particular, it uses the
|
||||
function H with the pre-defined initial value IV (a fixed value
|
||||
specified by each iterative hash function to initialize its
|
||||
compression function). However, if desired, a performance
|
||||
improvement can be achieved at the cost of (possibly) modifying the
|
||||
code of H to support variable IVs.
|
||||
|
||||
The idea is that the intermediate results of the compression function
|
||||
on the B-byte blocks (K XOR ipad) and (K XOR opad) can be precomputed
|
||||
only once at the time of generation of the key K, or before its first
|
||||
use. These intermediate results are stored and then used to
|
||||
initialize the IV of H each time that a message needs to be
|
||||
authenticated. This method saves, for each authenticated message,
|
||||
the application of the compression function of H on two B-byte blocks
|
||||
(i.e., on (K XOR ipad) and (K XOR opad)). Such a savings may be
|
||||
significant when authenticating short streams of data. We stress
|
||||
that the stored intermediate values need to be treated and protected
|
||||
the same as secret keys.
|
||||
|
||||
Choosing to implement HMAC in the above way is a decision of the
|
||||
local implementation and has no effect on inter-operability.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 4]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
5. Truncated output
|
||||
|
||||
A well-known practice with message authentication codes is to
|
||||
truncate the output of the MAC and output only part of the bits
|
||||
(e.g., [MM, ANSI]). Preneel and van Oorschot [PV] show some
|
||||
analytical advantages of truncating the output of hash-based MAC
|
||||
functions. The results in this area are not absolute as for the
|
||||
overall security advantages of truncation. It has advantages (less
|
||||
information on the hash result available to an attacker) and
|
||||
disadvantages (less bits to predict for the attacker). Applications
|
||||
of HMAC can choose to truncate the output of HMAC by outputting the t
|
||||
leftmost bits of the HMAC computation for some parameter t (namely,
|
||||
the computation is carried in the normal way as defined in section 2
|
||||
above but the end result is truncated to t bits). We recommend that
|
||||
the output length t be not less than half the length of the hash
|
||||
output (to match the birthday attack bound) and not less than 80 bits
|
||||
(a suitable lower bound on the number of bits that need to be
|
||||
predicted by an attacker). We propose denoting a realization of HMAC
|
||||
that uses a hash function H with t bits of output as HMAC-H-t. For
|
||||
example, HMAC-SHA1-80 denotes HMAC computed using the SHA-1 function
|
||||
and with the output truncated to 80 bits. (If the parameter t is not
|
||||
specified, e.g. HMAC-MD5, then it is assumed that all the bits of the
|
||||
hash are output.)
|
||||
|
||||
6. Security
|
||||
|
||||
The security of the message authentication mechanism presented here
|
||||
depends on cryptographic properties of the hash function H: the
|
||||
resistance to collision finding (limited to the case where the
|
||||
initial value is secret and random, and where the output of the
|
||||
function is not explicitly available to the attacker), and the
|
||||
message authentication property of the compression function of H when
|
||||
applied to single blocks (in HMAC these blocks are partially unknown
|
||||
to an attacker as they contain the result of the inner H computation
|
||||
and, in particular, cannot be fully chosen by the attacker).
|
||||
|
||||
These properties, and actually stronger ones, are commonly assumed
|
||||
for hash functions of the kind used with HMAC. In particular, a hash
|
||||
function for which the above properties do not hold would become
|
||||
unsuitable for most (probably, all) cryptographic applications,
|
||||
including alternative message authentication schemes based on such
|
||||
functions. (For a complete analysis and rationale of the HMAC
|
||||
function the reader is referred to [BCK1].)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 5]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
Given the limited confidence gained so far as for the cryptographic
|
||||
strength of candidate hash functions, it is important to observe the
|
||||
following two properties of the HMAC construction and its secure use
|
||||
for message authentication:
|
||||
|
||||
1. The construction is independent of the details of the particular
|
||||
hash function H in use and then the latter can be replaced by any
|
||||
other secure (iterative) cryptographic hash function.
|
||||
|
||||
2. Message authentication, as opposed to encryption, has a
|
||||
"transient" effect. A published breaking of a message authentication
|
||||
scheme would lead to the replacement of that scheme, but would have
|
||||
no adversarial effect on information authenticated in the past. This
|
||||
is in sharp contrast with encryption, where information encrypted
|
||||
today may suffer from exposure in the future if, and when, the
|
||||
encryption algorithm is broken.
|
||||
|
||||
The strongest attack known against HMAC is based on the frequency of
|
||||
collisions for the hash function H ("birthday attack") [PV,BCK2], and
|
||||
is totally impractical for minimally reasonable hash functions.
|
||||
|
||||
As an example, if we consider a hash function like MD5 where the
|
||||
output length equals L=16 bytes (128 bits) the attacker needs to
|
||||
acquire the correct message authentication tags computed (with the
|
||||
_same_ secret key K!) on about 2**64 known plaintexts. This would
|
||||
require the processing of at least 2**64 blocks under H, an
|
||||
impossible task in any realistic scenario (for a block length of 64
|
||||
bytes this would take 250,000 years in a continuous 1Gbps link, and
|
||||
without changing the secret key K during all this time). This attack
|
||||
could become realistic only if serious flaws in the collision
|
||||
behavior of the function H are discovered (e.g. collisions found
|
||||
after 2**30 messages). Such a discovery would determine the immediate
|
||||
replacement of the function H (the effects of such failure would be
|
||||
far more severe for the traditional uses of H in the context of
|
||||
digital signatures, public key certificates, etc.).
|
||||
|
||||
Note: this attack needs to be strongly contrasted with regular
|
||||
collision attacks on cryptographic hash functions where no secret key
|
||||
is involved and where 2**64 off-line parallelizable (!) operations
|
||||
suffice to find collisions. The latter attack is approaching
|
||||
feasibility [VW] while the birthday attack on HMAC is totally
|
||||
impractical. (In the above examples, if one uses a hash function
|
||||
with, say, 160 bit of output then 2**64 should be replaced by 2**80.)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 6]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
A correct implementation of the above construction, the choice of
|
||||
random (or cryptographically pseudorandom) keys, a secure key
|
||||
exchange mechanism, frequent key refreshments, and good secrecy
|
||||
protection of keys are all essential ingredients for the security of
|
||||
the integrity verification mechanism provided by HMAC.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 7]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
Appendix -- Sample Code
|
||||
|
||||
For the sake of illustration we provide the following sample code for
|
||||
the implementation of HMAC-MD5 as well as some corresponding test
|
||||
vectors (the code is based on MD5 code as described in [MD5]).
|
||||
|
||||
/*
|
||||
** Function: hmac_md5
|
||||
*/
|
||||
|
||||
void
|
||||
hmac_md5(text, text_len, key, key_len, digest)
|
||||
unsigned char* text; /* pointer to data stream */
|
||||
int text_len; /* length of data stream */
|
||||
unsigned char* key; /* pointer to authentication key */
|
||||
int key_len; /* length of authentication key */
|
||||
caddr_t digest; /* caller digest to be filled in */
|
||||
|
||||
{
|
||||
MD5_CTX context;
|
||||
unsigned char k_ipad[65]; /* inner padding -
|
||||
* key XORd with ipad
|
||||
*/
|
||||
unsigned char k_opad[65]; /* outer padding -
|
||||
* key XORd with opad
|
||||
*/
|
||||
unsigned char tk[16];
|
||||
int i;
|
||||
/* if key is longer than 64 bytes reset it to key=MD5(key) */
|
||||
if (key_len > 64) {
|
||||
|
||||
MD5_CTX tctx;
|
||||
|
||||
MD5Init(&tctx);
|
||||
MD5Update(&tctx, key, key_len);
|
||||
MD5Final(tk, &tctx);
|
||||
|
||||
key = tk;
|
||||
key_len = 16;
|
||||
}
|
||||
|
||||
/*
|
||||
* the HMAC_MD5 transform looks like:
|
||||
*
|
||||
* MD5(K XOR opad, MD5(K XOR ipad, text))
|
||||
*
|
||||
* where K is an n byte key
|
||||
* ipad is the byte 0x36 repeated 64 times
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 8]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
* opad is the byte 0x5c repeated 64 times
|
||||
* and text is the data being protected
|
||||
*/
|
||||
|
||||
/* start out by storing key in pads */
|
||||
bzero( k_ipad, sizeof k_ipad);
|
||||
bzero( k_opad, sizeof k_opad);
|
||||
bcopy( key, k_ipad, key_len);
|
||||
bcopy( key, k_opad, key_len);
|
||||
|
||||
/* XOR key with ipad and opad values */
|
||||
for (i=0; i<64; i++) {
|
||||
k_ipad[i] ^= 0x36;
|
||||
k_opad[i] ^= 0x5c;
|
||||
}
|
||||
/*
|
||||
* perform inner MD5
|
||||
*/
|
||||
MD5Init(&context); /* init context for 1st
|
||||
* pass */
|
||||
MD5Update(&context, k_ipad, 64) /* start with inner pad */
|
||||
MD5Update(&context, text, text_len); /* then text of datagram */
|
||||
MD5Final(digest, &context); /* finish up 1st pass */
|
||||
/*
|
||||
* perform outer MD5
|
||||
*/
|
||||
MD5Init(&context); /* init context for 2nd
|
||||
* pass */
|
||||
MD5Update(&context, k_opad, 64); /* start with outer pad */
|
||||
MD5Update(&context, digest, 16); /* then results of 1st
|
||||
* hash */
|
||||
MD5Final(digest, &context); /* finish up 2nd pass */
|
||||
}
|
||||
|
||||
Test Vectors (Trailing '\0' of a character string not included in test):
|
||||
|
||||
key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
|
||||
key_len = 16 bytes
|
||||
data = "Hi There"
|
||||
data_len = 8 bytes
|
||||
digest = 0x9294727a3638bb1c13f48ef8158bfc9d
|
||||
|
||||
key = "Jefe"
|
||||
data = "what do ya want for nothing?"
|
||||
data_len = 28 bytes
|
||||
digest = 0x750c783e6ab0b503eaa86e310a5db738
|
||||
|
||||
key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 9]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
key_len 16 bytes
|
||||
data = 0xDDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD...
|
||||
..DDDDDDDDDDDDDDDDDDDD
|
||||
data_len = 50 bytes
|
||||
digest = 0x56be34521d144c88dbb8c733f0e8b3f6
|
||||
|
||||
Acknowledgments
|
||||
|
||||
Pau-Chen Cheng, Jeff Kraemer, and Michael Oehler, have provided
|
||||
useful comments on early drafts, and ran the first interoperability
|
||||
tests of this specification. Jeff and Pau-Chen kindly provided the
|
||||
sample code and test vectors that appear in the appendix. Burt
|
||||
Kaliski, Bart Preneel, Matt Robshaw, Adi Shamir, and Paul van
|
||||
Oorschot have provided useful comments and suggestions during the
|
||||
investigation of the HMAC construction.
|
||||
|
||||
References
|
||||
|
||||
[ANSI] ANSI X9.9, "American National Standard for Financial
|
||||
Institution Message Authentication (Wholesale)," American
|
||||
Bankers Association, 1981. Revised 1986.
|
||||
|
||||
[Atk] Atkinson, R., "IP Authentication Header", RFC 1826, August
|
||||
1995.
|
||||
|
||||
[BCK1] M. Bellare, R. Canetti, and H. Krawczyk,
|
||||
"Keyed Hash Functions and Message Authentication",
|
||||
Proceedings of Crypto'96, LNCS 1109, pp. 1-15.
|
||||
(http://www.research.ibm.com/security/keyed-md5.html)
|
||||
|
||||
[BCK2] M. Bellare, R. Canetti, and H. Krawczyk,
|
||||
"Pseudorandom Functions Revisited: The Cascade Construction",
|
||||
Proceedings of FOCS'96.
|
||||
|
||||
[Dobb] H. Dobbertin, "The Status of MD5 After a Recent Attack",
|
||||
RSA Labs' CryptoBytes, Vol. 2 No. 2, Summer 1996.
|
||||
http://www.rsa.com/rsalabs/pubs/cryptobytes.html
|
||||
|
||||
[PV] B. Preneel and P. van Oorschot, "Building fast MACs from hash
|
||||
functions", Advances in Cryptology -- CRYPTO'95 Proceedings,
|
||||
Lecture Notes in Computer Science, Springer-Verlag Vol.963,
|
||||
1995, pp. 1-14.
|
||||
|
||||
[MD5] Rivest, R., "The MD5 Message-Digest Algorithm",
|
||||
RFC 1321, April 1992.
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 10]
|
||||
|
||||
RFC 2104 HMAC February 1997
|
||||
|
||||
|
||||
[MM] Meyer, S. and Matyas, S.M., Cryptography, New York Wiley,
|
||||
1982.
|
||||
|
||||
[RIPEMD] H. Dobbertin, A. Bosselaers, and B. Preneel, "RIPEMD-160: A
|
||||
strengthened version of RIPEMD", Fast Software Encryption,
|
||||
LNCS Vol 1039, pp. 71-82.
|
||||
ftp://ftp.esat.kuleuven.ac.be/pub/COSIC/bosselae/ripemd/.
|
||||
|
||||
[SHA] NIST, FIPS PUB 180-1: Secure Hash Standard, April 1995.
|
||||
|
||||
[Tsu] G. Tsudik, "Message authentication with one-way hash
|
||||
functions", In Proceedings of Infocom'92, May 1992.
|
||||
(Also in "Access Control and Policy Enforcement in
|
||||
Internetworks", Ph.D. Dissertation, Computer Science
|
||||
Department, University of Southern California, April 1991.)
|
||||
|
||||
[VW] P. van Oorschot and M. Wiener, "Parallel Collision
|
||||
Search with Applications to Hash Functions and Discrete
|
||||
Logarithms", Proceedings of the 2nd ACM Conf. Computer and
|
||||
Communications Security, Fairfax, VA, November 1994.
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Hugo Krawczyk
|
||||
IBM T.J. Watson Research Center
|
||||
P.O.Box 704
|
||||
Yorktown Heights, NY 10598
|
||||
|
||||
EMail: hugo@watson.ibm.com
|
||||
|
||||
Mihir Bellare
|
||||
Dept of Computer Science and Engineering
|
||||
Mail Code 0114
|
||||
University of California at San Diego
|
||||
9500 Gilman Drive
|
||||
La Jolla, CA 92093
|
||||
|
||||
EMail: mihir@cs.ucsd.edu
|
||||
|
||||
Ran Canetti
|
||||
IBM T.J. Watson Research Center
|
||||
P.O.Box 704
|
||||
Yorktown Heights, NY 10598
|
||||
|
||||
EMail: canetti@watson.ibm.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Krawczyk, et. al. Informational [Page 11]
|
||||
|
2075
doc/rfc/rfc4226.txt
2075
doc/rfc/rfc4226.txt
File diff suppressed because it is too large
Load diff
1011
doc/rfc/rfc4648.txt
1011
doc/rfc/rfc4648.txt
File diff suppressed because it is too large
Load diff
7115
doc/rfc/rfc6234.txt
7115
doc/rfc/rfc6234.txt
File diff suppressed because it is too large
Load diff
|
@ -1,899 +0,0 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Internet Engineering Task Force (IETF) D. M'Raihi
|
||||
Request for Comments: 6238 Verisign, Inc.
|
||||
Category: Informational S. Machani
|
||||
ISSN: 2070-1721 Diversinet Corp.
|
||||
M. Pei
|
||||
Symantec
|
||||
J. Rydell
|
||||
Portwise, Inc.
|
||||
May 2011
|
||||
|
||||
|
||||
TOTP: Time-Based One-Time Password Algorithm
|
||||
|
||||
Abstract
|
||||
|
||||
This document describes an extension of the One-Time Password (OTP)
|
||||
algorithm, namely the HMAC-based One-Time Password (HOTP) algorithm,
|
||||
as defined in RFC 4226, to support the time-based moving factor. The
|
||||
HOTP algorithm specifies an event-based OTP algorithm, where the
|
||||
moving factor is an event counter. The present work bases the moving
|
||||
factor on a time value. A time-based variant of the OTP algorithm
|
||||
provides short-lived OTP values, which are desirable for enhanced
|
||||
security.
|
||||
|
||||
The proposed algorithm can be used across a wide range of network
|
||||
applications, from remote Virtual Private Network (VPN) access and
|
||||
Wi-Fi network logon to transaction-oriented Web applications. The
|
||||
authors believe that a common and shared algorithm will facilitate
|
||||
adoption of two-factor authentication on the Internet by enabling
|
||||
interoperability across commercial and open-source implementations.
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This document is not an Internet Standards Track specification; it is
|
||||
published for informational purposes.
|
||||
|
||||
This document is a product of the Internet Engineering Task Force
|
||||
(IETF). It represents the consensus of the IETF community. It has
|
||||
received public review and has been approved for publication by the
|
||||
Internet Engineering Steering Group (IESG). Not all documents
|
||||
approved by the IESG are a candidate for any level of Internet
|
||||
Standard; see Section 2 of RFC 5741.
|
||||
|
||||
Information about the current status of this document, any errata,
|
||||
and how to provide feedback on it may be obtained at
|
||||
http://www.rfc-editor.org/info/rfc6238.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 1]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction ....................................................2
|
||||
1.1. Scope ......................................................2
|
||||
1.2. Background .................................................3
|
||||
2. Notation and Terminology ........................................3
|
||||
3. Algorithm Requirements ..........................................3
|
||||
4. TOTP Algorithm ..................................................4
|
||||
4.1. Notations ..................................................4
|
||||
4.2. Description ................................................4
|
||||
5. Security Considerations .........................................5
|
||||
5.1. General ....................................................5
|
||||
5.2. Validation and Time-Step Size ..............................6
|
||||
6. Resynchronization ...............................................7
|
||||
7. Acknowledgements ................................................7
|
||||
8. References ......................................................8
|
||||
8.1. Normative References .......................................8
|
||||
8.2. Informative References .....................................8
|
||||
Appendix A. TOTP Algorithm: Reference Implementation ...............9
|
||||
Appendix B. Test Vectors ..........................................14
|
||||
|
||||
1. Introduction
|
||||
|
||||
1.1. Scope
|
||||
|
||||
This document describes an extension of the One-Time Password (OTP)
|
||||
algorithm, namely the HMAC-based One-Time Password (HOTP) algorithm,
|
||||
as defined in [RFC4226], to support the time-based moving factor.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 2]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
1.2. Background
|
||||
|
||||
As defined in [RFC4226], the HOTP algorithm is based on the
|
||||
HMAC-SHA-1 algorithm (as specified in [RFC2104]) and applied to an
|
||||
increasing counter value representing the message in the HMAC
|
||||
computation.
|
||||
|
||||
Basically, the output of the HMAC-SHA-1 calculation is truncated to
|
||||
obtain user-friendly values:
|
||||
|
||||
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
|
||||
|
||||
where Truncate represents the function that can convert an HMAC-SHA-1
|
||||
value into an HOTP value. K and C represent the shared secret and
|
||||
counter value; see [RFC4226] for detailed definitions.
|
||||
|
||||
TOTP is the time-based variant of this algorithm, where a value T,
|
||||
derived from a time reference and a time step, replaces the counter C
|
||||
in the HOTP computation.
|
||||
|
||||
TOTP implementations MAY use HMAC-SHA-256 or HMAC-SHA-512 functions,
|
||||
based on SHA-256 or SHA-512 [SHA2] hash functions, instead of the
|
||||
HMAC-SHA-1 function that has been specified for the HOTP computation
|
||||
in [RFC4226].
|
||||
|
||||
2. Notation and Terminology
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
3. Algorithm Requirements
|
||||
|
||||
This section summarizes the requirements taken into account for
|
||||
designing the TOTP algorithm.
|
||||
|
||||
R1: The prover (e.g., token, soft token) and verifier (authentication
|
||||
or validation server) MUST know or be able to derive the current
|
||||
Unix time (i.e., the number of seconds elapsed since midnight UTC
|
||||
of January 1, 1970) for OTP generation. See [UT] for a more
|
||||
detailed definition of the commonly known "Unix time". The
|
||||
precision of the time used by the prover affects how often the
|
||||
clock synchronization should be done; see Section 6.
|
||||
|
||||
R2: The prover and verifier MUST either share the same secret or the
|
||||
knowledge of a secret transformation to generate a shared secret.
|
||||
|
||||
R3: The algorithm MUST use HOTP [RFC4226] as a key building block.
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 3]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
R4: The prover and verifier MUST use the same time-step value X.
|
||||
|
||||
R5: There MUST be a unique secret (key) for each prover.
|
||||
|
||||
R6: The keys SHOULD be randomly generated or derived using key
|
||||
derivation algorithms.
|
||||
|
||||
R7: The keys MAY be stored in a tamper-resistant device and SHOULD be
|
||||
protected against unauthorized access and usage.
|
||||
|
||||
4. TOTP Algorithm
|
||||
|
||||
This variant of the HOTP algorithm specifies the calculation of a
|
||||
one-time password value, based on a representation of the counter as
|
||||
a time factor.
|
||||
|
||||
4.1. Notations
|
||||
|
||||
o X represents the time step in seconds (default value X =
|
||||
30 seconds) and is a system parameter.
|
||||
|
||||
o T0 is the Unix time to start counting time steps (default value is
|
||||
0, i.e., the Unix epoch) and is also a system parameter.
|
||||
|
||||
4.2. Description
|
||||
|
||||
Basically, we define TOTP as TOTP = HOTP(K, T), where T is an integer
|
||||
and represents the number of time steps between the initial counter
|
||||
time T0 and the current Unix time.
|
||||
|
||||
More specifically, T = (Current Unix time - T0) / X, where the
|
||||
default floor function is used in the computation.
|
||||
|
||||
For example, with T0 = 0 and Time Step X = 30, T = 1 if the current
|
||||
Unix time is 59 seconds, and T = 2 if the current Unix time is
|
||||
60 seconds.
|
||||
|
||||
The implementation of this algorithm MUST support a time value T
|
||||
larger than a 32-bit integer when it is beyond the year 2038. The
|
||||
value of the system parameters X and T0 are pre-established during
|
||||
the provisioning process and communicated between a prover and
|
||||
verifier as part of the provisioning step. The provisioning flow is
|
||||
out of scope of this document; refer to [RFC6030] for such
|
||||
provisioning container specifications.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 4]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
5. Security Considerations
|
||||
|
||||
5.1. General
|
||||
|
||||
The security and strength of this algorithm depend on the properties
|
||||
of the underlying building block HOTP, which is a construction based
|
||||
on HMAC [RFC2104] using SHA-1 as the hash function.
|
||||
|
||||
The conclusion of the security analysis detailed in [RFC4226] is
|
||||
that, for all practical purposes, the outputs of the dynamic
|
||||
truncation on distinct inputs are uniformly and independently
|
||||
distributed strings.
|
||||
|
||||
The analysis demonstrates that the best possible attack against the
|
||||
HOTP function is the brute force attack.
|
||||
|
||||
As indicated in the algorithm requirement section, keys SHOULD be
|
||||
chosen at random or using a cryptographically strong pseudorandom
|
||||
generator properly seeded with a random value.
|
||||
|
||||
Keys SHOULD be of the length of the HMAC output to facilitate
|
||||
interoperability.
|
||||
|
||||
We RECOMMEND following the recommendations in [RFC4086] for all
|
||||
pseudorandom and random number generations. The pseudorandom numbers
|
||||
used for generating the keys SHOULD successfully pass the randomness
|
||||
test specified in [CN], or a similar well-recognized test.
|
||||
|
||||
All the communications SHOULD take place over a secure channel, e.g.,
|
||||
Secure Socket Layer/Transport Layer Security (SSL/TLS) [RFC5246] or
|
||||
IPsec connections [RFC4301].
|
||||
|
||||
We also RECOMMEND storing the keys securely in the validation system,
|
||||
and, more specifically, encrypting them using tamper-resistant
|
||||
hardware encryption and exposing them only when required: for
|
||||
example, the key is decrypted when needed to verify an OTP value, and
|
||||
re-encrypted immediately to limit exposure in the RAM to a short
|
||||
period of time.
|
||||
|
||||
The key store MUST be in a secure area, to avoid, as much as
|
||||
possible, direct attack on the validation system and secrets
|
||||
database. Particularly, access to the key material should be limited
|
||||
to programs and processes required by the validation system only.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 5]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
5.2. Validation and Time-Step Size
|
||||
|
||||
An OTP generated within the same time step will be the same. When an
|
||||
OTP is received at a validation system, it doesn't know a client's
|
||||
exact timestamp when an OTP was generated. The validation system may
|
||||
typically use the timestamp when an OTP is received for OTP
|
||||
comparison. Due to network latency, the gap (as measured by T, that
|
||||
is, the number of time steps since T0) between the time that the OTP
|
||||
was generated and the time that the OTP arrives at the receiving
|
||||
system may be large. The receiving time at the validation system and
|
||||
the actual OTP generation may not fall within the same time-step
|
||||
window that produced the same OTP. When an OTP is generated at the
|
||||
end of a time-step window, the receiving time most likely falls into
|
||||
the next time-step window. A validation system SHOULD typically set
|
||||
a policy for an acceptable OTP transmission delay window for
|
||||
validation. The validation system should compare OTPs not only with
|
||||
the receiving timestamp but also the past timestamps that are within
|
||||
the transmission delay. A larger acceptable delay window would
|
||||
expose a larger window for attacks. We RECOMMEND that at most one
|
||||
time step is allowed as the network delay.
|
||||
|
||||
The time-step size has an impact on both security and usability. A
|
||||
larger time-step size means a larger validity window for an OTP to be
|
||||
accepted by a validation system. There are implications for using a
|
||||
larger time-step size, as follows:
|
||||
|
||||
First, a larger time-step size exposes a larger window to attack.
|
||||
When an OTP is generated and exposed to a third party before it is
|
||||
consumed, the third party can consume the OTP within the time-step
|
||||
window.
|
||||
|
||||
We RECOMMEND a default time-step size of 30 seconds. This default
|
||||
value of 30 seconds is selected as a balance between security and
|
||||
usability.
|
||||
|
||||
Second, the next different OTP must be generated in the next time-
|
||||
step window. A user must wait until the clock moves to the next
|
||||
time-step window from the last submission. The waiting time may not
|
||||
be exactly the length of the time step, depending on when the last
|
||||
OTP was generated. For example, if the last OTP was generated at the
|
||||
halfway point in a time-step window, the waiting time for the next
|
||||
OTP is half the length of the time step. In general, a larger time-
|
||||
step window means a longer waiting time for a user to get the next
|
||||
valid OTP after the last successful OTP validation. A too-large
|
||||
window (for example, 10 minutes) most probably won't be suitable for
|
||||
typical Internet login use cases; a user may not be able to get the
|
||||
next OTP within 10 minutes and therefore will have to re-login to the
|
||||
same site in 10 minutes.
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 6]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Note that a prover may send the same OTP inside a given time-step
|
||||
window multiple times to a verifier. The verifier MUST NOT accept
|
||||
the second attempt of the OTP after the successful validation has
|
||||
been issued for the first OTP, which ensures one-time only use of an
|
||||
OTP.
|
||||
|
||||
6. Resynchronization
|
||||
|
||||
Because of possible clock drifts between a client and a validation
|
||||
server, we RECOMMEND that the validator be set with a specific limit
|
||||
to the number of time steps a prover can be "out of synch" before
|
||||
being rejected.
|
||||
|
||||
This limit can be set both forward and backward from the calculated
|
||||
time step on receipt of the OTP value. If the time step is
|
||||
30 seconds as recommended, and the validator is set to only accept
|
||||
two time steps backward, then the maximum elapsed time drift would be
|
||||
around 89 seconds, i.e., 29 seconds in the calculated time step and
|
||||
60 seconds for two backward time steps.
|
||||
|
||||
This would mean the validator could perform a validation against the
|
||||
current time and then two further validations for each backward step
|
||||
(for a total of 3 validations). Upon successful validation, the
|
||||
validation server can record the detected clock drift for the token
|
||||
in terms of the number of time steps. When a new OTP is received
|
||||
after this step, the validator can validate the OTP with the current
|
||||
timestamp adjusted with the recorded number of time-step clock drifts
|
||||
for the token.
|
||||
|
||||
Also, it is important to note that the longer a prover has not sent
|
||||
an OTP to a validation system, the longer (potentially) the
|
||||
accumulated clock drift between the prover and the verifier. In such
|
||||
cases, the automatic resynchronization described above may not work
|
||||
if the drift exceeds the allowed threshold. Additional
|
||||
authentication measures should be used to safely authenticate the
|
||||
prover and explicitly resynchronize the clock drift between the
|
||||
prover and the validator.
|
||||
|
||||
7. Acknowledgements
|
||||
|
||||
The authors of this document would like to thank the following people
|
||||
for their contributions and support to make this a better
|
||||
specification: Hannes Tschofenig, Jonathan Tuliani, David Dix,
|
||||
Siddharth Bajaj, Stu Veath, Shuh Chang, Oanh Hoang, John Huang, and
|
||||
Siddhartha Mohapatra.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 7]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
8. References
|
||||
|
||||
8.1. Normative References
|
||||
|
||||
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
|
||||
Hashing for Message Authentication", RFC 2104,
|
||||
February 1997.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker,
|
||||
"Randomness Recommendations for Security", BCP 106,
|
||||
RFC 4086, June 2005.
|
||||
|
||||
[RFC4226] M'Raihi, D., Bellare, M., Hoornaert, F., Naccache, D., and
|
||||
O. Ranen, "HOTP: An HMAC-Based One-Time Password
|
||||
Algorithm", RFC 4226, December 2005.
|
||||
|
||||
[SHA2] NIST, "FIPS PUB 180-3: Secure Hash Standard (SHS)",
|
||||
October 2008, <http://csrc.nist.gov/publications/fips/
|
||||
fips180-3/fips180-3_final.pdf>.
|
||||
|
||||
8.2. Informative References
|
||||
|
||||
[CN] Coron, J. and D. Naccache, "An Accurate Evaluation of
|
||||
Maurer's Universal Test", LNCS 1556, February 1999,
|
||||
<http://www.gemplus.com/smart/rd/publications/pdf/
|
||||
CN99maur.pdf>.
|
||||
|
||||
[RFC4301] Kent, S. and K. Seo, "Security Architecture for the
|
||||
Internet Protocol", RFC 4301, December 2005.
|
||||
|
||||
[RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
|
||||
(TLS) Protocol Version 1.2", RFC 5246, August 2008.
|
||||
|
||||
[RFC6030] Hoyer, P., Pei, M., and S. Machani, "Portable Symmetric
|
||||
Key Container (PSKC)", RFC 6030, October 2010.
|
||||
|
||||
[UT] Wikipedia, "Unix time", February 2011,
|
||||
<http://en.wikipedia.org/wiki/Unix_time>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 8]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Appendix A. TOTP Algorithm: Reference Implementation
|
||||
|
||||
<CODE BEGINS>
|
||||
|
||||
/**
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted pursuant to, and subject to the license
|
||||
terms contained in, the Simplified BSD License set forth in Section
|
||||
4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
*/
|
||||
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
* This is an example implementation of the OATH
|
||||
* TOTP algorithm.
|
||||
* Visit www.openauthentication.org for more information.
|
||||
*
|
||||
* @author Johan Rydell, PortWise, Inc.
|
||||
*/
|
||||
|
||||
public class TOTP {
|
||||
|
||||
private TOTP() {}
|
||||
|
||||
/**
|
||||
* This method uses the JCE to provide the crypto algorithm.
|
||||
* HMAC computes a Hashed Message Authentication Code with the
|
||||
* crypto hash algorithm as a parameter.
|
||||
*
|
||||
* @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256,
|
||||
* HmacSHA512)
|
||||
* @param keyBytes: the bytes to use for the HMAC key
|
||||
* @param text: the message or text to be authenticated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 9]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
private static byte[] hmac_sha(String crypto, byte[] keyBytes,
|
||||
byte[] text){
|
||||
try {
|
||||
Mac hmac;
|
||||
hmac = Mac.getInstance(crypto);
|
||||
SecretKeySpec macKey =
|
||||
new SecretKeySpec(keyBytes, "RAW");
|
||||
hmac.init(macKey);
|
||||
return hmac.doFinal(text);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new UndeclaredThrowableException(gse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method converts a HEX string to Byte[]
|
||||
*
|
||||
* @param hex: the HEX string
|
||||
*
|
||||
* @return: a byte array
|
||||
*/
|
||||
|
||||
private static byte[] hexStr2Bytes(String hex){
|
||||
// Adding one byte to get the right conversion
|
||||
// Values starting with "0" can be converted
|
||||
byte[] bArray = new BigInteger("10" + hex,16).toByteArray();
|
||||
|
||||
// Copy all the REAL bytes, not the "first"
|
||||
byte[] ret = new byte[bArray.length - 1];
|
||||
for (int i = 0; i < ret.length; i++)
|
||||
ret[i] = bArray[i+1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final int[] DIGITS_POWER
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
= {1,10,100,1000,10000,100000,1000000,10000000,100000000 };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 10]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key,
|
||||
String time,
|
||||
String returnDigits){
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA1");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP256(String key,
|
||||
String time,
|
||||
String returnDigits){
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA256");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 11]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP512(String key,
|
||||
String time,
|
||||
String returnDigits){
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA512");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given
|
||||
* set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
* @param crypto: the crypto function to use
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes
|
||||
* {@link truncationDigits} digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key,
|
||||
String time,
|
||||
String returnDigits,
|
||||
String crypto){
|
||||
int codeDigits = Integer.decode(returnDigits).intValue();
|
||||
String result = null;
|
||||
|
||||
// Using the counter
|
||||
// First 8 bytes are for the movingFactor
|
||||
// Compliant with base RFC 4226 (HOTP)
|
||||
while (time.length() < 16 )
|
||||
time = "0" + time;
|
||||
|
||||
// Get the HEX in a Byte[]
|
||||
byte[] msg = hexStr2Bytes(time);
|
||||
byte[] k = hexStr2Bytes(key);
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 12]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
byte[] hash = hmac_sha(crypto, k, msg);
|
||||
|
||||
// put selected bytes into result int
|
||||
int offset = hash[hash.length - 1] & 0xf;
|
||||
|
||||
int binary =
|
||||
((hash[offset] & 0x7f) << 24) |
|
||||
((hash[offset + 1] & 0xff) << 16) |
|
||||
((hash[offset + 2] & 0xff) << 8) |
|
||||
(hash[offset + 3] & 0xff);
|
||||
|
||||
int otp = binary % DIGITS_POWER[codeDigits];
|
||||
|
||||
result = Integer.toString(otp);
|
||||
while (result.length() < codeDigits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Seed for HMAC-SHA1 - 20 bytes
|
||||
String seed = "3132333435363738393031323334353637383930";
|
||||
// Seed for HMAC-SHA256 - 32 bytes
|
||||
String seed32 = "3132333435363738393031323334353637383930" +
|
||||
"313233343536373839303132";
|
||||
// Seed for HMAC-SHA512 - 64 bytes
|
||||
String seed64 = "3132333435363738393031323334353637383930" +
|
||||
"3132333435363738393031323334353637383930" +
|
||||
"3132333435363738393031323334353637383930" +
|
||||
"31323334";
|
||||
long T0 = 0;
|
||||
long X = 30;
|
||||
long testTime[] = {59L, 1111111109L, 1111111111L,
|
||||
1234567890L, 2000000000L, 20000000000L};
|
||||
|
||||
String steps = "0";
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 13]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
try {
|
||||
System.out.println(
|
||||
"+---------------+-----------------------+" +
|
||||
"------------------+--------+--------+");
|
||||
System.out.println(
|
||||
"| Time(sec) | Time (UTC format) " +
|
||||
"| Value of T(Hex) | TOTP | Mode |");
|
||||
System.out.println(
|
||||
"+---------------+-----------------------+" +
|
||||
"------------------+--------+--------+");
|
||||
|
||||
for (int i=0; i<testTime.length; i++) {
|
||||
long T = (testTime[i] - T0)/X;
|
||||
steps = Long.toHexString(T).toUpperCase();
|
||||
while (steps.length() < 16) steps = "0" + steps;
|
||||
String fmtTime = String.format("%1$-11s", testTime[i]);
|
||||
String utcTime = df.format(new Date(testTime[i]*1000));
|
||||
System.out.print("| " + fmtTime + " | " + utcTime +
|
||||
" | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed, steps, "8",
|
||||
"HmacSHA1") + "| SHA1 |");
|
||||
System.out.print("| " + fmtTime + " | " + utcTime +
|
||||
" | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed32, steps, "8",
|
||||
"HmacSHA256") + "| SHA256 |");
|
||||
System.out.print("| " + fmtTime + " | " + utcTime +
|
||||
" | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed64, steps, "8",
|
||||
"HmacSHA512") + "| SHA512 |");
|
||||
|
||||
System.out.println(
|
||||
"+---------------+-----------------------+" +
|
||||
"------------------+--------+--------+");
|
||||
}
|
||||
}catch (final Exception e){
|
||||
System.out.println("Error : " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<CODE ENDS>
|
||||
|
||||
Appendix B. Test Vectors
|
||||
|
||||
This section provides test values that can be used for the HOTP time-
|
||||
based variant algorithm interoperability test.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 14]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
The test token shared secret uses the ASCII string value
|
||||
"12345678901234567890". With Time Step X = 30, and the Unix epoch as
|
||||
the initial value to count time steps, where T0 = 0, the TOTP
|
||||
algorithm will display the following values for specified modes and
|
||||
timestamps.
|
||||
|
||||
+-------------+--------------+------------------+----------+--------+
|
||||
| Time (sec) | UTC Time | Value of T (hex) | TOTP | Mode |
|
||||
+-------------+--------------+------------------+----------+--------+
|
||||
| 59 | 1970-01-01 | 0000000000000001 | 94287082 | SHA1 |
|
||||
| | 00:00:59 | | | |
|
||||
| 59 | 1970-01-01 | 0000000000000001 | 46119246 | SHA256 |
|
||||
| | 00:00:59 | | | |
|
||||
| 59 | 1970-01-01 | 0000000000000001 | 90693936 | SHA512 |
|
||||
| | 00:00:59 | | | |
|
||||
| 1111111109 | 2005-03-18 | 00000000023523EC | 07081804 | SHA1 |
|
||||
| | 01:58:29 | | | |
|
||||
| 1111111109 | 2005-03-18 | 00000000023523EC | 68084774 | SHA256 |
|
||||
| | 01:58:29 | | | |
|
||||
| 1111111109 | 2005-03-18 | 00000000023523EC | 25091201 | SHA512 |
|
||||
| | 01:58:29 | | | |
|
||||
| 1111111111 | 2005-03-18 | 00000000023523ED | 14050471 | SHA1 |
|
||||
| | 01:58:31 | | | |
|
||||
| 1111111111 | 2005-03-18 | 00000000023523ED | 67062674 | SHA256 |
|
||||
| | 01:58:31 | | | |
|
||||
| 1111111111 | 2005-03-18 | 00000000023523ED | 99943326 | SHA512 |
|
||||
| | 01:58:31 | | | |
|
||||
| 1234567890 | 2009-02-13 | 000000000273EF07 | 89005924 | SHA1 |
|
||||
| | 23:31:30 | | | |
|
||||
| 1234567890 | 2009-02-13 | 000000000273EF07 | 91819424 | SHA256 |
|
||||
| | 23:31:30 | | | |
|
||||
| 1234567890 | 2009-02-13 | 000000000273EF07 | 93441116 | SHA512 |
|
||||
| | 23:31:30 | | | |
|
||||
| 2000000000 | 2033-05-18 | 0000000003F940AA | 69279037 | SHA1 |
|
||||
| | 03:33:20 | | | |
|
||||
| 2000000000 | 2033-05-18 | 0000000003F940AA | 90698825 | SHA256 |
|
||||
| | 03:33:20 | | | |
|
||||
| 2000000000 | 2033-05-18 | 0000000003F940AA | 38618901 | SHA512 |
|
||||
| | 03:33:20 | | | |
|
||||
| 20000000000 | 2603-10-11 | 0000000027BC86AA | 65353130 | SHA1 |
|
||||
| | 11:33:20 | | | |
|
||||
| 20000000000 | 2603-10-11 | 0000000027BC86AA | 77737706 | SHA256 |
|
||||
| | 11:33:20 | | | |
|
||||
| 20000000000 | 2603-10-11 | 0000000027BC86AA | 47863826 | SHA512 |
|
||||
| | 11:33:20 | | | |
|
||||
+-------------+--------------+------------------+----------+--------+
|
||||
|
||||
Table 1: TOTP Table
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 15]
|
||||
|
||||
RFC 6238 HOTPTimeBased May 2011
|
||||
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
David M'Raihi
|
||||
Verisign, Inc.
|
||||
685 E. Middlefield Road
|
||||
Mountain View, CA 94043
|
||||
USA
|
||||
|
||||
EMail: davidietf@gmail.com
|
||||
|
||||
|
||||
Salah Machani
|
||||
Diversinet Corp.
|
||||
2225 Sheppard Avenue East, Suite 1801
|
||||
Toronto, Ontario M2J 5C2
|
||||
Canada
|
||||
|
||||
EMail: smachani@diversinet.com
|
||||
|
||||
|
||||
Mingliang Pei
|
||||
Symantec
|
||||
510 E. Middlefield Road
|
||||
Mountain View, CA 94043
|
||||
USA
|
||||
|
||||
EMail: Mingliang_Pei@symantec.com
|
||||
|
||||
|
||||
Johan Rydell
|
||||
Portwise, Inc.
|
||||
275 Hawthorne Ave., Suite 119
|
||||
Palo Alto, CA 94301
|
||||
USA
|
||||
|
||||
EMail: johanietf@gmail.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
M'Raihi, et al. Informational [Page 16]
|
||||
|
|
@ -10,11 +10,3 @@ security_HEADERS = \
|
|||
pam_constants.h \
|
||||
pam_modules.h \
|
||||
pam_types.h
|
||||
|
||||
if WITH_OATH
|
||||
security_HEADERS += \
|
||||
oath.h \
|
||||
oath_constants.h \
|
||||
oath_rfc4648.h \
|
||||
oath_types.h
|
||||
endif
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_H_INCLUDED
|
||||
#define OATH_H_INCLUDED
|
||||
|
||||
#include <security/oath_constants.h>
|
||||
#include <security/oath_types.h>
|
||||
#include <security/oath_rfc4648.h>
|
||||
|
||||
struct oath_key *oath_key_alloc(void);
|
||||
void oath_key_free(struct oath_key *);
|
||||
struct oath_key *oath_key_from_uri(const char *);
|
||||
struct oath_key *oath_key_from_file(const char *);
|
||||
char *oath_key_to_uri(const struct oath_key *);
|
||||
|
||||
struct oath_key *oath_key_dummy(enum oath_mode, enum oath_hash, unsigned int);
|
||||
|
||||
unsigned int oath_hotp(const uint8_t *, size_t, uint64_t, unsigned int);
|
||||
unsigned int oath_hotp_current(struct oath_key *);
|
||||
int oath_hotp_match(struct oath_key *, unsigned int, int);
|
||||
|
||||
unsigned int oath_totp(const uint8_t *, size_t, unsigned int);
|
||||
unsigned int oath_totp_current(const struct oath_key *);
|
||||
int oath_totp_match(struct oath_key *, unsigned int, int);
|
||||
|
||||
#endif
|
|
@ -1,84 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_CONSTANTS_H_INCLUDED
|
||||
#define OATH_CONSTANTS_H_INCLUDED
|
||||
|
||||
/*
|
||||
* OATH modes
|
||||
*/
|
||||
enum oath_mode {
|
||||
om_undef, /* not set / default */
|
||||
om_hotp, /* RFC 4226 HOTP */
|
||||
om_totp, /* RFC 6238 TOTP */
|
||||
om_max
|
||||
};
|
||||
|
||||
/*
|
||||
* Hash functions
|
||||
*/
|
||||
enum oath_hash {
|
||||
oh_undef, /* not set / default */
|
||||
oh_md5, /* RFC 1321 MD5 */
|
||||
oh_sha1, /* FIPS 180 SHA-1 */
|
||||
oh_sha256, /* FIPS 180 SHA-256 */
|
||||
oh_sha512, /* FIPS 180 SHA-512 */
|
||||
oh_max
|
||||
};
|
||||
|
||||
/*
|
||||
* Default time step for TOTP: 30 seconds.
|
||||
*/
|
||||
#define OATH_DEF_TIMESTEP 30
|
||||
|
||||
/*
|
||||
* Maximum time step for TOTP: 10 minutes, which RFC 6238 cites as an
|
||||
* example of an unreasonably large time step.
|
||||
*/
|
||||
#define OATH_MAX_TIMESTEP 600
|
||||
|
||||
/*
|
||||
* Maximum key length in bytes. HMAC has a 64-byte block size; if the key
|
||||
* K is longer than that, HMAC derives a new key K' = H(K).
|
||||
*/
|
||||
#define OATH_MAX_KEYLEN 64
|
||||
|
||||
/*
|
||||
* Maximum label length in characters, including terminating NUL.
|
||||
*/
|
||||
#define OATH_MAX_LABELLEN 64
|
||||
|
||||
/*
|
||||
* Label to use for dummy keys
|
||||
*/
|
||||
#define OATH_DUMMY_LABEL "oath-dummy-key"
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -186,6 +186,7 @@ enum {
|
|||
OPENPAM_VERIFY_POLICY_FILE,
|
||||
OPENPAM_RESTRICT_MODULE_NAME,
|
||||
OPENPAM_VERIFY_MODULE_FILE,
|
||||
OPENPAM_FALLBACK_TO_OTHER,
|
||||
OPENPAM_NUM_FEATURES
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -39,7 +39,7 @@
|
|||
#define SECURITY_OPENPAM_VERSION_H_INCLUDED
|
||||
|
||||
#define OPENPAM
|
||||
#define OPENPAM_VERSION 20120526
|
||||
#define OPENPAM_RELEASE "Micrampelis"
|
||||
#define OPENPAM_VERSION 20140912
|
||||
#define OPENPAM_RELEASE "Ourouparia"
|
||||
|
||||
#endif /* !SECURITY_OPENPAM_VERSION_H_INCLUDED */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# $Id$
|
||||
|
||||
SUBDIRS = libpam
|
||||
SUBDIRS =
|
||||
|
||||
if WITH_OATH
|
||||
SUBDIRS += liboath
|
||||
if !WITH_SYSTEM_LIBPAM
|
||||
SUBDIRS += libpam
|
||||
endif
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/libpam
|
||||
|
||||
lib_LTLIBRARIES = liboath.la
|
||||
|
||||
liboath_la_SOURCES = \
|
||||
oath_base32.c \
|
||||
oath_base64.c \
|
||||
oath_hotp.c \
|
||||
oath_totp.c \
|
||||
oath_key_alloc.c \
|
||||
oath_key_dummy.c \
|
||||
oath_key_free.c \
|
||||
oath_key.c
|
||||
|
||||
liboath_la_LDFLAGS = -no-undefined -version-info @LIB_MAJ@
|
||||
liboath_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la @CRYPTO_LIBS@
|
|
@ -1,164 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
static const char b32[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
/*
|
||||
* Encode data in RFC 3548 base 32 representation. The target buffer must
|
||||
* have room for base32_enclen(len) characters and a terminating NUL.
|
||||
*/
|
||||
int
|
||||
base32_enc(const uint8_t *in, size_t ilen, char *out, size_t *olen)
|
||||
{
|
||||
uint64_t bits;
|
||||
|
||||
if (*olen <= base32_enclen(ilen))
|
||||
return (-1);
|
||||
*olen = 0;
|
||||
while (ilen >= 5) {
|
||||
bits = 0;
|
||||
bits |= (uint64_t)in[0] << 32;
|
||||
bits |= (uint64_t)in[1] << 24;
|
||||
bits |= (uint64_t)in[2] << 16;
|
||||
bits |= (uint64_t)in[3] << 8;
|
||||
bits |= (uint64_t)in[4];
|
||||
ilen -= 5;
|
||||
in += 5;
|
||||
out[0] = b32[bits >> 35 & 0x1f];
|
||||
out[1] = b32[bits >> 30 & 0x1f];
|
||||
out[2] = b32[bits >> 25 & 0x1f];
|
||||
out[3] = b32[bits >> 20 & 0x1f];
|
||||
out[4] = b32[bits >> 15 & 0x1f];
|
||||
out[5] = b32[bits >> 10 & 0x1f];
|
||||
out[6] = b32[bits >> 5 & 0x1f];
|
||||
out[7] = b32[bits & 0x1f];
|
||||
*olen += 8;
|
||||
out += 8;
|
||||
}
|
||||
if (ilen > 0) {
|
||||
bits = 0;
|
||||
switch (ilen) {
|
||||
case 4:
|
||||
bits |= (uint64_t)in[3] << 8;
|
||||
case 3:
|
||||
bits |= (uint64_t)in[2] << 16;
|
||||
case 2:
|
||||
bits |= (uint64_t)in[1] << 24;
|
||||
case 1:
|
||||
bits |= (uint64_t)in[0] << 32;
|
||||
}
|
||||
out[0] = b32[bits >> 35 & 0x1f];
|
||||
out[1] = b32[bits >> 30 & 0x1f];
|
||||
out[2] = ilen > 1 ? b32[bits >> 25 & 0x1f] : '=';
|
||||
out[3] = ilen > 1 ? b32[bits >> 20 & 0x1f] : '=';
|
||||
out[4] = ilen > 2 ? b32[bits >> 15 & 0x1f] : '=';
|
||||
out[5] = ilen > 3 ? b32[bits >> 10 & 0x1f] : '=';
|
||||
out[6] = ilen > 3 ? b32[bits >> 5 & 0x1f] : '=';
|
||||
out[7] = '=';
|
||||
*olen += 8;
|
||||
out += 8;
|
||||
}
|
||||
out[0] = '\0';
|
||||
++*olen;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode data in RFC 2548 base 32 representation, stopping at the
|
||||
* terminating NUL, the first invalid (non-base32, non-whitespace)
|
||||
* character or after len characters, whichever comes first.
|
||||
*
|
||||
* The olen argument is used by the caller to pass the size of the buffer
|
||||
* and by base32_dec() to return the amount of data successfully decoded.
|
||||
* If the buffer is too small, base32_dec() discards the excess data, but
|
||||
* returns the total amount.
|
||||
*/
|
||||
int
|
||||
base32_dec(const char *in, size_t ilen, uint8_t *out, size_t *olen)
|
||||
{
|
||||
size_t len;
|
||||
uint64_t bits;
|
||||
int shift;
|
||||
|
||||
for (len = 0, bits = 0, shift = 40; ilen && *in; --ilen, ++in) {
|
||||
if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
|
||||
continue;
|
||||
} else if (*in >= 'A' && *in <= 'Z') {
|
||||
shift -= 5;
|
||||
bits |= (uint64_t)(*in - 'A') << shift;
|
||||
} else if (*in >= 'a' && *in <= 'z') {
|
||||
shift -= 5;
|
||||
bits |= (uint64_t)(*in - 'a') << shift;
|
||||
} else if (*in >= '2' && *in <= '7') {
|
||||
shift -= 5;
|
||||
bits |= (uint64_t)(*in - '2' + 26) << shift;
|
||||
} else if (*in == '=' &&
|
||||
(shift == 30 || shift == 20 || shift == 15 || shift == 5)) {
|
||||
/* hack: assume the rest of the padding is ok */
|
||||
shift = 0;
|
||||
} else {
|
||||
*olen = 0;
|
||||
return (-1);
|
||||
}
|
||||
if (shift == 0) {
|
||||
if ((len += 5) <= *olen) {
|
||||
out[0] = (bits >> 32) & 0xff;
|
||||
out[1] = (bits >> 24) & 0xff;
|
||||
out[2] = (bits >> 16) & 0xff;
|
||||
out[3] = (bits >> 8) & 0xff;
|
||||
out[4] = bits & 0xff;
|
||||
out += 5;
|
||||
}
|
||||
bits = 0;
|
||||
shift = 40;
|
||||
}
|
||||
if (*in == '=')
|
||||
break;
|
||||
}
|
||||
if (len > *olen) {
|
||||
*olen = len;
|
||||
return (-1);
|
||||
}
|
||||
*olen = len;
|
||||
return (0);
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
static const char b64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
/*
|
||||
* Encode data in RFC 3548 base 64 representation. The target buffer must
|
||||
* have room for base64_enclen(len) characters and a terminating NUL.
|
||||
*/
|
||||
int
|
||||
base64_enc(const uint8_t *in, size_t ilen, char *out, size_t *olen)
|
||||
{
|
||||
uint32_t bits;
|
||||
|
||||
if (*olen <= base64_enclen(ilen))
|
||||
return (-1);
|
||||
*olen = 0;
|
||||
while (ilen >= 3) {
|
||||
bits = 0;
|
||||
bits |= (uint32_t)in[0] << 16;
|
||||
bits |= (uint32_t)in[1] << 8;
|
||||
bits |= (uint32_t)in[2];
|
||||
ilen -= 3;
|
||||
in += 3;
|
||||
out[0] = b64[bits >> 18 & 0x3f];
|
||||
out[1] = b64[bits >> 12 & 0x3f];
|
||||
out[2] = b64[bits >> 6 & 0x3f];
|
||||
out[3] = b64[bits & 0x3f];
|
||||
*olen += 4;
|
||||
out += 4;
|
||||
}
|
||||
if (ilen > 0) {
|
||||
bits = 0;
|
||||
switch (ilen) {
|
||||
case 2:
|
||||
bits |= (uint32_t)in[1] << 8;
|
||||
case 1:
|
||||
bits |= (uint32_t)in[0] << 16;
|
||||
}
|
||||
out[0] = b64[bits >> 18 & 0x3f];
|
||||
out[1] = b64[bits >> 12 & 0x3f];
|
||||
out[2] = ilen > 1 ? b64[bits >> 6 & 0x3f] : '=';
|
||||
out[3] = '=';
|
||||
*olen += 4;
|
||||
out += 4;
|
||||
}
|
||||
out[0] = '\0';
|
||||
++*olen;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode data in RFC 2548 base 64 representation, stopping at the
|
||||
* terminating NUL, the first invalid (non-base64, non-whitespace)
|
||||
* character or after len characters, whichever comes first.
|
||||
*
|
||||
* The olen argument is used by the caller to pass the size of the buffer
|
||||
* and by base64_dec() to return the amount of data successfully decoded.
|
||||
* If the buffer is too small, base64_dec() discards the excess data, but
|
||||
* returns the total amount.
|
||||
*/
|
||||
int
|
||||
base64_dec(const char *in, size_t ilen, uint8_t *out, size_t *olen)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t bits;
|
||||
int shift;
|
||||
|
||||
for (len = 0, bits = 0, shift = 24; ilen && *in; --ilen, ++in) {
|
||||
if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
|
||||
continue;
|
||||
} else if (*in >= 'A' && *in <= 'Z') {
|
||||
shift -= 6;
|
||||
bits |= (uint32_t)(*in - 'A') << shift;
|
||||
} else if (*in >= 'a' && *in <= 'z') {
|
||||
shift -= 6;
|
||||
bits |= (uint32_t)(*in - 'a' + 26) << shift;
|
||||
} else if (*in >= '0' && *in <= '9') {
|
||||
shift -= 6;
|
||||
bits |= (uint32_t)(*in - '2' + 52) << shift;
|
||||
} else if (*in == '+') {
|
||||
shift -= 6;
|
||||
bits |= (uint32_t)62 << shift;
|
||||
} else if (*in == '/') {
|
||||
shift -= 6;
|
||||
bits |= (uint32_t)63 << shift;
|
||||
} else if (*in == '=' && (shift == 12 || shift == 6)) {
|
||||
/* hack: assume the rest of the padding is ok */
|
||||
shift = 0;
|
||||
} else {
|
||||
*olen = 0;
|
||||
return (-1);
|
||||
}
|
||||
if (shift == 0) {
|
||||
if ((len += 3) <= *olen) {
|
||||
out[1] = (bits >> 16) & 0xff;
|
||||
out[1] = (bits >> 8) & 0xff;
|
||||
out[2] = bits & 0xff;
|
||||
out += 3;
|
||||
}
|
||||
bits = 0;
|
||||
shift = 24;
|
||||
}
|
||||
if (*in == '=')
|
||||
break;
|
||||
}
|
||||
if (len > *olen) {
|
||||
*olen = len;
|
||||
return (-1);
|
||||
}
|
||||
*olen = len;
|
||||
return (0);
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#define StToNum(St) (St)
|
||||
|
||||
static uint32_t
|
||||
DT(const uint8_t *String)
|
||||
{
|
||||
uint8_t OffsetBits;
|
||||
int Offset;
|
||||
uint32_t P;
|
||||
|
||||
OffsetBits = String[19] & 0x0f;
|
||||
Offset = StToNum(OffsetBits);
|
||||
P = (uint32_t)String[Offset + 0] << 24 |
|
||||
(uint32_t)String[Offset + 1] << 16 |
|
||||
(uint32_t)String[Offset + 2] << 8 |
|
||||
(uint32_t)String[Offset + 3];
|
||||
return (P & 0x7fffffffUL);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
oath_hotp(const uint8_t *K, size_t Klen, uint64_t seq, unsigned int Digit)
|
||||
{
|
||||
HMAC_CTX ctx;
|
||||
uint8_t C[8];
|
||||
uint8_t HS[20];
|
||||
unsigned int HSlen;
|
||||
uint32_t Sbits, Snum;
|
||||
unsigned int mod, D;
|
||||
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
C[i] = seq & 0xff;
|
||||
seq >>= 8;
|
||||
}
|
||||
|
||||
/* HS = HMAC-SHA-1(K,C) */
|
||||
HMAC_CTX_init(&ctx);
|
||||
HMAC_Init_ex(&ctx, K, Klen, EVP_sha1(), NULL);
|
||||
HMAC_Update(&ctx, (const uint8_t *)&C, sizeof C);
|
||||
HMAC_Final(&ctx, HS, &HSlen);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
|
||||
Sbits = DT(HS);
|
||||
Snum = StToNum(Sbits);
|
||||
for (mod = 1; Digit > 0; --Digit)
|
||||
mod *= 10;
|
||||
D = Snum % mod;
|
||||
return (D);
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes the current code for the given key and advances the counter.
|
||||
*/
|
||||
unsigned int
|
||||
oath_hotp_current(struct oath_key *k)
|
||||
{
|
||||
unsigned int code;
|
||||
|
||||
if (k == NULL)
|
||||
return (-1);
|
||||
if (k->mode != om_hotp)
|
||||
return (-1);
|
||||
if (k->counter == UINT64_MAX)
|
||||
return (-1);
|
||||
code = oath_hotp(k->key, k->keylen, k->counter, k->digits);
|
||||
k->counter += 1;
|
||||
return (code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares the code provided by the user with expected values within a
|
||||
* given window. Returns 1 if there was a match, 0 if not, and -1 if an
|
||||
* error occurred. Also advances the counter if there was a match.
|
||||
*/
|
||||
int
|
||||
oath_hotp_match(struct oath_key *k, unsigned int response, int window)
|
||||
{
|
||||
unsigned int code;
|
||||
int dummy;
|
||||
|
||||
if (k == NULL)
|
||||
return (-1);
|
||||
if (window < 1)
|
||||
return (-1);
|
||||
if (k->mode != om_hotp)
|
||||
return (-1);
|
||||
if (k->counter >= UINT64_MAX - window)
|
||||
return (-1);
|
||||
dummy = (strcmp(k->label, OATH_DUMMY_LABEL) == 0);
|
||||
for (int i = 0; i < window; ++i) {
|
||||
code = oath_hotp(k->key, k->keylen, k->counter + i, k->digits);
|
||||
if (code == response && !dummy) {
|
||||
k->counter = k->counter + i;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
|
@ -1,268 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_asprintf.h"
|
||||
#include "openpam_strlcmp.h"
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* Allocate a struct oath_key and populate it from a Google Authenticator
|
||||
* otpauth URI
|
||||
*/
|
||||
struct oath_key *
|
||||
oath_key_from_uri(const char *uri)
|
||||
{
|
||||
struct oath_key *key;
|
||||
const char *p, *q, *r;
|
||||
uintmax_t n;
|
||||
char *e;
|
||||
|
||||
if ((key = oath_key_alloc()) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* check method */
|
||||
p = uri;
|
||||
if (strlcmp("otpauth://", p, 10) != 0)
|
||||
goto invalid;
|
||||
p += 10;
|
||||
|
||||
/* check mode (hotp = event, totp = time-sync) */
|
||||
if ((q = strchr(p, '/')) == NULL)
|
||||
goto invalid;
|
||||
if (strlcmp("hotp", p, q - p) == 0) {
|
||||
key->mode = om_hotp;
|
||||
} else if (strlcmp("totp", p, q - p) == 0) {
|
||||
key->mode = om_totp;
|
||||
} else {
|
||||
goto invalid;
|
||||
}
|
||||
p = q + 1;
|
||||
|
||||
/* extract label */
|
||||
if ((q = strchr(p, '?')) == NULL)
|
||||
goto invalid;
|
||||
if ((key->labellen = q - p + 1) > sizeof key->label)
|
||||
goto invalid;
|
||||
memcpy(key->label, p, q - p);
|
||||
key->label[q - p] = '\0';
|
||||
p = q + 1;
|
||||
|
||||
/* extract parameters */
|
||||
key->counter = UINT64_MAX;
|
||||
while (*p != '\0') {
|
||||
if ((q = strchr(p, '=')) == NULL)
|
||||
goto invalid;
|
||||
q = q + 1;
|
||||
if ((r = strchr(p, '&')) == NULL)
|
||||
r = strchr(p, '\0');
|
||||
if (r < q)
|
||||
/* & before = */
|
||||
goto invalid;
|
||||
/* p points to key, q points to value, r points to & or NUL */
|
||||
if (strlcmp("secret=", p, q - p) == 0) {
|
||||
if (key->keylen != 0)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
key->keylen = sizeof key->key;
|
||||
if (base32_dec(q, r - q, key->key, &key->keylen) != 0)
|
||||
goto invalid;
|
||||
if (base32_enclen(key->keylen) != (size_t)(r - q))
|
||||
goto invalid;
|
||||
} else if (strlcmp("algorithm=", p, q - p) == 0) {
|
||||
if (key->hash != oh_undef)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
if (strlcmp("SHA1", q, r - q) == 0)
|
||||
key->hash = oh_sha1;
|
||||
else if (strlcmp("SHA256", q, r - q) == 0)
|
||||
key->hash = oh_sha256;
|
||||
else if (strlcmp("SHA512", q, r - q) == 0)
|
||||
key->hash = oh_sha512;
|
||||
else if (strlcmp("MD5", q, r - q) == 0)
|
||||
key->hash = oh_md5;
|
||||
else
|
||||
goto invalid;
|
||||
} else if (strlcmp("digits=", p, q - p) == 0) {
|
||||
if (key->digits != 0)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
/* only 6 or 8 */
|
||||
if (r - q != 1 || (*q != '6' && *q != '8'))
|
||||
goto invalid;
|
||||
key->digits = *q - '0';
|
||||
} else if (strlcmp("counter=", p, q - p) == 0) {
|
||||
if (key->counter != UINT64_MAX)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
n = strtoumax(q, &e, 10);
|
||||
if (e != r || n >= UINT64_MAX)
|
||||
goto invalid;
|
||||
key->counter = (uint64_t)n;
|
||||
} else if (strlcmp("period=", p, q - p) == 0) {
|
||||
if (key->timestep != 0)
|
||||
/* dupe */
|
||||
goto invalid;
|
||||
n = strtoumax(q, &e, 10);
|
||||
if (e != r || n > OATH_MAX_TIMESTEP)
|
||||
goto invalid;
|
||||
key->timestep = n;
|
||||
} else {
|
||||
goto invalid;
|
||||
}
|
||||
/* final parameter? */
|
||||
if (*r == '\0')
|
||||
break;
|
||||
/* skip & and continue */
|
||||
p = r + 1;
|
||||
}
|
||||
|
||||
/* sanity checks and default values */
|
||||
if (key->mode == om_hotp) {
|
||||
if (key->timestep != 0)
|
||||
goto invalid;
|
||||
if (key->counter == UINTMAX_MAX)
|
||||
key->counter = 0;
|
||||
} else if (key->mode == om_totp) {
|
||||
if (key->counter != UINTMAX_MAX)
|
||||
goto invalid;
|
||||
if (key->timestep == 0)
|
||||
key->timestep = OATH_DEF_TIMESTEP;
|
||||
} else {
|
||||
/* unreachable */
|
||||
oath_key_free(key);
|
||||
return (NULL);
|
||||
}
|
||||
if (key->hash == oh_undef)
|
||||
key->hash = oh_sha1;
|
||||
if (key->digits == 0)
|
||||
key->digits = 6;
|
||||
if (key->keylen == 0)
|
||||
goto invalid;
|
||||
return (key);
|
||||
|
||||
invalid:
|
||||
openpam_log(PAM_LOG_NOTICE, "invalid OATH URI: %s", uri);
|
||||
oath_key_free(key);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct oath_key *
|
||||
oath_key_from_file(const char *filename)
|
||||
{
|
||||
struct oath_key *key;
|
||||
FILE *f;
|
||||
char *line;
|
||||
size_t len;
|
||||
|
||||
if ((f = fopen(filename, "r")) == NULL)
|
||||
return (NULL);
|
||||
/* get first non-empty non-comment line */
|
||||
line = openpam_readline(f, NULL, &len);
|
||||
if (strlcmp("otpauth://", line, len) == 0) {
|
||||
key = oath_key_from_uri(line);
|
||||
} else {
|
||||
openpam_log(PAM_LOG_ERROR,
|
||||
"unrecognized key file format: %s", filename);
|
||||
key = NULL;
|
||||
}
|
||||
fclose(f);
|
||||
return (key);
|
||||
}
|
||||
|
||||
char *
|
||||
oath_key_to_uri(const struct oath_key *key)
|
||||
{
|
||||
const char *hash;
|
||||
char *tmp, *uri;
|
||||
size_t kslen, urilen;
|
||||
|
||||
switch (key->hash) {
|
||||
case oh_sha1:
|
||||
hash = "SHA1";
|
||||
break;
|
||||
case oh_sha256:
|
||||
hash = "SHA256";
|
||||
break;
|
||||
case oh_sha512:
|
||||
hash = "SHA512";
|
||||
break;
|
||||
case oh_md5:
|
||||
hash = "MD5";
|
||||
break;
|
||||
default:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (key->mode == om_hotp) {
|
||||
urilen = asprintf(&uri, "otpauth://"
|
||||
"%s/%s?algorithm=%s&digits=%d&counter=%ju&secret=",
|
||||
"hotp", key->label, hash, key->digits,
|
||||
(uintmax_t)key->counter);
|
||||
} else if (key->mode == om_totp) {
|
||||
urilen = asprintf(&uri, "otpauth://"
|
||||
"%s/%s?algorithm=%s&digits=%d&period=%u&secret=",
|
||||
"totp", key->label, hash, key->digits, key->timestep);
|
||||
} else {
|
||||
/* unreachable */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* compute length of base32-encoded key and append it */
|
||||
kslen = base32_enclen(key->keylen) + 1;
|
||||
if ((tmp = realloc(uri, urilen + kslen)) == NULL) {
|
||||
free(uri);
|
||||
return (NULL);
|
||||
}
|
||||
uri = tmp;
|
||||
if (base32_enc(key->key, key->keylen, uri + urilen, &kslen) != 0) {
|
||||
free(uri);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (uri);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Allocates an OATH key structure
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_alloc(void)
|
||||
{
|
||||
struct oath_key *key;
|
||||
int prot, flags;
|
||||
|
||||
prot = PROT_READ|PROT_WRITE;
|
||||
flags = MAP_ANON;
|
||||
#ifdef MAP_NOCORE
|
||||
flags |= MAP_NOCORE;
|
||||
#endif
|
||||
if ((key = mmap(NULL, sizeof *key, prot, flags, -1, 0)) != NULL) {
|
||||
memset(key, 0, sizeof *key);
|
||||
key->mapped = 1;
|
||||
if (mlock(key, sizeof *key) == 0)
|
||||
key->locked = 1;
|
||||
} else {
|
||||
openpam_log(PAM_LOG_ERROR, "mmap(): %m");
|
||||
if ((key = calloc(sizeof *key, 1)) == NULL)
|
||||
openpam_log(PAM_LOG_ERROR, "malloc(): %m");
|
||||
}
|
||||
return (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_alloc function allocates and initializes an OATH key
|
||||
* structure.
|
||||
*
|
||||
* Keys allocated with =oath_key_alloc must be freed using =oath_key_free.
|
||||
*
|
||||
* >oath_key_free
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,76 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Creates a dummy OATH key structure
|
||||
*/
|
||||
|
||||
struct oath_key *
|
||||
oath_key_dummy(enum oath_mode mode, enum oath_hash hash, unsigned int digits)
|
||||
{
|
||||
struct oath_key *key;
|
||||
|
||||
if ((key = oath_key_alloc()) == NULL)
|
||||
return (NULL);
|
||||
key->mode = mode;
|
||||
key->digits = digits;
|
||||
key->counter = 0;
|
||||
key->timestep = 30;
|
||||
key->hash = hash;
|
||||
strcpy(key->label, "oath-dummy-key");
|
||||
key->labellen = strlen(key->label);
|
||||
key->keylen = sizeof key->key;
|
||||
return (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_dummy function allocates and initializes a dummy OATH key
|
||||
* structure.
|
||||
* Authentication attempts using a dummy key will always fail.
|
||||
*
|
||||
* Keys allocated with =oath_key_dummy must be freed using =oath_key_free.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
* >oath_key_free
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,78 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
/*
|
||||
* OATH
|
||||
*
|
||||
* Wipes and frees an OATH key structure
|
||||
*/
|
||||
|
||||
void
|
||||
oath_key_free(struct oath_key *key)
|
||||
{
|
||||
int mapped, locked;
|
||||
|
||||
if (key != NULL) {
|
||||
mapped = key->mapped;
|
||||
locked = key->locked;
|
||||
memset(key, 0, sizeof *key);
|
||||
if (mapped) {
|
||||
if (locked)
|
||||
munlock(key, sizeof *key);
|
||||
munmap(key, sizeof *key);
|
||||
} else {
|
||||
free(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The =oath_key_free function wipes and frees an OATH key structure which
|
||||
* was previously allocated using the =oath_key_alloc function.
|
||||
*
|
||||
* >oath_key_alloc
|
||||
*
|
||||
* AUTHOR UIO
|
||||
*/
|
|
@ -1,105 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <security/oath.h>
|
||||
|
||||
#define TOTP_TIME_STEP 30
|
||||
|
||||
unsigned int
|
||||
oath_totp(const uint8_t *K, size_t Klen, unsigned int Digit)
|
||||
{
|
||||
time_t now;
|
||||
|
||||
time(&now);
|
||||
return (oath_hotp(K, Klen, now / TOTP_TIME_STEP, Digit));
|
||||
}
|
||||
|
||||
unsigned int
|
||||
oath_totp_current(const struct oath_key *k)
|
||||
{
|
||||
unsigned int code;
|
||||
uint64_t seq;
|
||||
|
||||
if (k == NULL)
|
||||
return (-1);
|
||||
if (k->mode != om_totp)
|
||||
return (-1);
|
||||
if (k->timestep == 0)
|
||||
return (-1);
|
||||
seq = time(NULL) / k->timestep;
|
||||
code = oath_hotp(k->key, k->keylen, seq, k->digits);
|
||||
return (code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares the code provided by the user with expected values within a
|
||||
* given window. Returns 1 if there was a match, 0 if not, and -1 if an
|
||||
* error occurred.
|
||||
*/
|
||||
int
|
||||
oath_totp_match(struct oath_key *k, unsigned int response, int window)
|
||||
{
|
||||
unsigned int code;
|
||||
uint64_t seq;
|
||||
int dummy;
|
||||
|
||||
if (k == NULL)
|
||||
return (-1);
|
||||
if (window < 1)
|
||||
return (-1);
|
||||
if (k->mode != om_totp)
|
||||
return (-1);
|
||||
if (k->timestep == 0)
|
||||
return (-1);
|
||||
seq = time(NULL) / k->timestep;
|
||||
dummy = (strcmp(k->label, OATH_DUMMY_LABEL) == 0);
|
||||
for (int i = -window; i <= window; ++i) {
|
||||
#if OATH_TOTP_PREVENT_REUSE
|
||||
/* XXX disabled for now, should be a key parameter? */
|
||||
if (seq + i <= k->lastuse)
|
||||
continue;
|
||||
#endif
|
||||
code = oath_hotp(k->key, k->keylen, seq + i, k->digits);
|
||||
if (code == response && !dummy) {
|
||||
k->lastuse = seq;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
|
@ -18,6 +18,7 @@ noinst_HEADERS = \
|
|||
openpam_strlcat.h \
|
||||
openpam_strlcmp.h \
|
||||
openpam_strlcpy.h \
|
||||
openpam_strlset.h \
|
||||
openpam_vasprintf.h
|
||||
|
||||
libpam_la_SOURCES = \
|
||||
|
@ -44,9 +45,10 @@ libpam_la_SOURCES = \
|
|||
openpam_set_option.c \
|
||||
openpam_set_feature.c \
|
||||
openpam_static.c \
|
||||
openpam_straddch.c \
|
||||
openpam_strlcat.c \
|
||||
openpam_strlcpy.c \
|
||||
openpam_straddch.c \
|
||||
openpam_strlset.c \
|
||||
openpam_subst.c \
|
||||
openpam_vasprintf.c \
|
||||
openpam_ttyconv.c \
|
||||
|
@ -77,8 +79,8 @@ libpam_la_SOURCES = \
|
|||
pam_vprompt.c \
|
||||
$(NULL)
|
||||
|
||||
libpam_la_LDFLAGS = -no-undefined -version-info @LIB_MAJ@
|
||||
libpam_la_LIBADD = @DL_LIBS@
|
||||
libpam_la_LDFLAGS = -no-undefined -version-info $(LIB_MAJ)
|
||||
libpam_la_LIBADD = $(DL_LIBS)
|
||||
|
||||
EXTRA_DIST = \
|
||||
pam_authenticate_secondary.c \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2001-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -193,6 +193,7 @@ openpam_parse_chain(pam_handle_t *pamh,
|
|||
openpam_log(PAM_LOG_ERROR,
|
||||
"%s(%d): missing or invalid facility",
|
||||
filename, lineno);
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (facility != fclt && facility != PAM_FACILITY_ANY) {
|
||||
|
@ -208,18 +209,28 @@ openpam_parse_chain(pam_handle_t *pamh,
|
|||
openpam_log(PAM_LOG_ERROR,
|
||||
"%s(%d): missing or invalid service name",
|
||||
filename, lineno);
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (wordv[i] != NULL) {
|
||||
openpam_log(PAM_LOG_ERROR,
|
||||
"%s(%d): garbage at end of line",
|
||||
filename, lineno);
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
ret = openpam_load_chain(pamh, servicename, fclt);
|
||||
FREEV(wordc, wordv);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Bogus errno, but this ensures that the
|
||||
* outer loop does not just ignore the
|
||||
* error and keep searching.
|
||||
*/
|
||||
if (errno == ENOENT)
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -229,6 +240,7 @@ openpam_parse_chain(pam_handle_t *pamh,
|
|||
openpam_log(PAM_LOG_ERROR,
|
||||
"%s(%d): missing or invalid control flag",
|
||||
filename, lineno);
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -238,6 +250,7 @@ openpam_parse_chain(pam_handle_t *pamh,
|
|||
openpam_log(PAM_LOG_ERROR,
|
||||
"%s(%d): missing or invalid module name",
|
||||
filename, lineno);
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -247,8 +260,11 @@ openpam_parse_chain(pam_handle_t *pamh,
|
|||
this->flag = ctlf;
|
||||
|
||||
/* load module */
|
||||
if ((this->module = openpam_load_module(modulename)) == NULL)
|
||||
if ((this->module = openpam_load_module(modulename)) == NULL) {
|
||||
if (errno == ENOENT)
|
||||
errno = ENOEXEC;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* The remaining items in wordv are the module's
|
||||
|
@ -281,7 +297,11 @@ openpam_parse_chain(pam_handle_t *pamh,
|
|||
* The loop ended because openpam_readword() returned NULL, which
|
||||
* can happen for four different reasons: an I/O error (ferror(f)
|
||||
* is true), a memory allocation failure (ferror(f) is false,
|
||||
* errno is non-zero)
|
||||
* feof(f) is false, errno is non-zero), the file ended with an
|
||||
* unterminated quote or backslash escape (ferror(f) is false,
|
||||
* feof(f) is true, errno is non-zero), or the end of the file was
|
||||
* reached without error (ferror(f) is false, feof(f) is true,
|
||||
* errno is zero).
|
||||
*/
|
||||
if (ferror(f) || errno != 0)
|
||||
goto syserr;
|
||||
|
@ -390,6 +410,10 @@ openpam_load_chain(pam_handle_t *pamh,
|
|||
for (path = openpam_policy_path; *path != NULL; ++path) {
|
||||
/* construct filename */
|
||||
len = strlcpy(filename, *path, sizeof filename);
|
||||
if (len >= sizeof filename) {
|
||||
errno = ENAMETOOLONG;
|
||||
RETURNN(-1);
|
||||
}
|
||||
if (filename[len - 1] == '/') {
|
||||
len = strlcat(filename, service, sizeof filename);
|
||||
if (len >= sizeof filename) {
|
||||
|
@ -402,6 +426,9 @@ openpam_load_chain(pam_handle_t *pamh,
|
|||
}
|
||||
ret = openpam_load_file(pamh, service, facility,
|
||||
filename, style);
|
||||
/* success */
|
||||
if (ret > 0)
|
||||
RETURNN(ret);
|
||||
/* the file exists, but an error occurred */
|
||||
if (ret == -1 && errno != ENOENT)
|
||||
RETURNN(ret);
|
||||
|
@ -411,7 +438,8 @@ openpam_load_chain(pam_handle_t *pamh,
|
|||
}
|
||||
|
||||
/* no hit */
|
||||
RETURNN(0);
|
||||
errno = ENOENT;
|
||||
RETURNN(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -432,13 +460,17 @@ openpam_configure(pam_handle_t *pamh,
|
|||
openpam_log(PAM_LOG_ERROR, "invalid service name");
|
||||
RETURNC(PAM_SYSTEM_ERR);
|
||||
}
|
||||
if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0)
|
||||
goto load_err;
|
||||
if (openpam_load_chain(pamh, service, PAM_FACILITY_ANY) < 0) {
|
||||
if (errno != ENOENT)
|
||||
goto load_err;
|
||||
}
|
||||
for (fclt = 0; fclt < PAM_NUM_FACILITIES; ++fclt) {
|
||||
if (pamh->chains[fclt] != NULL)
|
||||
continue;
|
||||
if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
|
||||
goto load_err;
|
||||
if (OPENPAM_FEATURE(FALLBACK_TO_OTHER)) {
|
||||
if (openpam_load_chain(pamh, PAM_OTHER, fclt) < 0)
|
||||
goto load_err;
|
||||
}
|
||||
}
|
||||
RETURNC(PAM_SUCCESS);
|
||||
load_err:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -38,11 +38,19 @@
|
|||
#define is_digit(ch) \
|
||||
(ch >= '0' && ch <= '9')
|
||||
|
||||
/*
|
||||
* Evaluates to non-zero if the argument is a hex digit.
|
||||
*/
|
||||
#define is_xdigit(ch) \
|
||||
((ch >= '0' && ch <= '9') || \
|
||||
(ch >= 'a' && ch <= 'f') || \
|
||||
(ch >= 'A' && ch <= 'F'))
|
||||
|
||||
/*
|
||||
* Evaluates to non-zero if the argument is an uppercase letter.
|
||||
*/
|
||||
#define is_upper(ch) \
|
||||
(ch >= 'A' && ch <= 'A')
|
||||
(ch >= 'A' && ch <= 'Z')
|
||||
|
||||
/*
|
||||
* Evaluates to non-zero if the argument is a lowercase letter.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -41,6 +41,8 @@
|
|||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
#include "openpam_impl.h"
|
||||
|
@ -63,7 +65,7 @@ openpam_dispatch(pam_handle_t *pamh,
|
|||
int flags)
|
||||
{
|
||||
pam_chain_t *chain;
|
||||
int err, fail, r;
|
||||
int err, fail, nsuccess, r;
|
||||
int debug;
|
||||
|
||||
ENTER();
|
||||
|
@ -101,7 +103,9 @@ openpam_dispatch(pam_handle_t *pamh,
|
|||
}
|
||||
|
||||
/* execute */
|
||||
for (err = fail = 0; chain != NULL; chain = chain->next) {
|
||||
err = PAM_SUCCESS;
|
||||
fail = nsuccess = 0;
|
||||
for (; chain != NULL; chain = chain->next) {
|
||||
if (chain->module->func[primitive] == NULL) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: no %s()",
|
||||
chain->module->path, pam_sm_func_name[primitive]);
|
||||
|
@ -115,7 +119,7 @@ openpam_dispatch(pam_handle_t *pamh,
|
|||
openpam_log(PAM_LOG_LIBDEBUG, "calling %s() in %s",
|
||||
pam_sm_func_name[primitive], chain->module->path);
|
||||
r = (chain->module->func[primitive])(pamh, flags,
|
||||
chain->optc, (const char **)chain->optv);
|
||||
chain->optc, (const char **)(intptr_t)chain->optv);
|
||||
pamh->current = NULL;
|
||||
openpam_log(PAM_LOG_LIBDEBUG, "%s: %s(): %s",
|
||||
chain->module->path, pam_sm_func_name[primitive],
|
||||
|
@ -127,6 +131,7 @@ openpam_dispatch(pam_handle_t *pamh,
|
|||
if (r == PAM_IGNORE)
|
||||
continue;
|
||||
if (r == PAM_SUCCESS) {
|
||||
++nsuccess;
|
||||
/*
|
||||
* For pam_setcred() and pam_chauthtok() with the
|
||||
* PAM_PRELIM_CHECK flag, treat "sufficient" as
|
||||
|
@ -148,7 +153,7 @@ openpam_dispatch(pam_handle_t *pamh,
|
|||
* fail. If a required module fails, record the
|
||||
* return code from the first required module to fail.
|
||||
*/
|
||||
if (err == 0)
|
||||
if (err == PAM_SUCCESS)
|
||||
err = r;
|
||||
if ((chain->flag == PAM_REQUIRED ||
|
||||
chain->flag == PAM_BINDING) && !fail) {
|
||||
|
@ -170,6 +175,18 @@ openpam_dispatch(pam_handle_t *pamh,
|
|||
|
||||
if (!fail && err != PAM_NEW_AUTHTOK_REQD)
|
||||
err = PAM_SUCCESS;
|
||||
|
||||
/*
|
||||
* Require the chain to be non-empty, and at least one module
|
||||
* in the chain to be successful, so that we don't fail open.
|
||||
*/
|
||||
if (err == PAM_SUCCESS && nsuccess < 1) {
|
||||
openpam_log(PAM_LOG_ERROR,
|
||||
"all modules were unsuccessful for %s()",
|
||||
pam_sm_func_name[primitive]);
|
||||
err = PAM_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
RETURNC(err);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -65,4 +65,9 @@ struct openpam_feature openpam_features[OPENPAM_NUM_FEATURES] = {
|
|||
"Verify ownership and permissions of module files",
|
||||
1
|
||||
),
|
||||
STRUCT_OPENPAM_FEATURE(
|
||||
FALLBACK_TO_OTHER,
|
||||
"Fall back to \"other\" policy for empty chains",
|
||||
1
|
||||
),
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2013 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -84,6 +84,7 @@ openpam_load_module(const char *modulename)
|
|||
static void
|
||||
openpam_release_module(pam_module_t *module)
|
||||
{
|
||||
|
||||
if (module == NULL)
|
||||
return;
|
||||
if (module->dlh == NULL)
|
||||
|
@ -104,6 +105,7 @@ openpam_release_module(pam_module_t *module)
|
|||
static void
|
||||
openpam_destroy_chain(pam_chain_t *chain)
|
||||
{
|
||||
|
||||
if (chain == NULL)
|
||||
return;
|
||||
openpam_destroy_chain(chain->next);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -79,6 +79,7 @@ openpam_readlinev(FILE *f, int *lineno, int *lenp)
|
|||
/* insert our word */
|
||||
wordv[wordvlen++] = word;
|
||||
wordv[wordvlen] = NULL;
|
||||
word = NULL;
|
||||
}
|
||||
if (errno != 0) {
|
||||
/* I/O error or out of memory */
|
||||
|
@ -86,6 +87,7 @@ openpam_readlinev(FILE *f, int *lineno, int *lenp)
|
|||
while (wordvlen--)
|
||||
free(wordv[wordvlen]);
|
||||
free(wordv);
|
||||
free(word);
|
||||
errno = serrno;
|
||||
return (NULL);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -55,18 +55,35 @@ openpam_readword(FILE *f, int *lineno, size_t *lenp)
|
|||
{
|
||||
char *word;
|
||||
size_t size, len;
|
||||
int ch, comment, escape, quote;
|
||||
int ch, escape, quote;
|
||||
int serrno;
|
||||
|
||||
errno = 0;
|
||||
|
||||
/* skip initial whitespace */
|
||||
comment = 0;
|
||||
while ((ch = getc(f)) != EOF && ch != '\n') {
|
||||
if (ch == '#')
|
||||
comment = 1;
|
||||
if (!is_lws(ch) && !comment)
|
||||
escape = quote = 0;
|
||||
while ((ch = getc(f)) != EOF) {
|
||||
if (ch == '\n') {
|
||||
/* either EOL or line continuation */
|
||||
if (!escape)
|
||||
break;
|
||||
if (lineno != NULL)
|
||||
++*lineno;
|
||||
escape = 0;
|
||||
} else if (escape) {
|
||||
/* escaped something else */
|
||||
break;
|
||||
} else if (ch == '#') {
|
||||
/* comment: until EOL, no continuation */
|
||||
while ((ch = getc(f)) != EOF)
|
||||
if (ch == '\n')
|
||||
break;
|
||||
break;
|
||||
} else if (ch == '\\') {
|
||||
escape = 1;
|
||||
} else if (!is_ws(ch)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ch == EOF)
|
||||
return (NULL);
|
||||
|
@ -76,7 +93,6 @@ openpam_readword(FILE *f, int *lineno, size_t *lenp)
|
|||
|
||||
word = NULL;
|
||||
size = len = 0;
|
||||
escape = quote = 0;
|
||||
while ((ch = fgetc(f)) != EOF && (!is_ws(ch) || quote || escape)) {
|
||||
if (ch == '\\' && !escape && quote != '\'') {
|
||||
/* escape next character */
|
||||
|
@ -90,7 +106,7 @@ openpam_readword(FILE *f, int *lineno, size_t *lenp)
|
|||
} else if (ch == quote && !escape) {
|
||||
/* end quote */
|
||||
quote = 0;
|
||||
} else if (ch == '\n' && escape && quote != '\'') {
|
||||
} else if (ch == '\n' && escape) {
|
||||
/* line continuation */
|
||||
escape = 0;
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* Copyright (c) 2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -29,34 +29,30 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_TYPES_H_INCLUDED
|
||||
#define OATH_TYPES_H_INCLUDED
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLSET
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "openpam_strlset.h"
|
||||
|
||||
/*
|
||||
* OATH key and associated parameters
|
||||
* like memset(3), but stops at the first NUL byte and NUL-terminates the
|
||||
* result. Returns the number of bytes that were written, not including
|
||||
* the terminating NUL.
|
||||
*/
|
||||
struct oath_key {
|
||||
/* mode and parameters */
|
||||
enum oath_mode mode;
|
||||
unsigned int digits;
|
||||
uint64_t counter;
|
||||
unsigned int timestep; /* in seconds */
|
||||
uint64_t lastuse;
|
||||
size_t
|
||||
openpam_strlset(char *str, int ch, size_t size)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
/* housekeeping */
|
||||
unsigned int mapped:1;
|
||||
unsigned int locked:1;
|
||||
|
||||
/* hash algorithm */
|
||||
enum oath_hash hash;
|
||||
|
||||
/* label */
|
||||
size_t labellen; /* bytes incl. NUL */
|
||||
char label[OATH_MAX_LABELLEN];
|
||||
|
||||
/* key */
|
||||
size_t keylen; /* bytes */
|
||||
uint8_t key[OATH_MAX_KEYLEN];
|
||||
};
|
||||
for (len = 0; *str && size > 1; ++len, --size)
|
||||
*str++ = ch;
|
||||
*str = '\0';
|
||||
return (++len);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* Copyright (c) 2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -29,14 +29,13 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_IMPL_H_INCLUDED
|
||||
#define OATH_IMPL_H_INCLUDED
|
||||
#ifndef OPENPAM_STRLSET_H_INCLUDED
|
||||
#define OPENPAM_STRLSET_H_INCLUDED
|
||||
|
||||
/*
|
||||
* Dummy key parameters
|
||||
*/
|
||||
#define OATH_DUMMY_LABEL ("oath-dummy-key")
|
||||
#define OATH_DUMMY_LABELLEN (sizeof DUMMY_LABEL)
|
||||
#define OATH_DUMMY_KEYLEN 80
|
||||
#ifndef HAVE_STRLSET
|
||||
size_t openpam_strlset(char *, int, size_t);
|
||||
#undef strlset
|
||||
#define strlset(arg, ...) openpam_strlset(arg, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2014 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -55,10 +55,11 @@
|
|||
#include <security/pam_appl.h>
|
||||
|
||||
#include "openpam_impl.h"
|
||||
#include "openpam_strlset.h"
|
||||
|
||||
int openpam_ttyconv_timeout = 0;
|
||||
|
||||
volatile sig_atomic_t caught_signal;
|
||||
static volatile sig_atomic_t caught_signal;
|
||||
|
||||
/*
|
||||
* Handle incoming signals during tty conversation
|
||||
|
@ -366,7 +367,7 @@ openpam_ttyconv(int n,
|
|||
fail:
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (aresp[i].resp != NULL) {
|
||||
memset(aresp[i].resp, 0, strlen(aresp[i].resp));
|
||||
strlset(aresp[i].resp, 0, PAM_MAX_RESP_SIZE);
|
||||
FREE(aresp[i].resp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2017 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -48,6 +48,7 @@
|
|||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_impl.h"
|
||||
#include "openpam_strlset.h"
|
||||
|
||||
static const char authtok_prompt[] = "Password:";
|
||||
static const char authtok_prompt_remote[] = "Password for %u@%h:";
|
||||
|
@ -121,9 +122,11 @@ pam_get_authtok(pam_handle_t *pamh,
|
|||
if ((promptp = openpam_get_option(pamh, prompt_option)) != NULL)
|
||||
prompt = promptp;
|
||||
/* no prompt provided, see if there is one tucked away somewhere */
|
||||
if (prompt == NULL)
|
||||
if (pam_get_item(pamh, pitem, &promptp) && promptp != NULL)
|
||||
if (prompt == NULL) {
|
||||
r = pam_get_item(pamh, pitem, &promptp);
|
||||
if (r == PAM_SUCCESS && promptp != NULL)
|
||||
prompt = promptp;
|
||||
}
|
||||
/* fall back to hardcoded default */
|
||||
if (prompt == NULL)
|
||||
prompt = default_prompt;
|
||||
|
@ -140,16 +143,21 @@ pam_get_authtok(pam_handle_t *pamh,
|
|||
if (twice) {
|
||||
r = pam_prompt(pamh, style, &resp2, "Retype %s", prompt);
|
||||
if (r != PAM_SUCCESS) {
|
||||
strlset(resp, 0, PAM_MAX_RESP_SIZE);
|
||||
FREE(resp);
|
||||
RETURNC(r);
|
||||
}
|
||||
if (strcmp(resp, resp2) != 0)
|
||||
if (strcmp(resp, resp2) != 0) {
|
||||
strlset(resp, 0, PAM_MAX_RESP_SIZE);
|
||||
FREE(resp);
|
||||
}
|
||||
strlset(resp2, 0, PAM_MAX_RESP_SIZE);
|
||||
FREE(resp2);
|
||||
}
|
||||
if (resp == NULL)
|
||||
RETURNC(PAM_TRY_AGAIN);
|
||||
r = pam_set_item(pamh, item, resp);
|
||||
strlset(resp, 0, PAM_MAX_RESP_SIZE);
|
||||
FREE(resp);
|
||||
if (r != PAM_SUCCESS)
|
||||
RETURNC(r);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2017 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -78,10 +78,11 @@ pam_get_user(pam_handle_t *pamh,
|
|||
if ((promptp = openpam_get_option(pamh, "user_prompt")) != NULL)
|
||||
prompt = promptp;
|
||||
/* no prompt provided, see if there is one tucked away somewhere */
|
||||
if (prompt == NULL)
|
||||
if (pam_get_item(pamh, PAM_USER_PROMPT, &promptp) &&
|
||||
promptp != NULL)
|
||||
if (prompt == NULL) {
|
||||
r = pam_get_item(pamh, PAM_USER_PROMPT, &promptp);
|
||||
if (r == PAM_SUCCESS && promptp != NULL)
|
||||
prompt = promptp;
|
||||
}
|
||||
/* fall back to hardcoded default */
|
||||
if (prompt == NULL)
|
||||
prompt = user_prompt;
|
||||
|
|
|
@ -55,7 +55,7 @@ m/OPENPAM_VERSION/ && s/\d{8}/'"${isodate}"'/;
|
|||
|
||||
echo "configure.ac"
|
||||
perl -p -i -e '
|
||||
m/AC_INIT/ && s/trunk|\d{8}/'"${isodate}"'/
|
||||
m/AC_INIT/ && s/nooath|\d{8}/'"${isodate}"'/
|
||||
' configure.ac
|
||||
|
||||
echo "man pages"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/perl -w
|
||||
#-
|
||||
# Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
# Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
# Copyright (c) 2004-2014 Dag-Erling Smørgrav
|
||||
# All rights reserved.
|
||||
#
|
||||
# This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -265,7 +265,7 @@ sub parse_source($) {
|
|||
$man .= ".Bl -tag -width 18n\n";
|
||||
$intaglist = 1;
|
||||
}
|
||||
s/^\.It [=;]([A-Za-z][A-Za-z_]+)$/.It Dv $1/gs;
|
||||
s/^\.It [=;]([A-Za-z][0-9A-Za-z_]+)$/.It Dv $1/gs;
|
||||
$man .= "$_\n";
|
||||
next;
|
||||
} elsif (($inlist || $intaglist) && m/^\S/) {
|
||||
|
@ -292,17 +292,17 @@ sub parse_source($) {
|
|||
}
|
||||
s/\s*=($func)\b\s*/\n.Fn $1\n/gs;
|
||||
s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs;
|
||||
s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
|
||||
s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs;
|
||||
s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs;
|
||||
s/\s*=!([a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
|
||||
while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
|
||||
s/\s*=((?:enum|struct|union) \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
|
||||
s/\s*:([a-z][0-9a-z_]+)\b\s*/\n.Va $1\n/gs;
|
||||
s/\s*;([a-z][0-9a-z_]+)\b\s*/\n.Dv $1\n/gs;
|
||||
s/\s*=!([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
|
||||
while (s/\s*=([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
|
||||
++$xref{3}->{$1};
|
||||
}
|
||||
s/\s*\"(?=\w)/\n.Do\n/gs;
|
||||
s/\"(?!\w)\s*/\n.Dc\n/gs;
|
||||
s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
|
||||
s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
|
||||
s/\s*=([A-Z][0-9A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
|
||||
s/\s*=([A-Z][0-9A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
|
||||
s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
|
||||
$man .= "$_\n";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/perl -Tw
|
||||
#-
|
||||
# Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
# Copyright (c) 2012-2014 Dag-Erling Smørgrav
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
@ -33,26 +33,30 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $CVEURL = "http://web.nvd.nist.gov/view/vuln/detail?vulnId=";
|
||||
|
||||
while (<>) {
|
||||
if (m/^OpenPAM ([A-Z][a-z]+)\t+(\d\d\d\d-\d\d-\d\d)\s*$/) {
|
||||
my ($relname, $reldate) = ($1, $2);
|
||||
my $wikitext = "= OpenPAM $relname =\n" .
|
||||
"\n" .
|
||||
"OpenPAM $relname was released on $reldate.\n";
|
||||
my $changes;
|
||||
while (<>) {
|
||||
last if m/^=+$/;
|
||||
$wikitext .= $_;
|
||||
$changes .= $_;
|
||||
}
|
||||
$wikitext =~ s/^ - ([A-Z]+): / - '''$1''' /gm;
|
||||
$wikitext =~ s/(\w+\(\d*\))/`$1`/gs;
|
||||
$wikitext =~ s/([^'])\b([A-Z_]{2,})\b([^'])/$1`$2`$3/gs;
|
||||
$wikitext =~ s/([.!?])\n +(\w)/$1 $2/gs;
|
||||
$wikitext =~ s/(\S)\n +(\S)/$1 $2/gs;
|
||||
$wikitext .= "\n" .
|
||||
"[http://sourceforge.net/projects/openpam/files/openpam/$relname/ Download from Sourceforge]\n";
|
||||
$changes =~ s/^ - ([A-Z]+): / - '''$1''' /gm;
|
||||
$changes =~ s/([\w.-]+\(\d*\))/`$1`/gs;
|
||||
$changes =~ s/([^'`])\b([A-Z_]{4,})\b([^'`])/$1`$2`$3/gs;
|
||||
$changes =~ s/(CVE-\d{4}-\d+)/[$CVEURL$1 $1]/gs;
|
||||
$changes =~ s/([.!?])\n +(\w)/$1 $2/gs;
|
||||
$changes =~ s/(\S)\n +(\S)/$1 $2/gs;
|
||||
open(my $fh, ">", "$relname.txt")
|
||||
or die("$relname.txt: $!\n");
|
||||
print($fh $wikitext);
|
||||
print($fh "= OpenPAM $relname =\n",
|
||||
"\n",
|
||||
"OpenPAM $relname was released on $reldate.\n",
|
||||
$changes,
|
||||
"\n",
|
||||
"[http://sourceforge.net/projects/openpam/files/openpam/$relname/ Download from Sourceforge]\n");
|
||||
close($fh);
|
||||
print("|| $reldate || [[Releases/$relname|$relname]] ||\n");
|
||||
}
|
||||
|
|
114
mkpkgng.in
114
mkpkgng.in
|
@ -1,4 +1,31 @@
|
|||
#!/bin/sh
|
||||
#-
|
||||
# Copyright (c) 2013-2014 Dag-Erling Smørgrav
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote
|
||||
# products derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
@ -34,17 +61,15 @@ yesno() {
|
|||
# Locate source and build directory
|
||||
#
|
||||
srcdir="@abs_top_srcdir@"
|
||||
[ -f "$srcdir/include/security/openpam.h" ] || \
|
||||
error "Unable to locate source directory."
|
||||
builddir="@abs_top_builddir@"
|
||||
cd "$srcdir"
|
||||
|
||||
#
|
||||
# Determine pkgng version and ABI
|
||||
#
|
||||
pkgver=$(pkg query %v pkg)
|
||||
pkgver=$(pkg -v)
|
||||
[ -n "$pkgver" ] || error "Unable to determine pkgng version."
|
||||
pkgabi=$(pkg -vv | awk '$1 == "ABI:" { print $2 }')
|
||||
pkgabi=$(pkg config abi)
|
||||
[ -n "$pkgabi" ] || error "Unable to determine package ABI."
|
||||
|
||||
#
|
||||
|
@ -56,7 +81,8 @@ if ! expr "$version" : "[0-9]{1,}$" >/dev/null ; then
|
|||
svnversion="$(svnversion 2>&1)"
|
||||
svnversion=$(expr "$svnversion" : '\([0-9][0-9]*\)[A-Z]\{0,1\}$')
|
||||
if [ -n "$svnversion" ] ; then
|
||||
version="$version-r${svnversion}"
|
||||
package="$package-$version"
|
||||
version="r$svnversion"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -73,69 +99,79 @@ make="$make --no-print-directory --quiet V=0"
|
|||
#
|
||||
# Create temporary directory
|
||||
#
|
||||
info "Creating temporary directory."
|
||||
info "Creating the temporary directory."
|
||||
tmproot=$(mktemp -d "${TMPDIR:-/tmp}/$package-$version.XXXXXX")
|
||||
[ -n "$tmproot" -a -d "$tmproot" ] || error "unable to create temporary directory"
|
||||
[ -n "$tmproot" -a -d "$tmproot" ] || \
|
||||
error "Unable to create the temporary directory."
|
||||
trap "exit 1" INT
|
||||
trap "info Deleting temporary directory. ; rm -rf '$tmproot'" EXIT
|
||||
trap "info Deleting the temporary directory. ; rm -rf '$tmproot'" EXIT
|
||||
set -e
|
||||
|
||||
#
|
||||
# Install into tmproot
|
||||
#
|
||||
info "Installing into temporary directory."
|
||||
info "Installing into the temporary directory."
|
||||
$make install DESTDIR="$tmproot"
|
||||
|
||||
#
|
||||
# Compress man pages
|
||||
#
|
||||
find $tmproot -type d -name 'man[0-9]' |
|
||||
while read mandir ; do
|
||||
find $mandir -type f -name '*.[0-9]' |
|
||||
while read manpage ; do
|
||||
gzip "$manpage"
|
||||
done
|
||||
find $mandir -type l -name '*.[0-9]' |
|
||||
while read manlink ; do
|
||||
ln -s "$(readlink $manlink).gz" "$manlink.gz"
|
||||
done
|
||||
done
|
||||
|
||||
#
|
||||
# Generate stub manifest
|
||||
#
|
||||
info "Generating stub manifest."
|
||||
info "Generating the stub manifest."
|
||||
manifest="$tmproot/+MANIFEST"
|
||||
cat >"$manifest" <<EOF
|
||||
name: $package
|
||||
version: $version
|
||||
origin: local/openpam
|
||||
origin: local/$package
|
||||
comment: BSD-licensed PAM implementation
|
||||
arch: $pkgabi
|
||||
www: @PACKAGE_URL@
|
||||
maintainer: @PACKAGE_BUGREPORT@
|
||||
prefix: @prefix@
|
||||
desc:
|
||||
OpenPAM is an open source PAM library that focuses on simplicity,
|
||||
correctness, and cleanliness.
|
||||
|
||||
OpenPAM aims to gather the best features of Solaris PAM, XSSO and
|
||||
Linux-PAM, plus some innovations of its own. In areas where these
|
||||
implementations disagree, OpenPAM tries to remain compatible with
|
||||
Solaris, at the expense of XSSO conformance and Linux-PAM
|
||||
compatibility.
|
||||
categories: local, security
|
||||
categories: [ local, security ]
|
||||
EOF
|
||||
cp "$srcdir/README" "$tmproot/+DESC"
|
||||
|
||||
#
|
||||
# Generate file list
|
||||
info "Generating file list."
|
||||
#
|
||||
info "Generating the file list."
|
||||
(
|
||||
echo "files:"
|
||||
find "$tmproot" -type f | while read file ; do
|
||||
[ "$file" = "$manifest" ] && continue
|
||||
echo "files: {"
|
||||
find -s "$tmproot@prefix@" -type f -or -type l | while read file ; do
|
||||
case $file in
|
||||
*.la)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
mode=$(stat -f%p "$file" | cut -c 3-)
|
||||
file="${file#$tmproot}"
|
||||
echo " $file: { uname: root, gname: wheel, perm: $mode }"
|
||||
done
|
||||
echo "}"
|
||||
)>>"$manifest"
|
||||
|
||||
# As of pkg 1.1.4, the shlib detection logic in "pkg create" only
|
||||
# works when tmproot == "/", so instead of creating a package directly
|
||||
# from the contents of $tmproot, we have to install to / and package
|
||||
# that.
|
||||
info "Packaging."
|
||||
if [ "$pkgver" \< "1.1.5" ] ; then
|
||||
info "pkg 1.1.4 or older detected."
|
||||
yesno "We must now install to /. Proceed?" || error "Chicken."
|
||||
$make install
|
||||
pkg create -m "$tmproot" -o "$builddir"
|
||||
else
|
||||
pkg create -r "$tmproot" -m "$tmproot" -o "$builddir"
|
||||
fi
|
||||
#
|
||||
# Create the package
|
||||
#
|
||||
info "Creating the package."
|
||||
pkg create -r "$tmproot" -m "$tmproot" -o "$builddir"
|
||||
|
||||
echo "Package created for $package-$version."
|
||||
#
|
||||
# Done
|
||||
#
|
||||
info "Package created for $package-$version."
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
# $Id$
|
||||
|
||||
SUBDIRS = pam_deny pam_permit
|
||||
SUBDIRS = pam_deny pam_permit pam_return
|
||||
|
||||
if WITH_PAM_UNIX
|
||||
SUBDIRS += pam_unix
|
||||
endif
|
||||
|
||||
if WITH_OATH
|
||||
SUBDIRS += pam_oath
|
||||
endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# $Id$
|
||||
|
||||
if CUSTOM_MODULES_DIR
|
||||
moduledir = @OPENPAM_MODULES_DIR@
|
||||
moduledir = $(OPENPAM_MODULES_DIR)
|
||||
else
|
||||
moduledir = $(libdir)
|
||||
endif
|
||||
|
@ -10,6 +10,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||
module_LTLIBRARIES = pam_deny.la
|
||||
|
||||
pam_deny_la_SOURCES = pam_deny.c
|
||||
pam_deny_la_LDFLAGS = -no-undefined -module -version-info @LIB_MAJ@ \
|
||||
pam_deny_la_LDFLAGS = -no-undefined -module -version-info $(LIB_MAJ) \
|
||||
-export-symbols-regex '^pam_sm_'
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
pam_deny_la_LIBADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
pam_deny_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# $Id$
|
||||
|
||||
if CUSTOM_MODULES_DIR
|
||||
moduledir = @OPENPAM_MODULES_DIR@
|
||||
else
|
||||
moduledir = $(libdir)
|
||||
endif
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
module_LTLIBRARIES = pam_oath.la
|
||||
|
||||
pam_oath_la_SOURCES = pam_oath.c
|
||||
pam_oath_la_LDFLAGS = -no-undefined -module -version-info @LIB_MAJ@ \
|
||||
-export-symbols-regex '^pam_sm_'
|
||||
pam_oath_la_LIBADD = \
|
||||
$(top_builddir)/lib/libpam/libpam.la \
|
||||
$(top_builddir)/lib/liboath/liboath.la
|
|
@ -1,319 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PAM_SM_AUTH
|
||||
#define PAM_SM_ACCOUNT
|
||||
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
#define PAM_OATH_PROMPT "Verification code: "
|
||||
#define PAM_OATH_HOTP_WINDOW 3
|
||||
#define PAM_OATH_TOTP_WINDOW 3
|
||||
|
||||
enum pam_oath_nokey { nokey_error = -1, nokey_fail, nokey_fake, nokey_ignore };
|
||||
|
||||
static const char *pam_oath_default_keyfile = "/var/oath/%u.otpauth";
|
||||
|
||||
/*
|
||||
* Parse the nokey or badkey option, which indicates how we should act if
|
||||
* the user has no keyfile or the keyfile is invalid.
|
||||
*/
|
||||
static enum pam_oath_nokey
|
||||
pam_oath_nokey_option(pam_handle_t *pamh, const char *option)
|
||||
{
|
||||
const char *value;
|
||||
|
||||
if ((value = openpam_get_option(pamh, option)) == NULL)
|
||||
return (nokey_fail);
|
||||
else if (strcmp(value, "fail") == 0)
|
||||
return (nokey_fail);
|
||||
else if (strcmp(value, "fake") == 0)
|
||||
return (nokey_fake);
|
||||
else if (strcmp(value, "ignore") == 0)
|
||||
return (nokey_ignore);
|
||||
openpam_log(PAM_LOG_ERROR, "the value of the %s option "
|
||||
"must be either 'fail', 'fake' or 'ignore'", option);
|
||||
return (nokey_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a numeric option. Returns -1 if the option is not set or its
|
||||
* value is not an integer in the range [0, INT_MAX].
|
||||
*/
|
||||
static int
|
||||
pam_oath_int_option(pam_handle_t *pamh, const char *option)
|
||||
{
|
||||
const char *value;
|
||||
char *end;
|
||||
long num;
|
||||
|
||||
if ((value = openpam_get_option(pamh, option)) == NULL)
|
||||
return (-1);
|
||||
num = strtol(value, &end, 10);
|
||||
if (*value == '\0' || *end != '\0' || num < 0 || num > INT_MAX) {
|
||||
openpam_log(PAM_LOG_ERROR, "the value of the %s option "
|
||||
"is invalid.", option);
|
||||
return (-1);
|
||||
}
|
||||
return (num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the location of the user's keyfile.
|
||||
*/
|
||||
static char *
|
||||
pam_oath_keyfile(pam_handle_t *pamh)
|
||||
{
|
||||
const char *keyfile;
|
||||
char *path;
|
||||
size_t size;
|
||||
|
||||
if ((keyfile = openpam_get_option(pamh, "keyfile")) == NULL)
|
||||
keyfile = pam_oath_default_keyfile;
|
||||
size = 0;
|
||||
if (openpam_subst(pamh, NULL, &size, keyfile) != PAM_TRY_AGAIN)
|
||||
return (NULL);
|
||||
if ((path = malloc(size)) == NULL)
|
||||
return (NULL);
|
||||
if (openpam_subst(pamh, path, &size, keyfile) != PAM_SUCCESS) {
|
||||
free(path);
|
||||
return (NULL);
|
||||
}
|
||||
return (path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the user's key.
|
||||
*/
|
||||
static struct oath_key *
|
||||
pam_oath_load_key(const char *keyfile)
|
||||
{
|
||||
|
||||
/* XXX should check ownership and permissions */
|
||||
return (oath_key_from_file(keyfile));
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the user's key.
|
||||
*/
|
||||
static int
|
||||
pam_oath_save_key(const struct oath_key *key, const char *keyfile)
|
||||
{
|
||||
char *keyuri;
|
||||
int fd, len, pam_err;
|
||||
|
||||
keyuri = NULL;
|
||||
len = 0;
|
||||
fd = -1;
|
||||
pam_err = PAM_SYSTEM_ERR;
|
||||
if ((keyuri = oath_key_to_uri(key)) == NULL)
|
||||
goto done;
|
||||
len = strlen(keyuri);
|
||||
if ((fd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0 ||
|
||||
write(fd, keyuri, len) != len || write(fd, "\n", 1) != 1) {
|
||||
openpam_log(PAM_LOG_ERROR, "%s: %m", keyfile);
|
||||
goto done;
|
||||
}
|
||||
pam_err = PAM_SUCCESS;
|
||||
done:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (keyfile != NULL) {
|
||||
memset(keyuri, 0, len);
|
||||
free(keyuri);
|
||||
}
|
||||
return (pam_err);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
enum pam_oath_nokey nokey, badkey;
|
||||
struct passwd *pwd;
|
||||
const char *user;
|
||||
char *keyfile;
|
||||
struct oath_key *key;
|
||||
unsigned long response;
|
||||
char *password, *end;
|
||||
int pam_err, ret, window;
|
||||
|
||||
/* unused */
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
keyfile = NULL;
|
||||
key = NULL;
|
||||
|
||||
openpam_log(PAM_LOG_VERBOSE, "attempting OATH authentication");
|
||||
|
||||
/* check how to behave if the user does not have a valid key */
|
||||
if ((nokey = pam_oath_nokey_option(pamh, "nokey")) == nokey_error ||
|
||||
(badkey = pam_oath_nokey_option(pamh, "badkey")) == nokey_error) {
|
||||
pam_err = PAM_SERVICE_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* identify user */
|
||||
if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
|
||||
goto done;
|
||||
if ((pwd = getpwnam(user)) == NULL) {
|
||||
pam_err = PAM_USER_UNKNOWN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
openpam_log(PAM_LOG_VERBOSE, "authenticating user %s", user);
|
||||
|
||||
/* load key */
|
||||
if ((keyfile = pam_oath_keyfile(pamh)) == NULL) {
|
||||
pam_err = PAM_SYSTEM_ERR;
|
||||
goto done;
|
||||
}
|
||||
openpam_log(PAM_LOG_VERBOSE, "attempting to load %s for %s", keyfile, user);
|
||||
key = pam_oath_load_key(keyfile);
|
||||
|
||||
/*
|
||||
* The user doesn't have a key, should we fake it?
|
||||
*
|
||||
* XXX implement badkey - currently, oath_key_from_file() doesn't
|
||||
* provide enough information for us to tell the difference
|
||||
* between a bad key and no key at all.
|
||||
*
|
||||
* XXX move this into pam_oath_load_key()
|
||||
*/
|
||||
if (key == NULL) {
|
||||
openpam_log(PAM_LOG_VERBOSE, "no key found for %s", user);
|
||||
switch (nokey) {
|
||||
case nokey_fail:
|
||||
pam_err = PAM_AUTHINFO_UNAVAIL;
|
||||
goto done;
|
||||
case nokey_fake:
|
||||
key = oath_key_dummy(om_hotp, oh_sha1, 6);
|
||||
break;
|
||||
case nokey_ignore:
|
||||
pam_err = PAM_IGNORE;
|
||||
goto done;
|
||||
default:
|
||||
/* can't happen */
|
||||
pam_err = PAM_SERVICE_ERR;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* get user's response */
|
||||
pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
|
||||
(const char **)&password, PAM_OATH_PROMPT);
|
||||
if (pam_err != PAM_SUCCESS) {
|
||||
openpam_log(PAM_LOG_VERBOSE, "conversation failure");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* convert to number */
|
||||
response = strtoul(password, &end, 10);
|
||||
if (end == password || *end != '\0')
|
||||
response = ULONG_MAX;
|
||||
|
||||
/* verify response */
|
||||
if (key->mode == om_hotp) {
|
||||
if ((window = pam_oath_int_option(pamh, "hotp_window")) < 0 &&
|
||||
(window = pam_oath_int_option(pamh, "window")) < 0)
|
||||
window = PAM_OATH_HOTP_WINDOW;
|
||||
ret = oath_hotp_match(key, response, window);
|
||||
} else {
|
||||
if ((window = pam_oath_int_option(pamh, "totp_window")) < 0 &&
|
||||
(window = pam_oath_int_option(pamh, "window")) < 0)
|
||||
window = PAM_OATH_TOTP_WINDOW;
|
||||
ret = oath_totp_match(key, response, window);
|
||||
}
|
||||
openpam_log(PAM_LOG_VERBOSE, "verification code %s",
|
||||
ret ? "matched" : "did not match");
|
||||
if (ret == 0) {
|
||||
pam_err = PAM_AUTH_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* write back (update counter for HOTP etc) */
|
||||
if (pam_oath_save_key(key, keyfile) != 0) {
|
||||
pam_err = PAM_SERVICE_ERR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
openpam_log(PAM_LOG_VERBOSE, "OATH authentication succeeded");
|
||||
pam_err = PAM_SUCCESS;
|
||||
done:
|
||||
oath_key_free(key);
|
||||
free(keyfile);
|
||||
return (pam_err);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_setcred(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
/* unused */
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
/* unused */
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
|
||||
PAM_MODULE_ENTRY("pam_unix");
|
|
@ -1,7 +1,7 @@
|
|||
# $Id$
|
||||
|
||||
if CUSTOM_MODULES_DIR
|
||||
moduledir = @OPENPAM_MODULES_DIR@
|
||||
moduledir = $(OPENPAM_MODULES_DIR)
|
||||
else
|
||||
moduledir = $(libdir)
|
||||
endif
|
||||
|
@ -10,6 +10,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||
module_LTLIBRARIES = pam_permit.la
|
||||
|
||||
pam_permit_la_SOURCES = pam_permit.c
|
||||
pam_permit_la_LDFLAGS = -no-undefined -module -version-info @LIB_MAJ@ \
|
||||
pam_permit_la_LDFLAGS = -no-undefined -module -version-info $(LIB_MAJ) \
|
||||
-export-symbols-regex '^pam_sm_'
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
pam_permit_la_LIBADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
pam_permit_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
|
19
modules/pam_return/Makefile.am
Normal file
19
modules/pam_return/Makefile.am
Normal file
|
@ -0,0 +1,19 @@
|
|||
# $Id$
|
||||
|
||||
if CUSTOM_MODULES_DIR
|
||||
moduledir = $(OPENPAM_MODULES_DIR)
|
||||
else
|
||||
moduledir = $(libdir)
|
||||
endif
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/libpam
|
||||
|
||||
module_LTLIBRARIES = pam_return.la
|
||||
|
||||
pam_return_la_SOURCES = pam_return.c
|
||||
pam_return_la_LDFLAGS = -no-undefined -module -version-info $(LIB_MAJ) \
|
||||
-export-symbols-regex '^pam_sm_'
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
pam_return_la_LIBADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
pam_return_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
127
modules/pam_return/pam_return.c
Normal file
127
modules/pam_return/pam_return.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*-
|
||||
* Copyright (c) 2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_impl.h"
|
||||
|
||||
static int
|
||||
pam_return(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
const char *errname;
|
||||
char *e;
|
||||
long errcode;
|
||||
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
if ((errname = openpam_get_option(pamh, "error")) == NULL ||
|
||||
errname[0] == '\0') {
|
||||
openpam_log(PAM_LOG_ERROR, "missing error parameter");
|
||||
return (PAM_SYSTEM_ERR);
|
||||
}
|
||||
/* is it a number? */
|
||||
errcode = strtol(errname, &e, 10);
|
||||
if (e != NULL && *e == '\0') {
|
||||
/* yep, check range */
|
||||
if (errcode >= INT_MIN && errcode <= INT_MAX)
|
||||
return (errcode);
|
||||
} else {
|
||||
/* nope, look it up */
|
||||
for (errcode = 0; errcode < PAM_NUM_ERRORS; ++errcode)
|
||||
if (strcmp(errname, pam_err_name[errcode]) == 0)
|
||||
return (errcode);
|
||||
}
|
||||
openpam_log(PAM_LOG_ERROR, "invalid error code '%s'", errname);
|
||||
return (PAM_SYSTEM_ERR);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
return (pam_return(pamh, flags, argc, argv));
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_setcred(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
return (pam_return(pamh, flags, argc, argv));
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
return (pam_return(pamh, flags, argc, argv));
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_open_session(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
return (pam_return(pamh, flags, argc, argv));
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_close_session(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
return (pam_return(pamh, flags, argc, argv));
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
return (pam_return(pamh, flags, argc, argv));
|
||||
}
|
||||
|
||||
PAM_MODULE_ENTRY("pam_return");
|
|
@ -1,7 +1,7 @@
|
|||
# $Id$
|
||||
|
||||
if CUSTOM_MODULES_DIR
|
||||
moduledir = @OPENPAM_MODULES_DIR@
|
||||
moduledir = $(OPENPAM_MODULES_DIR)
|
||||
else
|
||||
moduledir = $(libdir)
|
||||
endif
|
||||
|
@ -10,6 +10,10 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||
module_LTLIBRARIES = pam_unix.la
|
||||
|
||||
pam_unix_la_SOURCES = pam_unix.c
|
||||
pam_unix_la_LDFLAGS = -no-undefined -module -version-info @LIB_MAJ@ \
|
||||
pam_unix_la_LDFLAGS = -no-undefined -module -version-info $(LIB_MAJ) \
|
||||
-export-symbols-regex '^pam_sm_'
|
||||
pam_unix_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la @CRYPT_LIBS@
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
pam_unix_la_LIBADD = $(SYSTEM_LIBPAM)
|
||||
else
|
||||
pam_unix_la_LIBADD = $(top_builddir)/lib/libpam/libpam.la $(CRYPT_LIBS)
|
||||
endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*-
|
||||
* Copyright (c) 2002-2003 Networks Associates Technology, Inc.
|
||||
* Copyright (c) 2004-2011 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2004-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by ThinkSec AS and
|
||||
|
@ -74,7 +74,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
|||
#endif
|
||||
struct passwd *pwd;
|
||||
const char *user;
|
||||
char *crypt_password, *password;
|
||||
const char *crypt_password, *password;
|
||||
int pam_err, retry;
|
||||
|
||||
(void)argc;
|
||||
|
@ -98,7 +98,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
|||
for (retry = 0; retry < 3; ++retry) {
|
||||
#ifdef OPENPAM
|
||||
pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
|
||||
(const char **)&password, NULL);
|
||||
&password, NULL);
|
||||
#else
|
||||
resp = NULL;
|
||||
pam_err = (*conv->conv)(1, &msgp, &resp, conv->appdata_ptr);
|
||||
|
|
41
pamgdb.in
41
pamgdb.in
|
@ -1,41 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
srcdir="@abs_top_srcdir@"
|
||||
builddir="@abs_top_builddir@"
|
||||
|
||||
# Make sure we get the right version of libpam
|
||||
pam_libdir="${builddir}/lib/.libs"
|
||||
LD_LIBRARY_PATH="${pam_libdir}:${LD_LIBRARY_PATH}"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH%:}"
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
# DWIM, assuming that the first positional argument is the name of the
|
||||
# program to debug rather than a gdb option.
|
||||
prog="$1"
|
||||
if expr "${prog}" : ".*/.*" >/dev/null ; then
|
||||
# The first argument is an absolute or relative path. There
|
||||
# is a good chance that it points to the wrapper script
|
||||
# generated by libtool rather than the actual binary.
|
||||
altprog="${prog%/*}/.libs/${prog##*/}"
|
||||
if [ -x "${altprog}" ] ; then
|
||||
shift
|
||||
set "${altprog}" "$@"
|
||||
fi
|
||||
elif expr "${prog}" : "[a-z.-][a-z.-]*" >/dev/null ; then
|
||||
# The first argument is just the name of the program. Look for
|
||||
# it in the build directory.
|
||||
for libdir in $(find "${builddir}" -type d -name .libs -print) ; do
|
||||
altprog="${libdir}/${prog}"
|
||||
if [ -x "${altprog}" ] ; then
|
||||
shift
|
||||
set "${altprog}" "$@"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Let's go!
|
||||
exec gdb "$@"
|
|
@ -2,23 +2,27 @@
|
|||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/lib/libpam
|
||||
|
||||
noinst_HEADERS = t.h
|
||||
AM_TESTS_ENVIRONMENT = \
|
||||
PAM_RETURN_SO=$(abs_top_builddir)/modules/pam_return/.libs/pam_return.so
|
||||
|
||||
noinst_HEADERS = t.h t_pam_conv.h
|
||||
|
||||
# tests
|
||||
TESTS =
|
||||
TESTS += t_openpam_ctype
|
||||
TESTS += t_openpam_dispatch
|
||||
TESTS += t_openpam_readword
|
||||
TESTS += t_openpam_readlinev
|
||||
if WITH_OATH
|
||||
TESTS += t_rfc4648
|
||||
endif
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
# libt - common support code
|
||||
check_LIBRARIES = libt.a
|
||||
libt_a_SOURCES = t_main.c t_file.c
|
||||
libt_a_SOURCES = t_main.c t_file.c t_pam_conv.c
|
||||
|
||||
# link with libpam and libt
|
||||
LDADD = libt.a $(top_builddir)/lib/libpam/libpam.la
|
||||
if WITH_OATH
|
||||
LDADD += $(top_builddir)/lib/liboath/liboath.la
|
||||
LDADD = libt.a
|
||||
if WITH_SYSTEM_LIBPAM
|
||||
LDADD += $(SYSTEM_LIBPAM)
|
||||
else
|
||||
LDADD += $(top_builddir)/lib/libpam/libpam.la
|
||||
endif
|
||||
|
|
12
t/t.h
12
t/t.h
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -32,6 +32,10 @@
|
|||
#ifndef T_H_INCLUDED
|
||||
#define T_H_INCLUDED
|
||||
|
||||
#if _BullseyeCoverage
|
||||
_Pragma("BullseyeCoverage save off")
|
||||
#endif
|
||||
|
||||
#include <security/openpam_attr.h>
|
||||
|
||||
struct t_test {
|
||||
|
@ -42,13 +46,13 @@ struct t_test {
|
|||
|
||||
#define T_FUNC(n, d) \
|
||||
static int t_ ## n ## _func(void *); \
|
||||
static const struct t_test t_ ## n = \
|
||||
static struct t_test t_ ## n = \
|
||||
{ t_ ## n ## _func, d, NULL }; \
|
||||
static int t_ ## n ## _func(OPENPAM_UNUSED(void *arg))
|
||||
|
||||
#define T_FUNC_ARG(n, d, a) \
|
||||
static int t_ ## n ## _func(void *); \
|
||||
static const struct t_test t_ ## n = \
|
||||
static struct t_test t_ ## n = \
|
||||
{ t_ ## n ## _func, d, a }; \
|
||||
static int t_ ## n ## _func(void *arg)
|
||||
|
||||
|
@ -57,7 +61,7 @@ struct t_test {
|
|||
|
||||
extern const char *t_progname;
|
||||
|
||||
const struct t_test **t_prepare(int, char **);
|
||||
struct t_test **t_prepare(int, char **);
|
||||
void t_cleanup(void);
|
||||
|
||||
void t_verbose(const char *, ...)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2016 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -92,6 +92,8 @@ t_fprintf(struct t_file *tf, const char *fmt, ...)
|
|||
va_end(ap);
|
||||
if (ferror(tf->file))
|
||||
err(1, "%s(): vfprintf()", __func__);
|
||||
if (fflush(tf->file) != 0)
|
||||
err(1, "%s(): fflush()", __func__);
|
||||
return (len);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -70,7 +70,7 @@ usage(void)
|
|||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const struct t_test **t_plan;
|
||||
struct t_test **t_plan;
|
||||
const char *desc;
|
||||
int n, pass, fail;
|
||||
int opt;
|
||||
|
@ -80,6 +80,9 @@ main(int argc, char *argv[])
|
|||
setlogmask(LOG_UPTO(0));
|
||||
#endif
|
||||
|
||||
/* make stdout line-buffered to preserve ordering */
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
/* clean up temp files in case of premature exit */
|
||||
atexit(t_fcloseall);
|
||||
|
||||
|
|
122
t/t_openpam_ctype.c
Normal file
122
t/t_openpam_ctype.c
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*-
|
||||
* Copyright (c) 2014-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "openpam_ctype.h"
|
||||
|
||||
#include "t.h"
|
||||
|
||||
#define OC_DIGIT "0123456789"
|
||||
#define OC_XDIGIT OC_DIGIT "ABCDEFabcdef"
|
||||
#define OC_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define OC_LOWER "abcdefghijklmnopqrstuvwxyz"
|
||||
#define OC_LETTER OC_UPPER OC_LOWER
|
||||
#define OC_LWS " \t\f\r"
|
||||
#define OC_WS OC_LWS "\n"
|
||||
#define OC_P "!\"#$%&'()*+,-./" OC_DIGIT ":;<=>?@" OC_UPPER "[\\]^_`" OC_LOWER "{|}~"
|
||||
#define OC_PFCS OC_DIGIT OC_LETTER "._-"
|
||||
|
||||
static const char oc_digit[] = OC_DIGIT;
|
||||
static const char oc_xdigit[] = OC_XDIGIT;
|
||||
static const char oc_upper[] = OC_UPPER;
|
||||
static const char oc_lower[] = OC_LOWER;
|
||||
static const char oc_letter[] = OC_LETTER;
|
||||
static const char oc_lws[] = OC_LWS;
|
||||
static const char oc_ws[] = OC_WS;
|
||||
static const char oc_p[] = OC_P;
|
||||
static const char oc_pfcs[] = OC_PFCS;
|
||||
|
||||
#define T_OC(set) \
|
||||
T_FUNC(t_oc_##set, "is_" #set) \
|
||||
{ \
|
||||
char crib[256]; \
|
||||
unsigned int i, ret; \
|
||||
\
|
||||
memset(crib, 0, sizeof crib); \
|
||||
for (i = 0; oc_##set[i]; ++i) \
|
||||
crib[(int)oc_##set[i]] = 1; \
|
||||
for (i = ret = 0; i < sizeof crib; ++i) { \
|
||||
if (is_##set(i) != crib[i]) { \
|
||||
t_verbose("is_%s() incorrect " \
|
||||
"for %#02x\n", #set, i); \
|
||||
++ret; \
|
||||
} \
|
||||
} \
|
||||
return (ret == 0); \
|
||||
}
|
||||
|
||||
T_OC(digit)
|
||||
T_OC(xdigit)
|
||||
T_OC(upper)
|
||||
T_OC(lower)
|
||||
T_OC(letter)
|
||||
T_OC(lws)
|
||||
T_OC(ws)
|
||||
T_OC(p)
|
||||
T_OC(pfcs)
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Boilerplate
|
||||
*/
|
||||
|
||||
static struct t_test *t_plan[] = {
|
||||
T(t_oc_digit),
|
||||
T(t_oc_xdigit),
|
||||
T(t_oc_upper),
|
||||
T(t_oc_lower),
|
||||
T(t_oc_letter),
|
||||
T(t_oc_lws),
|
||||
T(t_oc_ws),
|
||||
T(t_oc_p),
|
||||
T(t_oc_pfcs),
|
||||
NULL
|
||||
};
|
||||
|
||||
struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (t_plan);
|
||||
}
|
||||
|
||||
void
|
||||
t_cleanup(void)
|
||||
{
|
||||
}
|
223
t/t_openpam_dispatch.c
Normal file
223
t/t_openpam_dispatch.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*-
|
||||
* Copyright (c) 2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_impl.h"
|
||||
#include "t.h"
|
||||
#include "t_pam_conv.h"
|
||||
|
||||
const char *pam_return_so;
|
||||
|
||||
T_FUNC(null, "null handle")
|
||||
{
|
||||
int pam_err;
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnonnull"
|
||||
#elif __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wnonnull"
|
||||
#endif
|
||||
pam_err = pam_authenticate(NULL, 0);
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#elif __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
return (pam_err == PAM_SYSTEM_ERR);
|
||||
}
|
||||
|
||||
T_FUNC(empty_policy, "empty policy")
|
||||
{
|
||||
struct t_pam_conv_script script;
|
||||
struct pam_conv pamc;
|
||||
struct t_file *tf;
|
||||
pam_handle_t *pamh;
|
||||
int pam_err, ret;
|
||||
|
||||
memset(&script, 0, sizeof script);
|
||||
pamc.conv = &t_pam_conv;
|
||||
pamc.appdata_ptr = &script;
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "# empty policy\n");
|
||||
pam_err = pam_start(tf->name, "test", &pamc, &pamh);
|
||||
t_verbose("pam_start() returned %d\n", pam_err);
|
||||
/*
|
||||
* Note: openpam_dispatch() currently returns PAM_SYSTEM_ERR when
|
||||
* the chain is empty, it should possibly return PAM_SERVICE_ERR
|
||||
* instead.
|
||||
*/
|
||||
pam_err = pam_authenticate(pamh, 0);
|
||||
t_verbose("pam_authenticate() returned %d\n", pam_err);
|
||||
ret = (pam_err != PAM_SUCCESS);
|
||||
pam_err = pam_setcred(pamh, 0);
|
||||
t_verbose("pam_setcred() returned %d\n", pam_err);
|
||||
ret |= (pam_err != PAM_SUCCESS);
|
||||
pam_err = pam_acct_mgmt(pamh, 0);
|
||||
t_verbose("pam_acct_mgmt() returned %d\n", pam_err);
|
||||
ret |= (pam_err != PAM_SUCCESS);
|
||||
pam_err = pam_chauthtok(pamh, 0);
|
||||
t_verbose("pam_chauthtok() returned %d\n", pam_err);
|
||||
ret |= (pam_err != PAM_SUCCESS);
|
||||
pam_err = pam_open_session(pamh, 0);
|
||||
t_verbose("pam_open_session() returned %d\n", pam_err);
|
||||
ret |= (pam_err != PAM_SUCCESS);
|
||||
pam_err = pam_close_session(pamh, 0);
|
||||
t_verbose("pam_close_session() returned %d\n", pam_err);
|
||||
ret |= (pam_err != PAM_SUCCESS);
|
||||
pam_err = pam_end(pamh, pam_err);
|
||||
ret |= (pam_err == PAM_SUCCESS);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static struct t_pam_return_case {
|
||||
int facility;
|
||||
int primitive;
|
||||
int flags;
|
||||
struct {
|
||||
int ctlflag;
|
||||
int modret;
|
||||
} mod[2];
|
||||
int result;
|
||||
} t_pam_return_cases[] = {
|
||||
{
|
||||
PAM_AUTH, PAM_SM_AUTHENTICATE, 0,
|
||||
{
|
||||
{ PAM_REQUIRED, PAM_SUCCESS },
|
||||
{ PAM_REQUIRED, PAM_SUCCESS },
|
||||
},
|
||||
PAM_SUCCESS,
|
||||
},
|
||||
};
|
||||
|
||||
T_FUNC(mod_return, "module return value")
|
||||
{
|
||||
struct t_pam_return_case *tc;
|
||||
struct t_pam_conv_script script;
|
||||
struct pam_conv pamc;
|
||||
struct t_file *tf;
|
||||
pam_handle_t *pamh;
|
||||
unsigned int i, j, n;
|
||||
int pam_err;
|
||||
|
||||
memset(&script, 0, sizeof script);
|
||||
pamc.conv = &t_pam_conv;
|
||||
pamc.appdata_ptr = &script;
|
||||
n = sizeof t_pam_return_cases / sizeof t_pam_return_cases[0];
|
||||
for (i = 0; i < n; ++i) {
|
||||
tc = &t_pam_return_cases[i];
|
||||
tf = t_fopen(NULL);
|
||||
for (j = 0; j < 2; ++j) {
|
||||
t_fprintf(tf, "%s %s %s error=%s\n",
|
||||
pam_facility_name[tc->facility],
|
||||
pam_control_flag_name[tc->mod[j].ctlflag],
|
||||
pam_return_so,
|
||||
pam_err_name[tc->mod[j].modret]);
|
||||
}
|
||||
pam_err = pam_start(tf->name, "test", &pamc, &pamh);
|
||||
t_verbose("pam_start() returned %d\n", pam_err);
|
||||
if (pam_err != PAM_SUCCESS)
|
||||
continue;
|
||||
switch (tc->primitive) {
|
||||
case PAM_SM_AUTHENTICATE:
|
||||
pam_err = pam_authenticate(pamh, tc->flags);
|
||||
break;
|
||||
case PAM_SM_SETCRED:
|
||||
pam_err = pam_setcred(pamh, tc->flags);
|
||||
break;
|
||||
case PAM_SM_ACCT_MGMT:
|
||||
pam_err = pam_acct_mgmt(pamh, tc->flags);
|
||||
break;
|
||||
case PAM_SM_OPEN_SESSION:
|
||||
pam_err = pam_open_session(pamh, tc->flags);
|
||||
break;
|
||||
case PAM_SM_CLOSE_SESSION:
|
||||
pam_err = pam_close_session(pamh, tc->flags);
|
||||
break;
|
||||
case PAM_SM_CHAUTHTOK:
|
||||
pam_err = pam_chauthtok(pamh, tc->flags);
|
||||
break;
|
||||
}
|
||||
t_verbose("%s returned %d\n",
|
||||
pam_func_name[tc->primitive], pam_err);
|
||||
t_fclose(tf);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Boilerplate
|
||||
*/
|
||||
|
||||
static struct t_test *t_plan[] = {
|
||||
T(null),
|
||||
T(empty_policy),
|
||||
T(mod_return),
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
|
||||
if ((pam_return_so = getenv("PAM_RETURN_SO")) == NULL)
|
||||
return (NULL);
|
||||
|
||||
openpam_set_feature(OPENPAM_RESTRICT_MODULE_NAME, 0);
|
||||
openpam_set_feature(OPENPAM_VERIFY_MODULE_FILE, 0);
|
||||
openpam_set_feature(OPENPAM_RESTRICT_SERVICE_NAME, 0);
|
||||
openpam_set_feature(OPENPAM_VERIFY_POLICY_FILE, 0);
|
||||
openpam_set_feature(OPENPAM_FALLBACK_TO_OTHER, 0);
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return (t_plan);
|
||||
}
|
||||
|
||||
void
|
||||
t_cleanup(void)
|
||||
{
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -125,6 +125,23 @@ static const char *hello_world[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static const char *numbers[] = {
|
||||
"zero", "one", "two", "three", "four", "five", "six", "seven",
|
||||
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
|
||||
"fifteen", "sixteen", "seventeen", "nineteen", "twenty",
|
||||
"twenty-one", "twenty-two", "twenty-three", "twenty-four",
|
||||
"twenty-five", "twenty-six", "twenty-seven", "twenty-eight",
|
||||
"twenty-nine", "thirty", "thirty-one", "thirty-two", "thirty-three",
|
||||
"thirty-four", "thirty-five", "thirty-six", "thirty-seven",
|
||||
"thirty-eight", "thirty-nine", "fourty", "fourty-one", "fourty-two",
|
||||
"fourty-three", "fourty-four", "fourty-five", "fourty-six",
|
||||
"fourty-seven", "fourty-eight", "fourty-nine", "fifty", "fifty-one",
|
||||
"fifty-two", "fifty-three", "fifty-four", "fifty-five", "fifty-six",
|
||||
"fifty-seven", "fifty-eight", "fifty-nine", "sixty", "sixty-one",
|
||||
"sixty-two", "sixty-three",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Lines without words
|
||||
|
@ -206,6 +223,20 @@ T_FUNC(whitespace_before_comment, "whitespace before comment")
|
|||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(line_continuation_within_whitespace, "line continuation within whitespace")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "%s \\\n %s\n", hello_world[0], hello_world[1]);
|
||||
t_frewind(tf);
|
||||
ret = orlv_expect(tf, hello_world, 2 /*lines*/, 0 /*eof*/) &&
|
||||
orlv_expect(tf, NULL, 0 /*lines*/, 1 /*eof*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Simple words
|
||||
|
@ -237,6 +268,22 @@ T_FUNC(two_words, "two words")
|
|||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(many_words, "many words")
|
||||
{
|
||||
struct t_file *tf;
|
||||
const char **word;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
for (word = numbers; *word; ++word)
|
||||
t_fprintf(tf, " %s", *word);
|
||||
t_fprintf(tf, "\n");
|
||||
t_frewind(tf);
|
||||
ret = orlv_expect(tf, numbers, 1 /*lines*/, 0 /*eof*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(unterminated_line, "unterminated line")
|
||||
{
|
||||
struct t_file *tf;
|
||||
|
@ -255,22 +302,24 @@ T_FUNC(unterminated_line, "unterminated line")
|
|||
* Boilerplate
|
||||
*/
|
||||
|
||||
const struct t_test *t_plan[] = {
|
||||
static struct t_test *t_plan[] = {
|
||||
T(empty_input),
|
||||
T(empty_line),
|
||||
T(unterminated_empty_line),
|
||||
T(whitespace),
|
||||
T(comment),
|
||||
T(whitespace_before_comment),
|
||||
T(line_continuation_within_whitespace),
|
||||
|
||||
T(one_word),
|
||||
T(two_words),
|
||||
T(many_words),
|
||||
T(unterminated_line),
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct t_test **
|
||||
struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012 Dag-Erling Smørgrav
|
||||
* Copyright (c) 2012-2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -195,6 +195,45 @@ T_FUNC(whitespace_before_comment, "whitespace before comment")
|
|||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(single_quoted_comment, "single-quoted comment")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, " '# comment'\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "# comment", 0 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(double_quoted_comment, "double-quoted comment")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, " \"# comment\"\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "# comment", 0 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(comment_at_eof, "comment at end of file")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "# comment");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, NULL, 0 /*lines*/, 1 /*eof*/, 0 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Simple cases - no quotes or escapes
|
||||
|
@ -414,6 +453,33 @@ T_FUNC(escaped_letter, "escaped letter")
|
|||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(escaped_comment, "escaped comment")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, " \\# comment\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "#", 0 /*lines*/, 0 /*eof*/, 0 /*eol*/) &&
|
||||
orw_expect(tf, "comment", 0 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(escape_at_eof, "escape at end of file")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "z\\");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, NULL, 0 /*lines*/, 1 /*eof*/, 0 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Quotes
|
||||
|
@ -818,18 +884,82 @@ T_FUNC(escaped_double_quote_within_double_quotes,
|
|||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Line continuation
|
||||
*/
|
||||
|
||||
T_FUNC(line_continuation_within_whitespace, "line continuation within whitespace")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "hello \\\n world\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "hello", 0 /*lines*/, 0 /*eof*/, 0 /*eol*/) &&
|
||||
orw_expect(tf, "world", 1 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(line_continuation_before_whitespace, "line continuation before whitespace")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "hello\\\n world\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "hello", 1 /*lines*/, 0 /*eof*/, 0 /*eol*/) &&
|
||||
orw_expect(tf, "world", 0 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(line_continuation_after_whitespace, "line continuation after whitespace")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "hello \\\nworld\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "hello", 0 /*lines*/, 0 /*eof*/, 0 /*eol*/) &&
|
||||
orw_expect(tf, "world", 1 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
T_FUNC(line_continuation_within_word, "line continuation within word")
|
||||
{
|
||||
struct t_file *tf;
|
||||
int ret;
|
||||
|
||||
tf = t_fopen(NULL);
|
||||
t_fprintf(tf, "hello\\\nworld\n");
|
||||
t_frewind(tf);
|
||||
ret = orw_expect(tf, "helloworld", 1 /*lines*/, 0 /*eof*/, 1 /*eol*/);
|
||||
t_fclose(tf);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Boilerplate
|
||||
*/
|
||||
|
||||
const struct t_test *t_plan[] = {
|
||||
static struct t_test *t_plan[] = {
|
||||
T(empty_input),
|
||||
T(empty_line),
|
||||
T(unterminated_line),
|
||||
T(single_whitespace),
|
||||
T(multiple_whitespace),
|
||||
T(comment),
|
||||
T(whitespace_before_comment),
|
||||
T(single_quoted_comment),
|
||||
T(double_quoted_comment),
|
||||
T(comment_at_eof),
|
||||
|
||||
T(single_word),
|
||||
T(single_whitespace_before_word),
|
||||
|
@ -847,6 +977,8 @@ const struct t_test *t_plan[] = {
|
|||
T(escaped_newline_within_word),
|
||||
T(escaped_newline_after_word),
|
||||
T(escaped_letter),
|
||||
T(escaped_comment),
|
||||
T(escape_at_eof),
|
||||
|
||||
T(naked_single_quote),
|
||||
T(naked_double_quote),
|
||||
|
@ -879,10 +1011,15 @@ const struct t_test *t_plan[] = {
|
|||
T(escaped_single_quote_within_double_quotes),
|
||||
T(escaped_double_quote_within_double_quotes),
|
||||
|
||||
T(line_continuation_within_whitespace),
|
||||
T(line_continuation_before_whitespace),
|
||||
T(line_continuation_after_whitespace),
|
||||
T(line_continuation_within_word),
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
const struct t_test **
|
||||
struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
|
||||
|
|
132
t/t_pam_conv.c
Normal file
132
t/t_pam_conv.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*-
|
||||
* Copyright (c) 2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
|
||||
#include "openpam_impl.h"
|
||||
#include "openpam_asprintf.h"
|
||||
#include "t.h"
|
||||
|
||||
#include "t_pam_conv.h"
|
||||
|
||||
/*
|
||||
* Conversation function
|
||||
*
|
||||
* The appdata argument points to a struct t_pam_conv_script which
|
||||
* contains both the expected messages and the desired responses. If
|
||||
* script.responses is NULL, t_pam_conv() will return PAM_CONV_ERR. If an
|
||||
* error occurs (incorrect number of messages, messages don't match script
|
||||
* etc.), script.comment will be set to point to a malloc()ed string
|
||||
* describing the error. Otherwise, t_pam_conv() will return to its
|
||||
* caller a malloc()ed copy of script.responses.
|
||||
*/
|
||||
int
|
||||
t_pam_conv(int nm, const struct pam_message **msgs,
|
||||
struct pam_response **respsp, void *ad)
|
||||
{
|
||||
struct t_pam_conv_script *s = ad;
|
||||
struct pam_response *resps;
|
||||
int i;
|
||||
|
||||
/* check message count */
|
||||
if (nm != s->nmsg) {
|
||||
asprintf(&s->comment, "expected %d messages, got %d",
|
||||
s->nmsg, nm);
|
||||
return (PAM_CONV_ERR);
|
||||
}
|
||||
if (nm <= 0 || nm > PAM_MAX_NUM_MSG) {
|
||||
/* since the previous test passed, this is intentional! */
|
||||
s->comment = NULL;
|
||||
return (PAM_CONV_ERR);
|
||||
}
|
||||
|
||||
/* check each message and provide the sed answer */
|
||||
if ((resps = calloc(nm, sizeof *resps)) == NULL)
|
||||
goto enomem;
|
||||
for (i = 0; i < nm; ++i) {
|
||||
if (msgs[i]->msg_style != s->msgs[i].msg_style) {
|
||||
asprintf(&s->comment,
|
||||
"message %d expected style %d got %d", i,
|
||||
s->msgs[i].msg_style, msgs[i]->msg_style);
|
||||
goto fail;
|
||||
}
|
||||
if (strcmp(msgs[i]->msg, s->msgs[i].msg) != 0) {
|
||||
asprintf(&s->comment,
|
||||
"message %d expected \"%s\" got \"%s\"", i,
|
||||
s->msgs[i].msg, msgs[i]->msg);
|
||||
goto fail;
|
||||
}
|
||||
switch (msgs[i]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
t_verbose("[PAM_PROMPT_ECHO_OFF] %s\n", msgs[i]->msg);
|
||||
break;
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
t_verbose("[PAM_PROMPT_ECHO_ON] %s\n", msgs[i]->msg);
|
||||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
t_verbose("[PAM_ERROR_MSG] %s\n", msgs[i]->msg);
|
||||
break;
|
||||
case PAM_TEXT_INFO:
|
||||
t_verbose("[PAM_TEXT_INFO] %s\n", msgs[i]->msg);
|
||||
break;
|
||||
default:
|
||||
asprintf(&s->comment, "invalid message style %d",
|
||||
msgs[i]->msg_style);
|
||||
goto fail;
|
||||
}
|
||||
/* copy the response, if there is one */
|
||||
if (s->resps[i].resp != NULL &&
|
||||
(resps[i].resp = strdup(s->resps[i].resp)) == NULL)
|
||||
goto enomem;
|
||||
resps[i].resp_retcode = s->resps[i].resp_retcode;
|
||||
}
|
||||
s->comment = NULL;
|
||||
*respsp = resps;
|
||||
return (PAM_SUCCESS);
|
||||
enomem:
|
||||
asprintf(&s->comment, "%s", strerror(ENOMEM));
|
||||
fail:
|
||||
for (i = 0; i < nm; ++i)
|
||||
free(resps[i].resp);
|
||||
free(resps);
|
||||
return (PAM_CONV_ERR);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*-
|
||||
* Copyright (c) 2012-2013 Universitetet i Oslo
|
||||
* Copyright (c) 2015 Dag-Erling Smørgrav
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -29,23 +29,17 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef OATH_RFC4648_H_INCLUDED
|
||||
#define OATH_RFC4648_H_INCLUDED
|
||||
#ifndef T_PAM_CONV_H_INCLUDED
|
||||
#define T_PAM_CONV_H_INCLUDED
|
||||
|
||||
/* estimate of output length for base32 encoding / decoding */
|
||||
#define base32_enclen(l) (size_t)(((l + 4) / 5) * 8)
|
||||
#define base32_declen(l) (size_t)(((l + 7) / 8) * 5)
|
||||
struct t_pam_conv_script {
|
||||
int nmsg;
|
||||
struct pam_message *msgs;
|
||||
struct pam_response *resps;
|
||||
char *comment;
|
||||
};
|
||||
|
||||
/* base32 encoding / decoding */
|
||||
int base32_enc(const uint8_t *, size_t, char *, size_t *);
|
||||
int base32_dec(const char *, size_t, uint8_t *, size_t *);
|
||||
|
||||
/* estimate of output length for base64 encoding / decoding */
|
||||
#define base64_enclen(l) (size_t)(((l + 2) / 3) * 4)
|
||||
#define base64_declen(l) (size_t)(((l + 3) / 4) * 3)
|
||||
|
||||
/* base64 encoding / decoding */
|
||||
int base64_enc(const uint8_t *, size_t, char *, size_t *);
|
||||
int base64_dec(const char *, size_t, uint8_t *, size_t *);
|
||||
int t_pam_conv(int, const struct pam_message **, struct pam_response **,
|
||||
void *);
|
||||
|
||||
#endif
|
194
t/t_rfc4648.c
194
t/t_rfc4648.c
|
@ -1,194 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 2013 Universitetet i Oslo
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <err.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/openpam.h>
|
||||
#include <security/oath.h>
|
||||
|
||||
#include "t.h"
|
||||
|
||||
/*
|
||||
* Test vectors from RFC 4648
|
||||
*/
|
||||
static struct t_vector {
|
||||
const char *plain;
|
||||
/* const char *base16; */
|
||||
const char *base32;
|
||||
const char *base64;
|
||||
} t_vectors[] = {
|
||||
{
|
||||
.plain = "",
|
||||
.base32 = "",
|
||||
.base64 = "",
|
||||
},
|
||||
{
|
||||
.plain = "f",
|
||||
.base32 = "MY======",
|
||||
.base64 = "Zg=="
|
||||
},
|
||||
{
|
||||
.plain = "fo",
|
||||
.base32 = "MZXQ====",
|
||||
.base64 = "Zm8=",
|
||||
},
|
||||
{
|
||||
.plain = "foo",
|
||||
.base32 = "MZXW6===",
|
||||
.base64 = "Zm9v",
|
||||
},
|
||||
{
|
||||
.plain = "foob",
|
||||
.base32 = "MZXW6YQ=",
|
||||
.base64 = "Zm9vYg==",
|
||||
},
|
||||
{
|
||||
.plain = "fooba",
|
||||
.base32 = "MZXW6YTB",
|
||||
.base64 = "Zm9vYmE=",
|
||||
},
|
||||
{
|
||||
.plain = "foobar",
|
||||
.base32 = "MZXW6YTBOI======",
|
||||
.base64 = "Zm9vYmFy",
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Encoding test function
|
||||
*/
|
||||
static int
|
||||
t_rfc4648_enc(const char *plain, const char *encoded,
|
||||
int (*enc)(const uint8_t *, size_t, char *, size_t *))
|
||||
{
|
||||
char buf[64];
|
||||
size_t blen, ilen, olen;
|
||||
|
||||
blen = sizeof buf;
|
||||
ilen = strlen(plain);
|
||||
olen = strlen(encoded) + 1;
|
||||
if (enc((const uint8_t *)plain, ilen, buf, &blen) != 0) {
|
||||
t_verbose("encoding failed\n");
|
||||
return (0);
|
||||
}
|
||||
if (blen != olen) {
|
||||
t_verbose("expected %zu B got %zu B\n", olen, blen);
|
||||
return (0);
|
||||
}
|
||||
if (strcmp(buf, encoded) != 0) {
|
||||
t_verbose("expected \"%s\" got \"%s\"\n", encoded, buf);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encoding test wrapper for base 32
|
||||
*/
|
||||
static int
|
||||
t_base32(void *arg)
|
||||
{
|
||||
struct t_vector *tv = (struct t_vector *)arg;
|
||||
|
||||
return (t_rfc4648_enc(tv->plain, tv->base32, base32_enc));
|
||||
}
|
||||
|
||||
/*
|
||||
* Encoding test wrapper for base 64
|
||||
*/
|
||||
static int
|
||||
t_base64(void *arg)
|
||||
{
|
||||
struct t_vector *tv = (struct t_vector *)arg;
|
||||
|
||||
return (t_rfc4648_enc(tv->plain, tv->base64, base64_enc));
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a test case for a given test vector
|
||||
*/
|
||||
static struct t_test *
|
||||
t_create_test(int (*func)(void *), const char *name, struct t_vector *tv)
|
||||
{
|
||||
struct t_test *test;
|
||||
char *desc;
|
||||
|
||||
if ((test = calloc(1, sizeof *test)) == NULL)
|
||||
return (NULL);
|
||||
test->func = func;
|
||||
if ((desc = calloc(1, strlen(name) + strlen(tv->plain) + 5)) == NULL)
|
||||
return (NULL);
|
||||
sprintf(desc, "%s(\"%s\")", name, tv->plain);
|
||||
test->desc = desc;
|
||||
test->arg = tv;
|
||||
return (test);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the test plan
|
||||
*/
|
||||
const struct t_test **
|
||||
t_prepare(int argc, char *argv[])
|
||||
{
|
||||
struct t_test **plan, **test;
|
||||
int n;
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
n = sizeof t_vectors / sizeof t_vectors[0];
|
||||
plan = calloc(n * 2 + 1, sizeof *plan);
|
||||
if (plan == NULL)
|
||||
return (NULL);
|
||||
test = plan;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
*test++ = t_create_test(t_base32, "BASE32", &t_vectors[i]);
|
||||
*test++ = t_create_test(t_base64, "BASE64", &t_vectors[i]);
|
||||
}
|
||||
return ((const struct t_test **)plan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
*/
|
||||
void
|
||||
t_cleanup(void)
|
||||
{
|
||||
}
|
Loading…
Add table
Reference in a new issue