Import Upstream version 0.92

This commit is contained in:
rtlhq 2022-11-24 21:10:51 +08:00
commit 590a4e56a1
87 changed files with 55466 additions and 0 deletions

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# autotools stuff
Makefile
Makefile.in
aclocal.m4
config.h
config.h.in
config.log
config.status
configure
install-sh
src/.deps/
src/.dirstamp
src/*.lo
src/.libs/
stamp-h1
autom4te.cache/
.libs/
*.la
build-aux/
libtool
test-suite.log
# objects
/src/*.o
/stoken
/stoken-gui
/stoken-*.tar.gz
/stoken.pc
# scratch files
.*.sw*
*~
# Debian
ppa/changelog
tmp.debian
# Windows
winpkg/
winpkg.zip
*.exe

123
CHANGES Normal file
View File

@ -0,0 +1,123 @@
V0.92 - 2017/11/12
- Update MinGW dependencies to work with Fedora 25 (bug #31)
- Fix compatibility with libtomcrypt 1.18 (bug #38)
- Invoke locally-generated libtool binary instead of assuming there is
one in the $PATH (bug #37)
- Use $USERPROFILE instead of $HOME on Windows 7 (bug #22)
V0.91 - 2017/01/08
- Fix segfault if ~/.stokenrc is empty and another program invokes libstoken
- Squash extraneous newline from `stoken tokencode` if stdin isn't a
tty (bug #29)
V0.90 - 2015/08/09
- Change version number from v0.9 -> v0.90 to make life easier for packagers
V0.9 - 2015/07/25
- Fix corner case in SDTID reader (bug #21)
- Support Nettle 3.x (bug #20)
- Other minor fixes
V0.81 - 2014/12/08
- Fix "make check" failures in GMT+xx timezones (xx >= 1)
V0.8 - 2014/12/07
- Update GUI from GTK+2 to GTK+3; use glade to define the layouts
- Add next tokencode and other information to the full GUI (stoken-gui).
The "stoken-gui --small" layout is unchanged.
- Add a list of known GUIDs used to bind tokens to specific device classes
(i.e. tokens can be bound to "any BlackBerry 10"). stoken will now try
each one of these IDs when importing a new bound token.
- Add new --qr and --show-qr export options to make it easy to pass tokens
to a mobile device
- Add "stoken --next" option to show the next tokencode
- Support using nettle+gmp as an alternative to libtomcrypt
- Generate valid TKNTrailer BatchSignature sections in .sdtid output files,
allowing use with RSA's official TokenConverter utility
- Fix parsing of RSA-supplied hard token seed files
- Add "make check" and several test cases
- Fix build errors on Mac OSX, enable OSX Travis builds, and submit a
Homebrew formula
- Fix build errors when using uClibc instead of glibc
- Add experimental Windows build using MinGW (see README for instructions)
- Relicense the JNI wrapper (BSD-3-clause) so that it is easier to incorporate
into non-free Android apps
- Various fixes for minor bugs, memory leaks, etc.
v0.6 - 2014/06/21
- Add support for reading and writing v3 (base64-encoded) Android tokens
- Add support for 30-second tokens. Library users should call
stoken_get_info() to check the token interval.
- Add "stoken-gui --small" mode to conserve desktop space
- Enable Alt-C and Alt-Q keyboard shortcuts in stoken-gui
- Fix handling of cached PINs for library callers
- Fix JNI symbol exports
- Fix "stoken issue" if no token is imported
- Add new stoken_get_info() API to query the S/N and expiration date
- Minor documentation updates
v0.5 - 2014/03/15
- Add support for importing/exporting sdtid XML files. The library now
depends on libxml.
- Add JNI code for integration into Android apps
- Add support for 6-digit tokencodes
- Fix problems decrypting some device-ID-bound tokens (bug #3)
- Fix build failures on kfreebsd and Android
- Fix handling of PIN-less tokens
- Fix out-of-tree builds
- Don't strip stoken / stoken-gui binaries by default
v0.2 - 2013/05/18
- Fix a couple of problems seen when manipulating tokens tied to a device
ID, particularly if the token was intended for a smartphone (bug #1)
- Clean up pkg-config dependencies
- Other minor cleanups
v0.1 - 2012/11/17
- Initial public release

504
COPYING.LIB Normal file
View File

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

120
Makefile.am Normal file
View File

@ -0,0 +1,120 @@
AUTOMAKE_OPTIONS = foreign subdir-objects
ACLOCAL_AMFLAGS = -I m4
SHELL = /bin/bash
AM_CPPFLAGS = -DDATA_DIR=\"$(datadir)\"
AM_CFLAGS = $(CRYPTO_CFLAGS) $(LIBXML2_CFLAGS) $(WFLAGS)
dist_man_MANS = stoken.1
lib_LTLIBRARIES = libstoken.la
libstoken_la_SOURCES = src/library.c src/securid.c src/sdtid.c \
src/compat.c src/stc-@CRYPTO_BACKEND@.c
libstoken_la_CFLAGS = $(AM_CFLAGS) -DLIBSTOKEN_BUILD
libstoken_la_LDFLAGS = -version-number @APIMAJOR@:@APIMINOR@ \
-no-undefined
if HAVE_VSCRIPT_COMPLEX
libstoken_la_LDFLAGS += $(VSCRIPT_LDFLAGS),@srcdir@/libstoken.map
endif
libstoken_la_LIBADD = $(CRYPTO_LIBS) $(LIBXML2_LIBS)
libstoken_la_DEPENDENCIES = libstoken.map
include_HEADERS = src/stoken.h
noinst_HEADERS = src/common.h src/securid.h src/stoken-internal.h \
src/sdtid.h
pkgconfig_DATA = stoken.pc
if USE_JNI
if JNI_STANDALONE
libstoken_la_SOURCES += src/jni.c
libstoken_la_CFLAGS += $(JNI_CFLAGS)
else
lib_LTLIBRARIES += libstoken-wrapper.la
libstoken_wrapper_la_SOURCES = src/jni.c
libstoken_wrapper_la_CFLAGS = $(AM_CFLAGS) $(JNI_CFLAGS)
libstoken_wrapper_la_LIBADD = libstoken.la
endif
endif
bin_PROGRAMS = stoken
stoken_SOURCES = src/cli.c src/common.c
stoken_LDADD = $(LDADD) $(CRYPTO_LIBS) libstoken.la
if ENABLE_GUI
bin_PROGRAMS += stoken-gui
stoken_gui_SOURCES = src/gui.c src/common.c
stoken_gui_CFLAGS = $(AM_CFLAGS) $(GTK_CFLAGS)
stoken_gui_CPPFLAGS = $(AM_CPPFLAGS) -DUIDIR=\"$(uidir)\"
stoken_gui_LDADD = $(LDADD) $(CRYPTO_LIBS) libstoken.la $(GTK_LIBS)
dist_man_MANS += stoken-gui.1
icondir = $(datadir)/pixmaps
dist_icon_DATA = gui/stoken-gui.png
desktopdir = $(datadir)/applications
dist_desktop_DATA = gui/stoken-gui.desktop \
gui/stoken-gui-small.desktop
uidir = $(datadir)/stoken
ui_DATA = gui/tokencode-small.ui \
gui/tokencode-detail.ui \
gui/password-dialog.ui \
gui/pin-dialog.ui
endif
dist_doc_DATA = examples/libstoken-test.c examples/sdtid-test.pl \
README.md
dist_noinst_SCRIPTS = autogen.sh
EXTRA_DIST = .gitignore libstoken.map CHANGES $(ui_DATA)
# package both variants explicitly, because @CRYPTO_BACKEND@ only picks one
EXTRA_DIST += src/stc-nettle.c src/stc-tomcrypt.c
GIT_EXTRA_DIST = examples/ java/ tests/
EXTRA_DIST += $(shell cd "$(top_srcdir)" && \
git ls-tree HEAD -r --name-only -- $(GIT_EXTRA_DIST) 2>/dev/null)
TEST_EXTENSIONS = .pipe
TESTS = tests/export-android-v2.pipe \
tests/export-iphone-password.pipe \
tests/export-read-v3.pipe \
tests/export-sdtid-devid-password.pipe \
tests/export-v3-sdtid.pipe \
tests/tokencode-v2.pipe \
tests/tokencode-v3.pipe \
tests/tokencode-sdtid.pipe \
tests/mac-align.pipe
PIPE_LOG_COMPILER = $(srcdir)/tests/pipe-wrapper.sh
dist_check_SCRIPTS = $(TESTS) $(PIPE_LOG_COMPILER)
TESTS_ENVIRONMENT = STOKEN=./stoken \
LIBTOOL="${LIBTOOL}" \
TESTDIR="$(srcdir)/tests"
if ENABLE_VALGRIND
TESTS_ENVIRONMENT += VALGRIND="valgrind --error-exitcode=1"
endif
DISTCLEANFILES = *~
.PHONY: winpkg
winpkg: $(bin_PROGRAMS)
rm -rf winpkg
mkdir winpkg
cp .libs/stoken.exe winpkg/
if ENABLE_GUI
cp .libs/stoken-gui.exe winpkg/
cp `./win32deps.pl .libs/stoken-gui.exe` winpkg/
cp gui/*.{ui,png} winpkg/
else
cp .libs/stoken.exe winpkg/
cp `./win32deps.pl .libs/stoken.exe` winpkg/
endif
rm -f winpkg.zip
zip -r winpkg.zip winpkg/

1719
Makefile.in Normal file

File diff suppressed because it is too large Load Diff

150
README.md Normal file
View File

@ -0,0 +1,150 @@
stoken - Software Token for Linux/UNIX
======================================
stoken is a tokencode generator compatible with RSA SecurID 128-bit (AES)
tokens. The project includes several components:
* A simple command line interface (CLI) used to manage and manipulate tokens
* A GTK+ GUI with cut&amp;paste functionality
* A shared library allowing other software to generate tokencodes on demand
## Building on Linux
### Dependencies
* libtomcrypt or nettle
* libxml2
* libgtk3.0 (required for stoken-gui only)
If you are building from Git, you'll need to install autoconf / automake /
libtool, and run autogen.sh first. This is not necessary if building from
a released source tarball.
On Debian or Ubuntu, this should satisfy most/all dependencies:
sudo apt-get install libgtk-3-dev libtomcrypt-dev libxml2-dev libtomcrypt-dev autoconf automake libtool build-essential
### Compile instructions
./configure
make
make check
make install
## Usage
First, import a token from a raw string or an "sdtid" XML file:
stoken import --token 2000123456...
stoken import --token com.rsa.securid.iphone://ctf?ctfData=2000123456...
stoken import --file mytoken.sdtid
This will prompt for an optional password, so that your seed is encrypted
on disk.
Next, use the CLI or GUI to show the current tokencode:
stoken tokencode
stoken-gui &
If your token requires a PIN, stoken will prompt for it. You can use
<code>stoken setpin</code> to cache your PIN in <code>~/.stokenrc</code>.
This is much less secure, but may be useful for automation.
Modern versions of OpenConnect link against libstoken and can send an
autogenerated tokencode as the password. Import your token using the
above instructions, then:
openconnect -u USERNAME --token-mode=rsa HOSTNAME
See the man pages for additional details: stoken(1), stoken-gui(1)
See examples/ and src/stoken.h for information on using the shared library
interface (libstoken) to generate tokencodes from other applications.
## Screenshots
<table border=1 cellpadding=15>
<tr align="center">
<td><code>stoken-gui</code>
<td><code>stoken-gui --small</code>
<tr valign="top">
<td><img src="misc/screenshot-0.png">
<td><img src="misc/screenshot-1.png">
</table>
## Building on other platforms
### Mac OS X
#### Initial setup
The following configuration was tested under Mavericks 10.9.5; other
variants may work too:
* Install gcc/make/headers: <code>xcode-select --install</code>
* Install [Homebrew](http://brew.sh/)
* Install [XQuartz](http://xquartz.macosforge.org/) to support GTK+3
* Use Homebrew to satisfy dependencies: <code>brew install git autoconf
automake libtool nettle pkg-config gtk+3 gnome-icon-theme
hicolor-icon-theme</code>
* Use OSX's builtin libxml2 (no action needed)
#### Compiling
Note that GNU libtool is called <code>glibtool</code> to avoid collisions
with Apple's libtool program:
export LIBTOOL=glibtool
git clone git://github.com/cernekee/stoken
cd stoken
bash autogen.sh
./configure
make
make check
make install
### Experimental Windows build
As of v0.8, stoken can be built for Windows using the [MinGW cross toolchain
on Fedora](http://fedoraproject.org/wiki/MinGW). This is not tested
or maintained regularly.
#### Initial setup
On a Fedora 20 PC (other versions may work as well), install the build
dependencies:
yum groupinstall "Development Tools"
yum install git autoconf automake libtool mingw32-gnutls mingw32-libxml2 mingw32-gtk3
#### Compiling
git clone git://github.com/cernekee/stoken
cd stoken
bash autogen.sh
mingw32-configure
make winpkg
If all goes well, you should be able to copy <code>winpkg.zip</code> to
a Windows PC and run <code>stoken.exe</code> or <code>stoken-gui.exe</code>.
#### TODO
Several items are known to be missing or broken on the Windows build:
* Default home directory is probably incorrect
* No installer
* The GUI requires its assets to be in the current directory
* Password entry is not masked
* <code>stoken --random</code> flag
* No charset translation on filenames
## Misc
Author: Kevin Cernekee &lt;cernekee@gmail.com&gt;
License: LGPLv2.1+
stoken is a hobbyist project, not affiliated with or endorsed by
RSA Security.

13
TODO Normal file
View File

@ -0,0 +1,13 @@
TODO list:
Library functions probably need man pages.
Consider sanitizing memory, and figure out how to avoid leaking key material.
Fix alpha channel on the icon.
Make better use of the public library functions from cli/gui.
vpnc integration.
Add hotkeys, import/about dialogs, other features to the GUI.

1436
aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

6
autogen.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
set -ex
autoreconf --force --install --verbose
rm -rf autom4te*.cache

347
build-aux/compile Executable file
View File

@ -0,0 +1,347 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

1441
build-aux/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1813
build-aux/config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

791
build-aux/depcomp Executable file
View File

@ -0,0 +1,791 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2013-05-30.07; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

508
build-aux/install-sh Executable file
View File

@ -0,0 +1,508 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2014-09-12.12; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
# $RANDOM is not portable (e.g. dash); use it when possible to
# lower collision chance
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
# As "mkdir -p" follows symlinks and we work in /tmp possibly; so
# create the $tmpdir first (and fail if unsuccessful) to make sure
# that nobody tries to guess the $tmpdir name.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

11156
build-aux/ltmain.sh Normal file

File diff suppressed because it is too large Load Diff

215
build-aux/missing Executable file
View File

@ -0,0 +1,215 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2013-10-28.13; # UTC
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autoheader autom4te automake makeinfo
bison yacc flex lex help2man
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=http://www.perl.org/
flex_URL=http://flex.sourceforge.net/
gnu_software_URL=http://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
case $normalized_program in
autoconf*)
echo "You should only need it if you modified 'configure.ac',"
echo "or m4 files included by it."
program_details 'autoconf'
;;
autoheader*)
echo "You should only need it if you modified 'acconfig.h' or"
echo "$configure_deps."
program_details 'autoheader'
;;
automake*)
echo "You should only need it if you modified 'Makefile.am' or"
echo "$configure_deps."
program_details 'automake'
;;
aclocal*)
echo "You should only need it if you modified 'acinclude.m4' or"
echo "$configure_deps."
program_details 'aclocal'
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
program_details 'autom4te'
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

148
build-aux/test-driver Executable file
View File

@ -0,0 +1,148 @@
#! /bin/sh
# test-driver - basic testsuite driver script.
scriptversion=2013-07-13.22; # UTC
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
# Make unconditional expansion of undefined variables an error. This
# helps a lot in preventing typo-related bugs.
set -u
usage_error ()
{
echo "$0: $*" >&2
print_usage >&2
exit 2
}
print_usage ()
{
cat <<END
Usage:
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
[--expect-failure={yes|no}] [--color-tests={yes|no}]
[--enable-hard-errors={yes|no}] [--]
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
END
}
test_name= # Used for reporting.
log_file= # Where to save the output of the test script.
trs_file= # Where to save the metadata of the test run.
expect_failure=no
color_tests=no
enable_hard_errors=yes
while test $# -gt 0; do
case $1 in
--help) print_usage; exit $?;;
--version) echo "test-driver $scriptversion"; exit $?;;
--test-name) test_name=$2; shift;;
--log-file) log_file=$2; shift;;
--trs-file) trs_file=$2; shift;;
--color-tests) color_tests=$2; shift;;
--expect-failure) expect_failure=$2; shift;;
--enable-hard-errors) enable_hard_errors=$2; shift;;
--) shift; break;;
-*) usage_error "invalid option: '$1'";;
*) break;;
esac
shift
done
missing_opts=
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
if test x"$missing_opts" != x; then
usage_error "the following mandatory options are missing:$missing_opts"
fi
if test $# -eq 0; then
usage_error "missing argument"
fi
if test $color_tests = yes; then
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
red='' # Red.
grn='' # Green.
lgn='' # Light green.
blu='' # Blue.
mgn='' # Magenta.
std='' # No color.
else
red= grn= lgn= blu= mgn= std=
fi
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
trap "st=129; $do_exit" 1
trap "st=130; $do_exit" 2
trap "st=141; $do_exit" 13
trap "st=143; $do_exit" 15
# Test script is run here.
"$@" >$log_file 2>&1
estatus=$?
if test $enable_hard_errors = no && test $estatus -eq 99; then
tweaked_estatus=1
else
tweaked_estatus=$estatus
fi
case $tweaked_estatus:$expect_failure in
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
0:*) col=$grn res=PASS recheck=no gcopy=no;;
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
esac
# Report the test outcome and exit status in the logs, so that one can
# know whether the test passed or failed simply by looking at the '.log'
# file, without the need of also peaking into the corresponding '.trs'
# file (automake bug#11814).
echo "$res $test_name (exit status: $estatus)" >>$log_file
# Report outcome to console.
echo "${col}${res}${std}: $test_name"
# Register the test result, and other relevant metadata.
echo ":test-result: $res" > $trs_file
echo ":global-test-result: $res" >> $trs_file
echo ":recheck: $recheck" >> $trs_file
echo ":copy-in-global-log: $gcopy" >> $trs_file
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

117
config.h.in Normal file
View File

@ -0,0 +1,117 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the `asprintf' function. */
#undef HAVE_ASPRINTF
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the `gmtime_r' function. */
#undef HAVE_GMTIME_R
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `mkstemps' function. */
#undef HAVE_MKSTEMPS
/* Define to 1 if you have the `mlockall' function. */
#undef HAVE_MLOCKALL
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strcasestr' function. */
#undef HAVE_STRCASESTR
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the `timegm' function. */
#undef HAVE_TIMEGM
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* libtomcrypt uses the pre-1.18 PKCS #1 constant naming convention */
#undef LIBTOMCRYPT_OLD_PKCS_NAMES
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* nettle uses the pre-3.x base64 API */
#undef NETTLE_OLD_BASE64_API
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Version number of package */
#undef VERSION
/* Define to 1 if on MINIX. */
#undef _MINIX
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
#undef _POSIX_1_SOURCE
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE

16689
configure vendored Executable file

File diff suppressed because it is too large Load Diff

288
configure.ac Normal file
View File

@ -0,0 +1,288 @@
AC_PREREQ([2.61])
AC_INIT([stoken], [0.92], [cernekee@gmail.com], [stoken], [http://stoken.sf.net/])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.11 parallel-tests])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_LIBTOOL
AC_GNU_SOURCE
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
LT_INIT([win32-dll])
# Upstream's pkg.m4 (since 0.27) offers this now, but define our own
# compatible version in case the local version of pkgconfig isn't new enough.
# https://bugs.freedesktop.org/show_bug.cgi?id=48743
m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
[AC_ARG_WITH([pkgconfigdir],
[AS_HELP_STRING([--with-pkgconfigdir],
[install directory for stoken.pc pkg-config file])],
[],[with_pkgconfigdir='$(libdir)/pkgconfig'])
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
: ${CFLAGS=""}
AC_PROG_CC
AM_PROG_CC_C_O
AC_CONFIG_FILES([Makefile])
# --enable-debug
AC_ARG_ENABLE(
[debug],
[AS_HELP_STRING([--enable-debug],[enable debugging code and output [default=no]])],
[enable_debug=$enableval],
[enable_debug="no"]
)
if test "x$enable_debug" = xyes; then
CFLAGS="$CFLAGS -O0 -ggdb -fno-inline"
fi
# --enable-valgrind
AC_ARG_ENABLE([valgrind], [AS_HELP_STRING([--enable-valgrind],
[use valgrind when running 'make check' [default=no]])],
[enable_valgrind=$enableval],
[enable_valgrind=no])
AM_CONDITIONAL([ENABLE_VALGRIND], [test $enable_valgrind != no])
EXTRA_PC_LIBS=""
AS_COMPILER_FLAGS(WFLAGS,
"-Wall
-Wextra
-Wno-missing-field-initializers
-Wno-sign-compare
-Wno-pointer-sign
-Wno-unused-parameter
-Wno-unused-const-variable
-Werror=pointer-to-int-cast
-Wdeclaration-after-statement
-Werror-implicit-function-declaration
-Wformat-security
-Winit-self
-Wno-missing-declarations
-Wmissing-include-dirs
-Wnested-externs
-Wpointer-arith
-Wwrite-strings")
AC_SUBST(WFLAGS, [$WFLAGS])
# mlockall() is missing on Bionic (Android)
AC_CHECK_FUNCS([mlockall])
# mkstemps is missing on uClibc
AC_CHECK_FUNCS([mkstemps])
# TODO: see if compatibility functions are needed to build on Darwin
AC_CHECK_FUNCS([strcasestr asprintf])
# time functions are slightly different on Windows
AC_CHECK_FUNCS([gmtime_r timegm])
# gtk / stoken-gui
AC_ARG_WITH([gtk], [AS_HELP_STRING([--with-gtk],
[build stoken-gui for gtk+ @<:@default=check@:>@])],
[],
[with_gtk=check])
PKG_PROG_PKG_CONFIG
if test "x$PKG_CONFIG" = x; then
if test "x$with_gtk" = xyes; then
AC_MSG_FAILURE([--with-gtk requires pkg-config])
fi
with_gtk=no
fi
enable_gui=no
AS_IF([test "x$with_gtk" != xno],
[PKG_CHECK_MODULES([GTK], [gtk+-3.0],
[enable_gui=yes],
[if test "x$with_gtk" != xcheck; then
AC_MSG_FAILURE(
[--with-gtk was given, but test for gtk failed])
fi
])
]
)
if test $enable_gui = yes; then
AC_MSG_CHECKING([if gtk+ is usable])
saved_LIBS="$LIBS"
saved_CFLAGS="$CFLAGS"
LIBS="$LIBS $GTK_LIBS"
CFLAGS="$CFLAGS $GTK_CFLAGS"
AC_TRY_LINK([#include <gtk/gtk.h>
#include <stdlib.h>],
[gtk_init(NULL,NULL);],
[AC_MSG_RESULT([yes])],
[AC_MSG_FAILURE([unable to link gtk+ test program])])
LIBS="$saved_LIBS"
CFLAGS="$saved_CFLAGS"
fi
AM_CONDITIONAL([ENABLE_GUI], [test $enable_gui = yes])
# libxml2 support for sdtid file parsing
PKG_CHECK_MODULES([LIBXML2], [libxml-2.0])
# crypto library (prefer tomcrypt if unspecified)
AC_ARG_WITH([tomcrypt], [AS_HELP_STRING([--with-tomcrypt],
[use libtomcrypt for crypto @<:@default=check@:>@])],
[with_tomcrypt=$withval],
[with_tomcrypt=check])
AC_ARG_WITH([nettle], [AS_HELP_STRING([--with-nettle],
[use nettle for crypto @<:@default=check@:>@])],
[with_nettle=$withval],
[with_nettle=check])
CRYPTO_BACKEND=""
if test "$with_tomcrypt" != no -a "$with_nettle" != yes; then
# libtomcrypt
# Some distributions add a libtomcrypt.pc file, but it isn't in the
# upstream libtomcrypt distribution so we can't count on it.
tomcrypt_pkg=no
if test "x$PKG_CONFIG" != x; then
PKG_CHECK_EXISTS([libtomcrypt], [tomcrypt_pkg=yes], [])
fi
if test $tomcrypt_pkg = no; then
AC_SUBST(TOMCRYPT_LIBS, [-ltomcrypt])
AC_SUBST(DEPS_PC, [])
TOMCRYPT_PC_LIBS="-ltomcrypt"
else
AC_SUBST(DEPS_PC, [libtomcrypt])
PKG_CHECK_MODULES([TOMCRYPT], libtomcrypt)
TOMCRYPT_PC_LIBS=
fi
CRYPTO_LIBS="$TOMCRYPT_LIBS"
CRYPTO_CFLAGS="-DLTM_DESC"
saved_LIBS="$LIBS"
saved_CFLAGS="$CFLAGS"
LIBS="$LIBS $CRYPTO_LIBS"
CFLAGS="$CFLAGS $CRYPTO_CFLAGS"
AC_MSG_CHECKING([if libtomcrypt is usable])
AC_TRY_LINK([#include <tomcrypt.h>
#include <stdlib.h>],
[rijndael_ecb_encrypt(NULL,NULL,NULL);
ltc_mp = ltm_desc;
register_hash(&sha1_desc);],
[AC_MSG_RESULT([yes])
CRYPTO_BACKEND="tomcrypt"
EXTRA_PC_LIBS="$EXTRA_PC_LIBS $TOMCRYPT_PC_LIBS"],
[AC_MSG_RESULT([no])])
AC_MSG_CHECKING([whether libtomcrypt uses newer LTC_PKCS_1_V1_5 naming convention])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <tomcrypt.h>],
[int padding = LTC_PKCS_1_V1_5;])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
AC_DEFINE([LIBTOMCRYPT_OLD_PKCS_NAMES], [1],
[libtomcrypt uses the pre-1.18 PKCS #1 constant naming convention])])
LIBS="$saved_LIBS"
CFLAGS="$saved_CFLAGS"
fi
if test -z "$CRYPTO_BACKEND" -a "$with_nettle" != no -a "$with_tomcrypt" != yes; then
PKG_CHECK_MODULES(NETTLE, [nettle >= 2.4])
PKG_CHECK_MODULES(HOGWEED, [hogweed >= 2.4])
deps="nettle, hogweed"
AC_SUBST(DEPS_PC, [$deps])
CRYPTO_CFLAGS="$NETTLE_CFLAGS $HOGWEED_CFLAGS"
# gmp is added since we directly use GMP functions
# unfortunately it doesn't show up in pkg-config
CRYPTO_LIBS="$NETTLE_LIBS $HOGWEED_LIBS -lgmp"
CRYPTO_BACKEND="nettle"
# nettle 2.x accepts a bidirectional (unsigned *) argument
# nettle 3.x accepts a (size_t *) output-only argument
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Werror"
AC_MSG_CHECKING([nettle base64 API version])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <nettle/base64.h>
#include <stdlib.h>],
[base64_decode_update(NULL, (size_t *)NULL, NULL, 0, NULL);])],
AC_MSG_RESULT([3.0+]),
[AC_MSG_RESULT([2.x])
AC_DEFINE([NETTLE_OLD_BASE64_API], [1],
[nettle uses the pre-3.x base64 API])])
CFLAGS="$saved_CFLAGS"
fi
if test -z "$CRYPTO_BACKEND"; then
AC_MSG_ERROR([Cannot find a suitable crypto library])
fi
AC_SUBST(EXTRA_PC_LIBS, [$EXTRA_PC_LIBS])
AC_SUBST(CRYPTO_CFLAGS, [$CRYPTO_CFLAGS])
AC_SUBST(CRYPTO_LIBS, [$CRYPTO_LIBS])
AC_SUBST(CRYPTO_BACKEND, [$CRYPTO_BACKEND])
# JNI
AC_ARG_WITH([java],
AS_HELP_STRING([--with-java(=DIR)],
[Build JNI bindings using jni.h from DIR [default=no]]),
[], [with_java=no])
if test "$with_java" = "yes"; then
AX_JNI_INCLUDE_DIR
for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
JNI_CFLAGS="$JNI_CFLAGS -I$JNI_INCLUDE_DIR"
done
elif test "$with_java" = "no"; then
JNI_CFLAGS=""
else
JNI_CFLAGS="-I$with_java"
fi
if test "x$JNI_CFLAGS" != "x"; then
oldCFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $JNI_CFLAGS"
AC_MSG_CHECKING([jni.h usability])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <jni.h>],
[jint foo = 0; (void)foo;])],
AC_MSG_RESULT([yes]),
[AC_MSG_RESULT([no])
AC_MSG_ERROR([unable to compile JNI test program])])
CFLAGS="$oldCFLAGS"
AC_SUBST(JNI_CFLAGS, [$JNI_CFLAGS])
fi
AM_CONDITIONAL(USE_JNI, [test "$JNI_CFLAGS" != ""])
AC_ARG_ENABLE([jni-standalone],
AS_HELP_STRING([--enable-jni-standalone],
[build JNI stubs directly into libstoken.so [default=no]]),
[jni_standalone=$enableval],
[jni_standalone=no])
AM_CONDITIONAL(JNI_STANDALONE, [test $jni_standalone = yes])
# library version
AX_CHECK_VSCRIPT
libhdr=${srcdir}/src/stoken.h
APIMAJOR="`awk '/#define STOKEN_API_VER_MAJOR/ {print $3}' ${libhdr}`"
APIMINOR="`awk '/#define STOKEN_API_VER_MINOR/ {print $3}' ${libhdr}`"
AC_SUBST(APIMAJOR)
AC_SUBST(APIMINOR)
AC_CONFIG_FILES(stoken.pc)
AC_OUTPUT

2
examples/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
libstoken-test

173
examples/libstoken-test.c Normal file
View File

@ -0,0 +1,173 @@
/*
* libstoken-test.c - example program illustrating the use of libstoken
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Build instructions:
*
* CFLAGS=`pkg-config --cflags stoken`
* LIBS=`pkg-config --libs stoken`
* gcc -c libstoken-test.c -o libstoken-test.o $CFLAGS
* gcc libstoken-test.o -o libstoken-test $LIBS
*
* Usage:
*
* # generate tokencode from ~/.stokenrc (if present)
* ./libstoken-test
*
* # generate tokencode from a different stokenrc file
* ./libstoken-test /tmp/stokenrc
*
* # generate tokencode from a token string provided on the command line
* ./libstoken-test 252503079680743142131101346153112272336172670304467711744173124152503452716757206
*
* # generate tokencode from an sdtid XML file
* ./libstoken-test "`cat foo.sdtid`"
*/
#include <signal.h>
#include <stoken.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#define BUFLEN 64
static void die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fflush(stdout);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(1);
}
static int raw_read_user_input(char *out, int max_len)
{
char *p;
fflush(stdout);
fflush(stderr);
if (fgets(out, max_len, stdin) == NULL) {
*out = 0;
return 0;
}
p = strchr(out, '\n');
if (p)
*p = 0;
return strlen(out);
}
static struct termios oldtio;
static void stdin_echo(int enable_echo);
static void restore_tio(int sig)
{
stdin_echo(1);
puts("");
exit(1);
}
static void stdin_echo(int enable_echo)
{
struct termios tio;
struct sigaction sa;
const int fd = 0;
if (!enable_echo) {
/* ripped from busybox bb_ask() */
tcgetattr(fd, &oldtio);
tcflush(fd, TCIFLUSH);
tio = oldtio;
tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
tcsetattr(fd, TCSANOW, &tio);
/* restore a sane terminal state if interrupted */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &restore_tio;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
} else
tcsetattr(fd, TCSANOW, &oldtio);
}
static void prompt(const char *msg, char *out, int max_len)
{
int rc;
printf("%s", msg);
stdin_echo(0);
rc = raw_read_user_input(out, max_len);
stdin_echo(1);
puts("");
if (rc == 0)
die("Aborting...\n");
}
int main(int argc, char **argv)
{
struct stoken_ctx *ctx = stoken_new();
char devid[BUFLEN] = { 0 }, pass[BUFLEN] = { 0 }, pin[BUFLEN] = { 0 };
char out[STOKEN_MAX_TOKENCODE + 1];
int rc;
if (argc >= 2) {
char *s = argv[1];
if (*s == '1' || *s == '2' || *s == '<') {
rc = stoken_import_string(ctx, s);
if (rc)
die("stoken_import_string returned %d\n", rc);
} else {
rc = stoken_import_rcfile(ctx, s);
if (rc)
die("stoken_import_rcfile returned %d\n", rc);
}
} else {
rc = stoken_import_rcfile(ctx, NULL);
if (rc)
die("stoken_import_rcfile returned %d\n", rc);
}
if (stoken_devid_required(ctx))
prompt("Device ID: ", devid, BUFLEN);
if (stoken_pass_required(ctx))
prompt("Password: ", pass, BUFLEN);
rc = stoken_decrypt_seed(ctx, pass, devid);
if (rc)
die("stoken_decrypt_seed returned %d\n", rc);
if (stoken_pin_required(ctx))
prompt("PIN: ", pin, BUFLEN);
rc = stoken_compute_tokencode(ctx, time(NULL), pin, out);
if (rc)
die("stoken_compute_tokencode returned %d\n", rc);
printf("Tokencode: %s\n", out);
stoken_destroy(ctx);
return 0;
}

30
examples/qr-encode.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Sample script to encode a token as a QR code. Requires "display" from
# ImageMagick, and qrencode. The input token must not be protected with
# a password or device ID.
if [ -n "$1" ]; then
if [ -e "$1" ]; then
args="--file $1"
else
args="--token $1"
fi
else
args=""
fi
token=$(stoken export --batch --android $args 2> /dev/null)
if [ $? != 0 ]; then
echo "usage: $0 [ { <token_string> | <sdtid_file> } ]"
exit 1
fi
set -ex
png=$(mktemp /tmp/qr-XXXXXX.png)
qrencode -o $png -l H "$token"
display $png
rm -f $png
exit 0

138
examples/sdtid-test.pl Executable file
View File

@ -0,0 +1,138 @@
#!/usr/bin/perl -w
use strict;
use XML::LibXML;
my $stoken = "stoken";
my $tc = "TokenConverter";
# --once means exit after the first try, leaving a sample sdtid file in cwd
my $once = 0;
sub add_str_node($$$)
{
my ($parent, $name, $value) = @_;
my $doc = $parent->ownerDocument;
my $node = $doc->createElement($name);
$node->appendChild($doc->createTextNode($value));
$parent->appendChild($node);
}
sub rand_str
{
my ($len) = @_;
my $max_rand = 28;
if (!defined($len)) {
$len = int(rand() * $max_rand) + 5;
}
my $ret = "";
while (1) {
my $c = chr(32 + int(rand() * 95));
# these expand to 2-byte sequences. see mangle_encoding()
if ($c eq '&' || $c eq '<' || $c eq '>') {
$len -= 2;
} else {
$len--;
}
if ($len <= 0) {
last;
}
$ret .= $c;
}
return $ret;
}
sub rand_bool()
{
return int(rand() * 2);
}
sub random_doc()
{
my $doc = XML::LibXML::Document->new('1.0');
my $root = $doc->createElement("TKNBatch");
$doc->setDocumentElement($root);
my $node = $doc->createElement("TKNHeader");
$root->appendChild($node);
add_str_node($node, "Version", "0");
add_str_node($node, "Origin", rand_str());
add_str_node($node, "Dest", rand_str());
add_str_node($node, "Name", rand_str(16));
add_str_node($node, "FirstToken", rand_str());
add_str_node($node, "LastToken", rand_str());
# NumTokens: default
add_str_node($node, "DefAddPIN", rand_bool());
add_str_node($node, "DefLocalPIN", rand_bool());
add_str_node($node, "DefCopyProtection", rand_bool());
add_str_node($node, "DefPinType", rand_bool());
add_str_node($node, "DefKeypad", rand_bool());
add_str_node($node, "DefProtLevel", rand_bool());
add_str_node($node, "DefRevision", rand_bool());
add_str_node($node, "DefTimeDerivedSeeds", rand_bool());
add_str_node($node, "DefAppDerivedSeeds", rand_bool());
# DefFormFactor: default
# HeaderMAC: computed
my $tkn = $doc->createElement("TKN");
$root->appendChild($tkn);
# SN: random
# Seed: random
add_str_node($tkn, "UserFirstName", rand_str());
add_str_node($tkn, "UserLastName", rand_str());
add_str_node($tkn, "UserLogin", rand_str());
$node = $doc->createElement("TokenAttributes");
$tkn->appendChild($node);
# DeviceSerialNumber: blank
add_str_node($node, "Nickname", rand_str());
# TokenMAC: computed
$node = $doc->createElement("TKNTrailer");
$root->appendChild($node);
add_str_node($node, "BatchSignature", rand_str(100));
add_str_node($node, "BatchCertificate", rand_str(500));
return $doc;
}
#
# MAIN
#
# allow running from the source dir
if (-x "../stoken") {
$ENV{'PATH'} = "..:".$ENV{'PATH'};
}
while (@ARGV != 0) {
my $a = $ARGV[0];
shift @ARGV;
if ($a eq "--once") {
$once = 1;
} else {
die "unknown arg: '$a'";
}
}
do {
my $doc = random_doc();
open(F, ">tpl.xml") or die;
print F $doc->toString(1);
close(F);
system("$stoken export --random --template tpl.xml --sdtid > out.sdtid") == 0
or die "can't run stoken";
system("$tc out.sdtid > ctf.txt") == 0 or die "TokenConverter failed";
system("$stoken show --file ctf.txt --seed | head -n 2 > seed.txt")
== 0 or die "can't read seed from ctf";
system("$stoken show --file out.sdtid --seed | head -n 2 > seed-test.txt")
== 0 or die "can't read seed from sdtid";
system("cmp seed.txt seed-test.txt") == 0 or die "seed mismatch";
} while (!$once);
exit 0;

149
gui/password-dialog.ui Normal file
View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkDialog" id="dialog_window">
<property name="can_focus">False</property>
<property name="border_width">10</property>
<property name="title" translatable="yes">Software Token</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="ok_button">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="neg_button">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<property name="yalign">0.62000000476837158</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-dialog-authentication</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="label" translatable="yes">&lt;span size="xx-large"&gt;Password required&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Please enter the password to unlock this software token.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="is_focus">True</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="visibility">False</property>
<property name="activates_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">ok_button</action-widget>
<action-widget response="-6">neg_button</action-widget>
</action-widgets>
</object>
</interface>

148
gui/pin-dialog.ui Normal file
View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkDialog" id="dialog_window">
<property name="can_focus">False</property>
<property name="border_width">10</property>
<property name="title" translatable="yes">Software Token</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="ok_button">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="neg_button">
<property name="label">Skip</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="yalign">0.62000000476837158</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-dialog-authentication</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="label" translatable="yes">&lt;span size="xx-large"&gt;PIN required&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Please enter your PIN.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="is_focus">True</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="visibility">False</property>
<property name="activates_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">ok_button</action-widget>
<action-widget response="-6">neg_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -0,0 +1,10 @@
[Desktop Entry]
Name=Software Token (small)
GenericName=Software Token
Comment=RSA SecurID-compatible software token
Exec=stoken-gui --small
Icon=stoken-gui
Terminal=false
Type=Application
Categories=Security;GTK;Utility;
Keywords=RSA;SecurID;Authenticator;

10
gui/stoken-gui.desktop Normal file
View File

@ -0,0 +1,10 @@
[Desktop Entry]
Name=Software Token
GenericName=Software Token
Comment=RSA SecurID-compatible software token
Exec=stoken-gui
Icon=stoken-gui
Terminal=false
Type=Application
Categories=Security;GTK;Utility;
Keywords=RSA;SecurID;Authenticator;

BIN
gui/stoken-gui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

327
gui/tokencode-detail.ui Normal file
View File

@ -0,0 +1,327 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkWindow" id="app_window">
<property name="can_focus">False</property>
<property name="border_width">10</property>
<property name="title" translatable="yes">Software Token</property>
<property name="default_width">300</property>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="tokencode_text">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span size="xx-large" weight="bold"&gt;0000 0000&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkDrawingArea" id="progress_bar">
<property name="height_request">10</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="copy_button">
<property name="label" translatable="yes">_Copy</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="hexpand">True</property>
<property name="relief">none</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">10</property>
<property name="margin_bottom">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEventBox" id="next_tokencode_eventbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Click to copy to clipboard</property>
<child>
<object class="GtkBox" id="box4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Next tokencode:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="next_tokencode_text">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Using PIN:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="using_pin_text">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEventBox" id="eventbox2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Click to copy to clipboard</property>
<child>
<object class="GtkBox" id="box8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Token S/N:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="token_sn_text">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box10">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Expiration date:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="exp_date_text">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

76
gui/tokencode-small.ui Normal file
View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkWindow" id="app_window">
<property name="can_focus">False</property>
<property name="border_width">10</property>
<property name="title" translatable="yes">Software Token</property>
<property name="resizable">False</property>
<property name="has_resize_grip">False</property>
<child>
<object class="GtkEventBox" id="event_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Click to copy to clipboard</property>
<child>
<object class="GtkFrame" id="tokencode_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="top_padding">10</property>
<property name="bottom_padding">10</property>
<property name="left_padding">10</property>
<property name="right_padding">10</property>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="tokencode_text">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span size="xx-large" weight="bold"&gt;0000 0000&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkDrawingArea" id="progress_bar">
<property name="height_request">10</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="small_tokencode_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Tokencode</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

2
java/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/
dist/

18
java/README Normal file
View File

@ -0,0 +1,18 @@
Description:
This directory contains a JNI interface layer for libstoken, and a
demo program to show how it can be used.
Build instructions:
From the top level, run:
./configure --with-java
make
cd java
ant
java -Djava.library.path=../.libs -jar dist/example.jar \
{ <token_string> | <stokenrc_path> }
Test/demo code is in src/com/example/
LibStoken wrapper library is in src/org/stoken/

33
java/build.xml Normal file
View File

@ -0,0 +1,33 @@
<project name="LibStoken" default="dist" basedir=".">
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<property name="src" location="src"/>
<property name="jar_lib" location="${dist}/stoken-wrapper.jar"/>
<property name="jar_app" location="${dist}/example.jar"/>
<target name="init">
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}"
includeantruntime="false"/>
</target>
<target name="dist" depends="compile">
<jar jarfile="${jar_lib}" basedir="${build}"
includes="org/stoken/*" />
<jar jarfile="${jar_app}" basedir="${build}">
<manifest>
<attribute name="Main-Class" value="com.example.LibTest" />
</manifest>
</jar>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>

View File

@ -0,0 +1,120 @@
/*
* LibTest.java - stoken Java test program
*
* Copyright 2014 Kevin Cernekee <cernekee@gmail.com>
*
* 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 ``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 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.
*/
package com.example;
import java.io.*;
import java.util.*;
import java.text.*;
import org.stoken.LibStoken;
public final class LibTest {
private static void die(String msg) {
System.out.println(msg);
System.exit(1);
}
private static void die(String msg, int error) {
String errors[] = { "SUCCESS", "INVALID_FORMAT", "IO_ERROR", "FILE_NOT_FOUND" };
error = -error;
if (error < errors.length) {
die(msg + ": " + errors[error]);
} else {
die(msg + ": unknown error");
}
}
private static String getline(String prompt) {
System.out.print(prompt);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
String line = br.readLine();
return line;
} catch (IOException e) {
die("\nI/O error");
}
return "";
}
public static void main(String argv[]) {
System.loadLibrary("stoken-wrapper");
LibStoken lib = new LibStoken();
if (argv.length != 1) {
die("usage: LibTest { <token_string> | <stokenrc_path> }");
}
int ret = lib.importRCFile(argv[0]);
if (ret != LibStoken.SUCCESS) {
ret = lib.importString(argv[0]);
if (ret != LibStoken.SUCCESS) {
die("Can't parse token string", ret);
}
}
String devID = null;
if (lib.isDevIDRequired()) {
devID = getline("Enter Device ID: ");
}
if (!lib.checkDevID(devID)) {
die("Device ID does not match token");
}
String pass = null;
if (lib.isPassRequired()) {
pass = getline("Enter password: ");
}
ret = lib.decryptSeed(pass, devID);
if (ret != LibStoken.SUCCESS) {
die("Unable to decrypt seed", ret);
}
LibStoken.StokenInfo info = lib.getInfo();
System.out.println("SN: " + info.serial);
Date d = new Date(info.unixExpDate * 1000);
System.out.println("Exp: " + new SimpleDateFormat("yyyy-MM-dd").format(d));
String PIN = null;
if (lib.isPINRequired()) {
PIN = getline("Enter PIN: ");
}
if (!lib.checkPIN(PIN)) {
die("Invalid PIN format");
}
String tokencode = lib.computeTokencode(0, PIN);
if (tokencode == null) {
die("Unable to compute tokencode");
}
System.out.println("TOKENCODE: " + tokencode);
lib.destroy();
}
}

View File

@ -0,0 +1,87 @@
/*
* LibStoken.java - Java wrapper for libstoken.so
*
* Copyright 2014 Kevin Cernekee <cernekee@gmail.com>
*
* 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 ``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 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.
*/
package org.stoken;
public class LibStoken {
/* constants */
public static final int SUCCESS = 0;
public static final int INVALID_FORMAT = -1;
public static final int IO_ERROR = -2;
public static final int FILE_NOT_FOUND = -3;
/* create/destroy library instances */
public LibStoken() {
libctx = init();
}
public synchronized void destroy() {
if (libctx != 0) {
free();
libctx = 0;
}
}
public static class StokenInfo {
public String serial;
public long unixExpDate;
public int interval;
public int tokenVersion;
public boolean usesPin;
};
public static class StokenGUID {
public String tag;
public String longName;
public String GUID;
}
/* public APIs */
public synchronized native int importRCFile(String path);
public synchronized native int importString(String str);
public synchronized native StokenInfo getInfo();
public synchronized native int getMinPIN();
public synchronized native int getMaxPIN();
public synchronized native boolean isPINRequired();
public synchronized native boolean isPassRequired();
public synchronized native boolean isDevIDRequired();
public synchronized native boolean checkPIN(String PIN);
public synchronized native boolean checkDevID(String DevID);
public synchronized native StokenGUID[] getGUIDList();
public synchronized native int decryptSeed(String pass, String devID);
public synchronized native String encryptSeed(String pass, String devID);
public synchronized native String computeTokencode(long when, String PIN);
public synchronized native String formatTokencode(String tokencode);
/* LibStoken internals */
long libctx;
synchronized native long init();
synchronized native void free();
}

67
libstoken.map Normal file
View File

@ -0,0 +1,67 @@
STOKEN_1.0 {
global:
stoken_new;
stoken_destroy;
stoken_import_rcfile;
stoken_import_string;
stoken_pin_range;
stoken_pin_required;
stoken_pass_required;
stoken_devid_required;
stoken_check_pin;
stoken_decrypt_seed;
stoken_compute_tokencode;
};
STOKEN_1.1 {
global:
stoken_check_devid;
stoken_encrypt_seed;
} STOKEN_1.0;
STOKEN_1.2 {
global:
stoken_get_info;
} STOKEN_1.1;
STOKEN_1.3 {
global:
stoken_format_tokencode;
stoken_get_guid_list;
} STOKEN_1.2;
STOKEN_PRIVATE {
global:
securid_check_devid;
securid_check_exp;
securid_compute_tokencode;
securid_decode_token;
securid_decrypt_pin;
securid_decrypt_seed;
securid_devid_required;
securid_encode_token;
securid_encrypt_pin;
securid_pass_required;
securid_pin_format_ok;
securid_pin_required;
securid_random_token;
securid_token_info;
securid_token_interval;
securid_unix_exp_date;
sdtid_decode;
sdtid_decrypt;
sdtid_issue;
sdtid_export;
sdtid_free;
stc_standalone_init;
__stoken_parse_and_decode_token;
__stoken_read_rcfile;
__stoken_write_rcfile;
__stoken_zap_rcfile_data;
stoken__strcasestr;
stoken__mkstemps;
/* NOTE: this can break non-GNU toolchains */
Java_*;
local:
*;
};

63
m4/as-compiler-flag.m4 Normal file
View File

@ -0,0 +1,63 @@
dnl
dnl http://cgit.freedesktop.org/swfdec/swfdec/plain/m4/as-compiler-flag.m4
dnl as-compiler-flag.m4 0.1.0
dnl autostars m4 macro for detection of compiler flags
dnl David Schleef <ds@schleef.org>
dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
dnl Tries to compile with the given CFLAGS.
dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
dnl and ACTION-IF-NOT-ACCEPTED otherwise.
AC_DEFUN([AS_COMPILER_FLAG],
[
AC_MSG_CHECKING([to see if compiler understands $1])
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
CFLAGS="$save_CFLAGS"
if test "X$flag_ok" = Xyes ; then
m4_ifvaln([$2],[$2])
true
else
m4_ifvaln([$3],[$3])
true
fi
AC_MSG_RESULT([$flag_ok])
])
dnl AS_COMPILER_FLAGS(VAR, FLAGS)
dnl Tries to compile with the given CFLAGS.
AC_DEFUN([AS_COMPILER_FLAGS],
[
list=$2
flags_supported=""
flags_unsupported=""
AC_MSG_CHECKING([for supported compiler flags])
for each in $list
do
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $each"
AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
CFLAGS="$save_CFLAGS"
if test "X$flag_ok" = Xyes ; then
flags_supported="$flags_supported $each"
else
flags_unsupported="$flags_unsupported $each"
fi
done
AC_MSG_RESULT([$flags_supported])
if test "X$flags_unsupported" != X ; then
AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
fi
$1="$$1 $flags_supported"
])

142
m4/ax_check_vscript.m4 Normal file
View File

@ -0,0 +1,142 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_vscript.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_VSCRIPT
#
# DESCRIPTION
#
# Check whether the linker supports version scripts. Version scripts are
# used when building shared libraries to bind symbols to version nodes
# (helping to detect incompatibilities) or to limit the visibility of
# non-public symbols.
#
# Output:
#
# If version scripts are supported, VSCRIPT_LDFLAGS will contain the
# appropriate flag to pass to the linker. On GNU systems this would
# typically be "-Wl,--version-script", and on Solaris it would
# typically be "-Wl,-M".
#
# Two Automake conditionals are also set:
#
# HAVE_VSCRIPT is true if the linker supports version scripts with
# entries that use simple wildcards, like "local: *".
#
# HAVE_VSCRIPT_COMPLEX is true if the linker supports version scripts with
# pattern matching wildcards, like "global: Java_*".
#
# On systems that do not support symbol versioning, such as Mac OS X, both
# conditionals will be false. They will also be false if the user passes
# "--disable-symvers" on the configure command line.
#
# Example:
#
# configure.ac:
#
# AX_CHECK_VSCRIPT
#
# Makefile.am:
#
# if HAVE_VSCRIPT
# libfoo_la_LDFLAGS += $(VSCRIPT_LDFLAGS),@srcdir@/libfoo.map
# endif
#
# if HAVE_VSCRIPT_COMPLEX
# libbar_la_LDFLAGS += $(VSCRIPT_LDFLAGS),@srcdir@/libbar.map
# endif
#
# LICENSE
#
# Copyright (c) 2014 Kevin Cernekee <cernekee@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 1
# _AX_CHECK_VSCRIPT(flag, global-sym, action-if-link-succeeds, [junk-file=no])
AC_DEFUN([_AX_CHECK_VSCRIPT], [
AC_LANG_PUSH([C])
ax_check_vscript_save_flags="$LDFLAGS"
echo "V1 { global: $2; local: *; };" > conftest.map
AS_IF([test x$4 = xyes], [
echo "{" >> conftest.map
])
LDFLAGS="$LDFLAGS -Wl,$1,conftest.map"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[int show, hide;]], [])], [$3])
LDFLAGS="$ax_check_vscript_save_flags"
rm -f conftest.map
AC_LANG_POP([C])
]) dnl _AX_CHECK_VSCRIPT
AC_DEFUN([AX_CHECK_VSCRIPT], [
AC_ARG_ENABLE([symvers],
AS_HELP_STRING([--disable-symvers],
[disable library symbol versioning [default=auto]]),
[want_symvers=$enableval],
[want_symvers=yes]
)
AS_IF([test x$want_symvers = xyes], [
dnl First test --version-script and -M with a simple wildcard.
AC_CACHE_CHECK([linker version script flag], ax_cv_check_vscript_flag, [
ax_cv_check_vscript_flag=unsupported
_AX_CHECK_VSCRIPT([--version-script], [show], [
ax_cv_check_vscript_flag=--version-script
])
AS_IF([test x$ax_cv_check_vscript_flag = xunsupported], [
_AX_CHECK_VSCRIPT([-M], [show], [ax_cv_check_vscript_flag=-M])
])
dnl The linker may interpret -M (no argument) as "produce a load map."
dnl If "-M conftest.map" doesn't fail when conftest.map contains
dnl obvious syntax errors, assume this is the case.
AS_IF([test x$ax_cv_check_vscript_flag != xunsupported], [
_AX_CHECK_VSCRIPT([$ax_cv_check_vscript_flag], [show],
[ax_cv_check_vscript_flag=unsupported], [yes])
])
])
dnl If the simple wildcard worked, retest with a complex wildcard.
AS_IF([test x$ax_cv_check_vscript_flag != xunsupported], [
ax_check_vscript_flag=$ax_cv_check_vscript_flag
AC_CACHE_CHECK([if version scripts can use complex wildcards],
ax_cv_check_vscript_complex_wildcards, [
ax_cv_check_vscript_complex_wildcards=no
_AX_CHECK_VSCRIPT([$ax_cv_check_vscript_flag], [sh*], [
ax_cv_check_vscript_complex_wildcards=yes])
])
ax_check_vscript_complex_wildcards="$ax_cv_check_vscript_complex_wildcards"
], [
ax_check_vscript_flag=
ax_check_vscript_complex_wildcards=no
])
], [
AC_MSG_CHECKING([linker version script flag])
AC_MSG_RESULT([disabled])
ax_check_vscript_flag=
ax_check_vscript_complex_wildcards=no
])
AS_IF([test x$ax_check_vscript_flag != x], [
VSCRIPT_LDFLAGS="-Wl,$ax_check_vscript_flag"
AC_SUBST([VSCRIPT_LDFLAGS])
])
AM_CONDITIONAL([HAVE_VSCRIPT],
[test x$ax_check_vscript_flag != x])
AM_CONDITIONAL([HAVE_VSCRIPT_COMPLEX],
[test x$ax_check_vscript_complex_wildcards = xyes])
]) dnl AX_CHECK_VSCRIPT

132
m4/ax_jni_include_dir.m4 Normal file
View File

@ -0,0 +1,132 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_JNI_INCLUDE_DIR
#
# DESCRIPTION
#
# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
# programs using the JNI interface.
#
# JNI include directories are usually in the Java distribution. This is
# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
# that order. When this macro completes, a list of directories is left in
# the variable JNI_INCLUDE_DIRS.
#
# Example usage follows:
#
# AX_JNI_INCLUDE_DIR
#
# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
# do
# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
# done
#
# If you want to force a specific compiler:
#
# - at the configure.in level, set JAVAC=yourcompiler before calling
# AX_JNI_INCLUDE_DIR
#
# - at the configure level, setenv JAVAC
#
# Note: This macro can work with the autoconf M4 macros for Java programs.
# This particular macro is not part of the original set of macros.
#
# LICENSE
#
# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 11
AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
AC_DEFUN([AX_JNI_INCLUDE_DIR],[
JNI_INCLUDE_DIRS=""
if test "x$JAVA_HOME" != x; then
_JTOPDIR="$JAVA_HOME"
else
if test "x$JAVAC" = x; then
JAVAC=javac
fi
AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
if test "x$_ACJNI_JAVAC" = xno; then
AC_MSG_ERROR([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
fi
_ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
fi
case "$host_os" in
darwin*) # Apple JDK is at /System location and has headers symlinked elsewhere
case "$_JTOPDIR" in
/System/Library/Frameworks/JavaVM.framework/*)
_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
_JINC="$_JTOPDIR/Headers";;
*) _JINC="$_JTOPDIR/include";;
esac;;
*) _JINC="$_JTOPDIR/include";;
esac
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
_AS_ECHO_LOG([_JINC=$_JINC])
# On Mac OS X 10.6.4, jni.h is a symlink:
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
# -> ../../CurrentJDK/Headers/jni.h.
AC_CHECK_FILE([$_JINC/jni.h],
[JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JINC"],
[_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
AC_CHECK_FILE([$_JTOPDIR/include/jni.h],
[JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include"],
AC_MSG_ERROR([cannot find JDK header files]))
])
# get the likely subdirectories for system specific java includes
case "$host_os" in
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
darwin*) _JNI_INC_SUBDIRS="darwin";;
linux*) _JNI_INC_SUBDIRS="linux genunix";;
osf*) _JNI_INC_SUBDIRS="alpha";;
solaris*) _JNI_INC_SUBDIRS="solaris";;
mingw*) _JNI_INC_SUBDIRS="win32";;
cygwin*) _JNI_INC_SUBDIRS="win32";;
*) _JNI_INC_SUBDIRS="genunix";;
esac
# add any subdirectories that are present
for JINCSUBDIR in $_JNI_INC_SUBDIRS
do
if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
fi
done
])
# _ACJNI_FOLLOW_SYMLINKS <path>
# Follows symbolic links on <path>,
# finally setting variable _ACJNI_FOLLOWED
# ----------------------------------------
AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
# find the include directory relative to the javac executable
_cur="$1"
while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
AC_MSG_CHECKING([symlink for $_cur])
_slink=`ls -ld "$_cur" | sed 's/.* -> //'`
case "$_slink" in
/*) _cur="$_slink";;
# 'X' avoids triggering unwanted echo options.
*) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
esac
AC_MSG_RESULT([$_cur])
done
_ACJNI_FOLLOWED="$_cur"
])# _ACJNI

8388
m4/libtool.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

437
m4/ltoptions.m4 vendored Normal file
View File

@ -0,0 +1,437 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 8 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option '$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
_LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
[_LT_WITH_AIX_SONAME([aix])])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [1], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the 'shared' and
# 'disable-shared' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the 'static' and
# 'disable-static' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the 'fast-install'
# and 'disable-fast-install' LT_INIT options.
# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the 'disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_AIX_SONAME([DEFAULT])
# ----------------------------------
# implement the --with-aix-soname flag, and support the `aix-soname=aix'
# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
m4_define([_LT_WITH_AIX_SONAME],
[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
shared_archive_member_spec=
case $host,$enable_shared in
power*-*-aix[[5-9]]*,yes)
AC_MSG_CHECKING([which variant of shared library versioning to provide])
AC_ARG_WITH([aix-soname],
[AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
[shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
[case $withval in
aix|svr4|both)
;;
*)
AC_MSG_ERROR([Unknown argument to --with-aix-soname])
;;
esac
lt_cv_with_aix_soname=$with_aix_soname],
[AC_CACHE_VAL([lt_cv_with_aix_soname],
[lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
with_aix_soname=$lt_cv_with_aix_soname])
AC_MSG_RESULT([$with_aix_soname])
if test aix != "$with_aix_soname"; then
# For the AIX way of multilib, we name the shared archive member
# based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
# and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
# Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
# the AIX toolchain works better with OBJECT_MODE set (default 32).
if test 64 = "${OBJECT_MODE-32}"; then
shared_archive_member_spec=shr_64
else
shared_archive_member_spec=shr
fi
fi
;;
*)
with_aix_soname=aix
;;
esac
_LT_DECL([], [shared_archive_member_spec], [0],
[Shared archive member basename, for filename based shared library versioning on AIX])dnl
])# _LT_WITH_AIX_SONAME
LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
# LT_INIT options.
# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[lt_p=${PACKAGE-default}
case $withval in
yes|no) pic_mode=$withval ;;
*)
pic_mode=default
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for lt_pkg in $withval; do
IFS=$lt_save_ifs
if test "X$lt_pkg" = "X$lt_p"; then
pic_mode=yes
fi
done
IFS=$lt_save_ifs
;;
esac],
[pic_mode=m4_default([$1], [default])])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the 'pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

124
m4/ltsugar.m4 vendored Normal file
View File

@ -0,0 +1,124 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59, which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

23
m4/ltversion.m4 vendored Normal file
View File

@ -0,0 +1,23 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# @configure_input@
# serial 4179 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.4.6])
m4_define([LT_PACKAGE_REVISION], [2.4.6])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.4.6'
macro_revision='2.4.6'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

99
m4/lt~obsolete.m4 vendored Normal file
View File

@ -0,0 +1,99 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
# Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 5 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])

577
src/cli.c Normal file
View File

@ -0,0 +1,577 @@
/*
* cli.c - stoken command-line interface
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <getopt.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include "common.h"
#include "stoken.h"
#include "securid.h"
#include "sdtid.h"
#include "stoken-internal.h"
#ifdef _WIN32
static int plat_read_user_input(char *out, int max_len, int hide_chars)
{
/* TODO: Hide passwords */
char *p;
fgets(out, max_len, stdin);
p = strchr(out, '\n');
if (p)
*p = 0;
return 0;
}
static void terminal_init(void)
{
}
static int fork_and_wait(void)
{
/* TODO */
die("Subprocess support is not yet implemented on Windows.\n");
return -EINVAL;
}
#else /* _WIN32 */
#include <termios.h>
#include <sys/wait.h>
static struct termios oldtio;
static void stdin_echo(int enable_echo)
{
struct termios tio = oldtio;
const int fd = 0;
if (!enable_echo) {
/* ripped from busybox bb_ask() */
tcflush(fd, TCIFLUSH);
tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
tcsetattr(fd, TCSANOW, &tio);
} else
tcsetattr(fd, TCSANOW, &oldtio);
}
static int plat_read_user_input(char *out, int max_len, int hide_chars)
{
char *p;
int ret = 0;
stdin_echo(!hide_chars);
fflush(stdout);
fflush(stderr);
if (fgets(out, max_len, stdin) == NULL) {
*out = 0;
goto done;
}
p = strchr(out, '\n');
if (p)
*p = 0;
ret = strlen(out);
done:
stdin_echo(1);
if (hide_chars && isatty(fileno(stdin)))
puts("");
return ret;
}
static void restore_tio(int sig)
{
stdin_echo(1);
puts("");
exit(1);
}
static void terminal_init(void)
{
struct sigaction sa;
const int fd = 0;
/* restore a sane terminal state if interrupted */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &restore_tio;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
tcgetattr(fd, &oldtio);
}
static int fork_and_wait(void)
{
pid_t child = fork();
if (child < 0)
die("can't fork\n");
else if (child == 0)
return 0;
else if (child > 0) {
int rv;
wait(&rv);
if (!WIFEXITED(rv) || WEXITSTATUS(rv))
exit(1);
}
return 1;
}
#endif /* _WIN32 */
static int read_user_input(char *out, int max_len, int hide_chars)
{
static int first = 1;
if (opt_stdin) {
if (!first) {
prompt("\n");
die("error: --stdin only allows one prompt\n");
}
first = 0;
return plat_read_user_input(out, max_len, hide_chars);
}
if (opt_batch) {
prompt("\n");
die("error: --batch mode specified but command-line input is requested\n");
}
return plat_read_user_input(out, max_len, hide_chars);
}
static void print_token_info_line(const char *key, const char *value)
{
/* require --seed to show anything sensitive */
if (strcasestr(key, "seed") && !opt_seed)
return;
printf("%-24s: %s\n", key, value);
}
static time_t adjusted_time(struct securid_token *t)
{
time_t now = time(NULL);
long new_time;
if (opt_next && opt_use_time)
die("error: --use-time and --next are mutually exclusive\n");
if (opt_next)
return now + securid_token_interval(t);
if (!opt_use_time)
return now;
else if (sscanf(opt_use_time, "+%ld", &new_time) == 1)
return now + new_time;
else if (sscanf(opt_use_time, "-%ld", &new_time) == 1)
return now - new_time;
else if (sscanf(opt_use_time, "%ld", &new_time) == 1)
return new_time;
die("error: invalid --use-time argument\n");
return 0;
}
static void request_devid(struct securid_token *t, char *devid)
{
int i;
if (opt_devid) {
if (securid_check_devid(t, opt_devid) == ERR_NONE) {
xstrncpy(devid, opt_devid, BUFLEN);
return;
}
warn("warning: --devid parameter is incorrect\n");
} else {
const struct stoken_guid *glist = stoken_get_guid_list();
for (i = 0; glist[i].tag != NULL; i++) {
if (securid_check_devid(t, glist[i].guid) == ERR_NONE) {
prompt("Using class GUID for %s; use --devid to override\n",
glist[i].long_name);
strncpy(devid, glist[i].guid, BUFLEN);
return;
}
}
}
prompt("This token is bound to a specific device.\n");
for (i = 0; ; i++) {
prompt("Enter device ID from the RSA 'About' screen: ");
if (read_user_input(devid, BUFLEN, 0) == 0)
continue;
if (securid_check_devid(t, devid) == ERR_NONE)
return;
if (i == 2)
die("error: invalid device ID\n");
prompt("Device ID does not match the token.\n");
}
}
static void request_pass(const char *prompt_msg, struct securid_token *t,
char *pass, char *devid)
{
int i, rc;
if (opt_password) {
rc = securid_decrypt_seed(t, opt_password, devid);
if (rc != ERR_DECRYPT_FAILED && rc != ERR_BAD_PASSWORD) {
xstrncpy(pass, opt_password, BUFLEN);
return;
}
warn("warning: --password parameter is incorrect\n");
}
for (i = 0; ; i++) {
prompt(prompt_msg);
if (read_user_input(pass, BUFLEN, 1) == 0)
continue;
rc = securid_decrypt_seed(t, pass, devid);
if (rc == ERR_DECRYPT_FAILED) {
if (i == 2)
die("error: invalid password\n");
warn("Bad password.\n");
} else
break;
}
}
static void request_new_pass(char *pass)
{
char confirm_pass[BUFLEN];
int len;
if (opt_new_password) {
len = strlen(opt_new_password);
if (len > MAX_PASS)
die("error: new password is too long\n");
xstrncpy(pass, opt_new_password, BUFLEN);
} else {
prompt("Enter new password: ");
len = read_user_input(pass, BUFLEN, 1);
prompt("Confirm new password: ");
read_user_input(confirm_pass, BUFLEN, 1);
if (len > MAX_PASS)
die("error: new password is too long\n");
if (strcmp(pass, confirm_pass) != 0)
die("error: passwords do not match\n");
}
}
static void request_pin(const char *prompt_msg, char *pin)
{
int i, rc;
if (opt_pin) {
rc = securid_pin_format_ok(opt_pin);
if (rc == ERR_BAD_LEN)
warn("warning: bad --pin argument length, ignoring\n");
else if (rc == ERR_GENERAL)
warn("warning: --pin argument is not numeric, ignoring\n");
else {
xstrncpy(pin, opt_pin, BUFLEN);
return;
}
}
for (i = 0; ; i++) {
prompt(prompt_msg);
read_user_input(pin, BUFLEN, 1);
rc = securid_pin_format_ok(pin);
if (rc == ERR_NONE)
break;
if (i == 2)
die("error: invalid PIN\n");
if (rc == ERR_BAD_LEN)
warn("PIN must be %d-%d digits. Use '0000' for no PIN.\n",
MIN_PIN, MAX_PIN);
else
warn("PIN can only contain digits.\n");
}
}
static void unlock_token(struct securid_token *t, int get_pin, char **ret_pass)
{
char devid[BUFLEN] = { 0 }, pass[BUFLEN] = { 0 }, pin[BUFLEN];
int rc;
if (securid_devid_required(t))
request_devid(t, devid);
if (securid_pass_required(t))
request_pass("Enter password to decrypt token: ",
t, pass, devid);
rc = securid_decrypt_seed(t, pass, devid);
if (rc != ERR_NONE)
die("error: can't decrypt token: %s\n", stoken_errstr[rc]);
if (t->enc_pin_str)
if (securid_decrypt_pin(t->enc_pin_str, pass, t->pin) !=
ERR_NONE)
warn("warning: can't decrypt PIN\n");
if (ret_pass && strlen(pass))
*ret_pass = xstrdup(pass);
/* always allow --pin to override .stokenrc */
if (get_pin && securid_pin_required(t) &&
(!strlen(t->pin) || opt_pin)) {
request_pin("Enter PIN:", pin);
xstrncpy(t->pin, pin, MAX_PIN + 1);
}
}
static void print_formatted(const char *buf)
{
char *formatted;
formatted = format_token(buf);
puts(formatted);
free(formatted);
}
static void display_qr(const char *filename)
{
const char *programs[] = {
/*
* I'd like to include xdg-open here, but it insists on
* opening the file in the background, which races with the
* temporary file cleanup.
*/
"display", /* ImageMagick */
"eog", /* Eye of GNOME */
"gwenview", /* KDE viewer */
"ristretto", /* Xfce */
NULL,
};
const char **p, *user;
if (fork_and_wait() != 0)
return;
user = getenv("QR_VIEWER");
if (user) {
execlp(user, user, filename, NULL);
die("unable to execute '%s'\n", user);
}
for (p = programs; *p; p++)
execlp(*p, *p, filename, NULL);
die("can't find a suitable image viewer; try setting $QR_VIEWER\n");
}
static void __export_qr(const char *filename, const char *token)
{
if (fork_and_wait() != 0)
return;
execlp("qrencode", "qrencode", "-l", "H", "-o", filename,
token, NULL);
die("can't exec qrencode (is it in your PATH?)\n");
}
static void export_qr(const char *filename, const char *token)
{
char *formatted;
if (opt_blocks) {
warn("warning: --blocks is invalid in QR mode; using --android\n");
opt_android = 1;
opt_blocks = 0;
}
if (!(opt_android || opt_iphone || opt_v3))
opt_android = 1;
formatted = format_token(token);
if (filename)
__export_qr(filename, formatted);
else {
char fname[64];
int fd;
snprintf(fname, sizeof(fname), "%s/XXXXXX.png",
getenv("TMPDIR") ? : "/tmp");
fd = mkstemps(fname, 4);
if (fd < 0)
die("can't create temp file '%s'\n", fname);
__export_qr(fname, formatted);
display_qr(fname);
unlink(fname);
}
free(formatted);
}
int main(int argc, char **argv)
{
char *cmd = parse_cmdline(argc, argv, NOT_GUI);
int rc;
char buf[BUFLEN];
struct securid_token *t;
rc = common_init(cmd);
if (rc != ERR_NONE)
die("can't initialize: %s\n", stoken_errstr[rc]);
if (!strcmp(cmd, "issue")) {
rc = sdtid_issue(opt_template, opt_new_password, opt_new_devid);
if (rc != ERR_NONE)
die("issue: error generating sdtid: %s\n",
stoken_errstr[rc]);
return 0;
}
t = current_token;
if (!t)
die("error: no token present. Use 'stoken import' to add one.\n");
terminal_init();
if (!strcmp(cmd, "tokencode")) {
int days_left;
unlock_token(t, 1, NULL);
days_left = securid_check_exp(t, adjusted_time(t));
if (days_left < 0 && !opt_force)
die("error: token has expired; use --force to override\n");
securid_compute_tokencode(t, adjusted_time(t), buf);
puts(buf);
if (days_left < 14 && !opt_force)
warn("warning: token expires in %d day%s\n", days_left,
days_left == 1 ? "" : "s");
} else if (!strcmp(cmd, "import")) {
char *pass;
unlock_token(t, 0, &pass);
if (!opt_keep_password) {
pass = xmalloc(BUFLEN);
request_new_pass(pass);
}
t->is_smartphone = 0;
securid_encode_token(t, pass, opt_new_devid, 2, buf);
rc = write_token_and_pin(buf, NULL, pass);
if (rc != ERR_NONE)
die("rcfile: error writing new token: %s\n",
stoken_errstr[rc]);
} else if (!strcmp(cmd, "export")) {
char *pass;
unlock_token(t, 0, &pass);
if (opt_new_password)
pass = opt_new_password;
else if (!opt_keep_password)
pass = NULL;
if (!opt_sdtid) {
t->is_smartphone = opt_iphone || opt_android ||
opt_v3 || opt_show_qr || opt_qr;
securid_encode_token(t, pass, opt_new_devid,
opt_v3 ? 3 : 2, buf);
if (opt_show_qr || opt_qr)
export_qr(opt_show_qr ? NULL : opt_qr, buf);
else
print_formatted(buf);
} else {
rc = sdtid_export(opt_template, t, pass, opt_new_devid);
if (rc != ERR_NONE)
die("export: error writing sdtid: %s\n",
stoken_errstr[rc]);
}
} else if (!strcmp(cmd, "show")) {
unlock_token(t, 0, NULL);
securid_token_info(t, &print_token_info_line);
} else if (!strcmp(cmd, "setpin")) {
char *pass = NULL, pin[BUFLEN], confirm_pin[BUFLEN];
int len;
if (opt_file || opt_token)
die("error: setpin only operates on the rcfile token\n");
unlock_token(t, 0, &pass);
if (opt_new_pin) {
if (securid_pin_format_ok(opt_new_pin) != ERR_NONE)
die("error: invalid --new-pin format\n");
xstrncpy(pin, opt_new_pin, BUFLEN);
len = strlen(pin);
} else {
prompt("Enter new PIN: ");
len = read_user_input(pin, BUFLEN, 1);
if (len > 0 && securid_pin_format_ok(pin) != ERR_NONE)
die("error: PIN must be 4-8 digits\n");
prompt("Confirm new PIN: ");
read_user_input(confirm_pin, BUFLEN, 1);
if (strcmp(pin, confirm_pin) != 0)
die("error: PINs do not match\n");
}
securid_encode_token(t, pass, NULL, 2, buf);
rc = write_token_and_pin(buf, len ? pin : NULL, pass);
free(pass);
if (rc != ERR_NONE)
die("error: can't set PIN: %s\n", stoken_errstr[rc]);
} else if (!strcmp(cmd, "setpass")) {
char pass[BUFLEN];
unlock_token(t, 0, NULL);
request_new_pass(pass);
securid_encode_token(t, pass, NULL, 2, buf);
/* just print to stdout if it didn't come from the rcfile */
if (opt_file || opt_token)
print_formatted(buf);
else {
rc = write_token_and_pin(buf,
strlen(t->pin) ? t->pin : NULL,
strlen(pass) ? pass : NULL);
if (rc != ERR_NONE)
die("error: can't set password: %s\n",
stoken_errstr[rc]);
}
} else
die("error: invalid command '%s'\n", cmd);
return 0;
}

481
src/common.c Normal file
View File

@ -0,0 +1,481 @@
/*
* common.c - Common functions for stoken and stoken-gui
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <ctype.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef HAVE_MLOCKALL
#include <sys/mman.h>
#endif
#include "common.h"
#include "securid.h"
#include "stoken.h"
#include "stoken-internal.h"
/* globals - shared with cli.c or gui.c */
int opt_random, opt_keep_password, opt_blocks, opt_iphone, opt_android,
opt_v3, opt_show_qr, opt_seed, opt_sdtid, opt_small, opt_next;
int opt_debug, opt_version, opt_help, opt_batch, opt_force, opt_stdin;
char *opt_rcfile, *opt_file, *opt_token, *opt_devid, *opt_password,
*opt_pin, *opt_use_time, *opt_new_password, *opt_new_devid,
*opt_new_pin, *opt_template, *opt_qr;
struct securid_token *current_token;
static int debug_level;
static struct stoken_cfg *cfg;
void prompt(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (!opt_stdin)
vfprintf(stdout, fmt, ap);
va_end(ap);
}
void warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fflush(stdout);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
void dbg(const char *fmt, ...)
{
va_list ap;
if (!debug_level)
return;
va_start(ap, fmt);
fflush(stdout);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
void die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fflush(stdout);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(1);
}
char *xstrdup(const char *s)
{
char *ret = strdup(s);
if (!ret)
die("out of memory\n");
return ret;
}
char *xconcat(const char *s1, const char *s2)
{
char *ret = xmalloc(strlen(s1) + strlen(s2) + 1);
strcpy(ret, s1);
strcat(ret, s2);
return ret;
}
void xstrncpy(char *dest, const char *src, size_t n)
{
strncpy(dest, src, n);
dest[n - 1] = 0;
}
void *xmalloc(size_t size)
{
void *ret = malloc(size);
if (!ret)
die("out of memory\n");
return ret;
}
void *xzalloc(size_t size)
{
void *ret = xmalloc(size);
memset(ret, 0, size);
return ret;
}
enum {
OPT_DEVID = 1,
OPT_USE_TIME,
OPT_NEW_PASSWORD,
OPT_NEW_DEVID,
OPT_NEW_PIN,
OPT_TEMPLATE,
OPT_QR,
};
static const struct option long_opts[] = {
/* global: token sources */
{ "rcfile", 1, NULL, 'r' },
{ "file", 1, NULL, 'i' },
{ "token", 1, NULL, 't' },
{ "random", 0, &opt_random, 1, },
/* global: secrets used to decrypt/use a seed */
{ "devid", 1, NULL, OPT_DEVID },
{ "password", 1, NULL, 'p' },
{ "pin", 1, NULL, 'n' },
/* GUI: use smaller window */
{ "small", 0, &opt_small, 1 },
/* global: misc/debug */
{ "debug", 0, NULL, 'd' },
{ "version", 0, NULL, 'v' },
{ "force", 0, NULL, 'f' },
{ "use-time", 1, NULL, OPT_USE_TIME },
{ "help", 0, NULL, 'h' },
/* all remaining options are for CLI only */
#define FINAL_GUI_OPTION "help"
{ "batch", 0, NULL, 'b' },
/* used for tokencode generation */
{ "next", 0, &opt_next, 1 },
/* these are mostly for exporting/issuing tokens */
{ "new-password", 1, NULL, OPT_NEW_PASSWORD },
{ "new-devid", 1, NULL, OPT_NEW_DEVID },
{ "new-pin", 1, NULL, OPT_NEW_PIN },
{ "template", 1, NULL, OPT_TEMPLATE },
{ "keep-password", 0, &opt_keep_password, 1 },
{ "blocks", 0, &opt_blocks, 1 },
{ "iphone", 0, &opt_iphone, 1 },
{ "android", 0, &opt_android, 1 },
{ "v3", 0, &opt_v3, 1 },
{ "sdtid", 0, &opt_sdtid, 1 },
{ "xml", 0, &opt_sdtid, 1 },
{ "qr", 1, NULL, OPT_QR },
{ "show-qr", 0, &opt_show_qr, 1 },
{ "seed", 0, &opt_seed, 1 },
{ "stdin", 0, NULL, 's' },
{ NULL, 0, NULL, 0 },
};
static void usage_common(void)
{
puts("Alternate seed sources:");
puts("");
puts(" --rcfile=<alt_rcfile>");
puts(" --token=<token_string>");
puts(" --file=<token_file>");
puts(" --random");
puts("");
puts("See the stoken(1) man page for additional information.");
}
static void usage_gui(void)
{
puts("usage: stoken-gui [ <options> ]");
puts("");
usage_common();
exit(1);
}
static void usage_cli(void)
{
puts("usage: stoken <cmd> [ <options> ]");
puts("");
puts("Common operations:");
puts("");
puts(" stoken [ tokencode ] [ --stdin ]");
puts(" stoken import { --token=<token_string> | --file=<token_file> } [ --force ]");
puts(" stoken setpass");
puts(" stoken setpin");
puts("");
puts("Other commands:");
puts("");
puts(" stoken show [ --seed ]");
puts(" stoken export [ { --blocks | --iphone | --android | --v3 | --sdtid |");
puts(" --qr=<file> | --show-qr } ]");
puts(" stoken issue [ --template=<sdtid_skeleton> ]");
puts("");
usage_common();
exit(1);
}
static void show_version(void)
{
puts(PACKAGE_STRING " - software token for Linux/UNIX systems");
puts("Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com>");
puts("");
puts("This is free software with ABSOLUTELY NO WARRANTY.");
puts("For details see the COPYING.LIB file in the source distribution.");
exit(0);
}
char *parse_cmdline(int argc, char **argv, int is_gui)
{
int ret, longindex = 0, last_gui_opt = 0;
const struct option *opt = long_opts;
char *cmd = NULL;
for (; strcmp(opt->name, FINAL_GUI_OPTION); last_gui_opt++, opt++)
;
while (1) {
ret = getopt_long(argc, argv, "r:i:t:p:n:dvhbfs",
long_opts, &longindex);
if (ret == -1)
break;
if (is_gui && longindex > last_gui_opt)
die("error: --%s is not valid in GUI mode\n",
long_opts[longindex].name);
switch (ret) {
case 'r': opt_rcfile = optarg; break;
case 'i': opt_file = optarg; break;
case 't': opt_token = optarg; break;
case 'p': opt_password = optarg; break;
case 'n': opt_pin = optarg; break;
case 'd': opt_debug = 1; break;
case 'v': opt_version = 1; break;
case 'h': opt_help = 1; break;
case 'b': opt_batch = 1; break;
case 'f': opt_force = 1; break;
case 's': opt_stdin = 1; break;
case OPT_DEVID: opt_devid = optarg; break;
case OPT_USE_TIME: opt_use_time = optarg; break;
case OPT_NEW_PASSWORD: opt_new_password = optarg; break;
case OPT_NEW_DEVID: opt_new_devid = optarg; break;
case OPT_NEW_PIN: opt_new_pin = optarg; break;
case OPT_TEMPLATE: opt_template = optarg; break;
case OPT_QR: opt_qr = optarg; break;
case 0: break;
default: opt_help = 1;
}
}
if (!is_gui && optind == argc - 1)
cmd = argv[optind];
else if (optind == argc)
cmd = xstrdup("tokencode"); /* default command */
else
warn("error: too many command-line arguments\n");
if (!cmd || !strcmp(cmd, "help") || opt_help) {
if (is_gui)
usage_gui();
else
usage_cli();
}
if (!strcmp(cmd, "version") || opt_version)
show_version();
return cmd;
}
static int read_token_from_file(char *filename, struct securid_token *t)
{
char buf[65536], *p;
int rc = ERR_BAD_LEN;
FILE *f;
size_t len;
f = fopen(filename, "r");
if (f == NULL)
return ERR_FILE_READ;
len = fread(buf, 1, sizeof(buf) - 1, f);
if (ferror(f))
len = 0;
fclose(f);
if (len == 0)
return ERR_FILE_READ;
buf[len] = 0;
for (p = buf; *p; ) {
rc = __stoken_parse_and_decode_token(p, t, 1);
/*
* keep checking more lines until we find something that
* looks like a token
*/
if (rc != ERR_GENERAL)
break;
p = strchr(p, '\n');
if (!p)
break;
p++;
}
return rc;
}
static int decode_rc_token(struct stoken_cfg *cfg, struct securid_token *t)
{
int rc = securid_decode_token(cfg->rc_token, t);
if (rc != ERR_NONE) {
warn("rcfile: token data is garbled, ignoring\n");
return rc;
}
if (cfg->rc_pin) {
if (t->flags & FL_PASSPROT)
t->enc_pin_str = xstrdup(cfg->rc_pin);
else {
if (securid_pin_format_ok(cfg->rc_pin) == ERR_NONE)
xstrncpy(t->pin, cfg->rc_pin, MAX_PIN + 1);
else
warn("rcfile: invalid PIN format\n");
}
}
return ERR_NONE;
}
int common_init(char *cmd)
{
int rc;
struct securid_token *t;
int is_import = !strcmp(cmd, "import");
/*
* we don't actually scrub memory, but at least try to keep the seeds
* from being swapped out to disk
*/
#ifdef HAVE_MLOCKALL
mlockall(MCL_CURRENT | MCL_FUTURE);
#endif
stc_standalone_init();
cfg = xzalloc(sizeof(*cfg));
if (__stoken_read_rcfile(opt_rcfile, cfg,
is_import ? &dbg : &warn) != ERR_NONE)
__stoken_zap_rcfile_data(cfg);
/* accept a token from the command line, or fall back to the rcfile */
do {
t = xzalloc(sizeof(struct securid_token));
if (opt_token) {
rc = __stoken_parse_and_decode_token(opt_token, t, 1);
if (rc != ERR_NONE)
die("error: --token string is garbled: %s\n",
stoken_errstr[rc]);
current_token = t;
break;
}
if (opt_file) {
rc = read_token_from_file(opt_file, t);
if (rc == ERR_MULTIPLE_TOKENS)
die("error: multiple tokens found; use 'stoken split' to create separate files\n");
else if (rc != ERR_NONE)
die("error: no valid token in file '%s': %s\n",
opt_file, stoken_errstr[rc]);
current_token = t;
break;
}
if (opt_random) {
rc = securid_random_token(t);
if (rc != ERR_NONE)
die("error: can't generate random token\n");
current_token = t;
break;
}
if (cfg->rc_token) {
if (is_import)
die("error: please specify --file, --token, or --random\n");
if (decode_rc_token(cfg, t) == ERR_NONE) {
current_token = t;
break;
}
}
free(t);
} while (0);
if (is_import && cfg->rc_token && !opt_force)
die("error: token already exists; use --force to overwrite it\n");
return ERR_NONE;
}
int write_token_and_pin(char *token_str, char *pin_str, char *password)
{
free(cfg->rc_ver);
free(cfg->rc_token);
free(cfg->rc_pin);
cfg->rc_token = xstrdup(token_str);
if (pin_str && !password)
cfg->rc_pin = xstrdup(pin_str);
else if (pin_str && password) {
cfg->rc_pin = securid_encrypt_pin(pin_str, password);
if (!cfg->rc_pin)
return ERR_GENERAL;
} else
cfg->rc_pin = NULL;
cfg->rc_ver = xstrdup("1");
return __stoken_write_rcfile(opt_rcfile, cfg, &warn);
}
char *format_token(const char *token_str)
{
int i;
char *out, *p;
if (opt_iphone)
return xconcat("com.rsa.securid.iphone://ctf?ctfData=",
token_str);
else if (opt_android || opt_v3)
return xconcat("http://127.0.0.1/securid/ctf?ctfData=",
token_str);
else if (!opt_blocks)
return xstrdup(token_str);
/* user requested blocks of 5 digits (--blocks) */
i = strlen(token_str);
out = xzalloc(i + (i / 5) + 2);
for (i = 0, p = out; token_str[i]; i++) {
if (i % 5 == 0 && i)
*(p++) = '-';
*(p++) = token_str[i];
}
return out;
}

81
src/common.h Normal file
View File

@ -0,0 +1,81 @@
/*
* common.h - Common definitions for stoken and stoken-gui
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __STOKEN_COMMON_H__
#define __STOKEN_COMMON_H__
#include "config.h"
#include <stdarg.h>
#include <sys/types.h>
#include "stoken-internal.h"
static const char stoken_errstr[][32] = {
"Success",
"General failure",
"Invalid length",
"Unsupported token version",
"Checksum failed",
"Invalid password format",
"Missing required password",
"Seed decryption failed",
"Device ID mismatch",
"Out of memory",
"Cannot read file",
"Multiple tokens found in input",
};
#define NOT_GUI 0
#define IS_GUI 1
void prompt(const char *fmt, ...);
void warn(const char *fmt, ...);
void dbg(const char *fmt, ...);
void die(const char *fmt, ...);
char *xstrdup(const char *s);
char *xconcat(const char *s1, const char *s2);
void xstrncpy(char *dest, const char *src, size_t n);
void *xmalloc(size_t size);
void *xzalloc(size_t size);
char *parse_cmdline(int argc, char **argv, int is_gui);
int common_init(char *cmd);
int write_token_and_pin(char *token_str, char *pin_str, char *password);
char *format_token(const char *raw_token_str);
/* binary flags, long options */
extern int opt_random, opt_keep_password, opt_blocks, opt_iphone, opt_android,
opt_v3, opt_show_qr, opt_seed, opt_sdtid, opt_small, opt_next;
/* binary flags, short/long options */
extern int opt_debug, opt_version, opt_help, opt_batch, opt_force, opt_stdin;
/* string arguments */
extern char *opt_rcfile, *opt_file, *opt_token, *opt_devid, *opt_password,
*opt_pin, *opt_use_time, *opt_new_password, *opt_new_devid,
*opt_new_pin, *opt_template, *opt_qr;
/* token read from .stokenrc, if available */
struct securid_token;
extern struct securid_token *current_token;
#endif /* !__STOKEN_COMMON_H__ */

260
src/compat.c Normal file
View File

@ -0,0 +1,260 @@
/*
* compat.c - compatibility functions for non-Linux hosts
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "stoken-internal.h"
#ifndef HAVE_STRCASESTR
/*
* Source: OpenConnect
* Copyright © 2008-2014 Intel Corporation.
* Authors: David Woodhouse <dwmw2@infradead.org>
*/
char *stoken__strcasestr(const char *haystack, const char *needle)
{
int hlen = strlen(haystack);
int nlen = strlen(needle);
int i, j;
for (i = 0; i < hlen - nlen + 1; i++) {
for (j = 0; j < nlen; j++) {
if (tolower(haystack[i + j]) !=
tolower(needle[j]))
break;
}
if (j == nlen)
return (char *)haystack + i;
}
return NULL;
}
#endif /* HAVE_STRCASESTR */
#ifndef HAVE_MKSTEMPS
/*
* Source: FreeBSD libc
* Copyright (c) 1987, 1993
* The Regents of the University of California. All rights reserved.
*/
static const unsigned char padchar[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static int
_gettemp(char *path, int *doopen, int domkdir, int slen)
{
char *start, *trv, *suffp, *carryp;
char *pad;
struct stat sbuf;
int rval;
char carrybuf[MAXPATHLEN];
if ((doopen != NULL && domkdir) || slen < 0) {
errno = EINVAL;
return (0);
}
for (trv = path; *trv != '\0'; ++trv)
;
if (trv - path >= MAXPATHLEN) {
errno = ENAMETOOLONG;
return (0);
}
trv -= slen;
suffp = trv;
--trv;
if (trv < path || NULL != strchr(suffp, '/')) {
errno = EINVAL;
return (0);
}
/* Fill space with random characters */
while (trv >= path && *trv == 'X') {
uint32_t r = rand() % (sizeof(padchar) - 1);
*trv-- = padchar[r];
}
start = trv + 1;
/* save first combination of random characters */
memcpy(carrybuf, start, suffp - start);
/*
* check the target directory.
*/
if (doopen != NULL || domkdir) {
for (; trv > path; --trv) {
if (*trv == '/') {
*trv = '\0';
rval = stat(path, &sbuf);
*trv = '/';
if (rval != 0)
return (0);
if (!S_ISDIR(sbuf.st_mode)) {
errno = ENOTDIR;
return (0);
}
break;
}
}
}
for (;;) {
if (doopen) {
if ((*doopen =
open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
return (1);
if (errno != EEXIST)
return (0);
} /* else if (domkdir) {
if (mkdir(path, 0700) == 0)
return (1);
if (errno != EEXIST)
return (0);
} else if (lstat(path, &sbuf))
return (errno == ENOENT); */
/* If we have a collision, cycle through the space of filenames */
for (trv = start, carryp = carrybuf;;) {
/* have we tried all possible permutations? */
if (trv == suffp)
return (0); /* yes - exit with EEXIST */
pad = strchr(padchar, *trv);
if (pad == NULL) {
/* this should never happen */
errno = EIO;
return (0);
}
/* increment character */
*trv = (*++pad == '\0') ? padchar[0] : *pad;
/* carry to next position? */
if (*trv == *carryp) {
/* increment position and loop */
++trv;
++carryp;
} else {
/* try with new name */
break;
}
}
}
/*NOTREACHED*/
}
int stoken__mkstemps(char *path, int slen)
{
int fd;
return (_gettemp(path, &fd, 0, slen) ? fd : -1);
}
#endif /* HAVE_MKSTEMPS */
#if !defined(HAVE_GMTIME_R) && defined(_WIN32)
struct tm *stoken__gmtime_r(const time_t *timep, struct tm *result)
{
/*
* This trick only works on Windows, because Windows gmtime()
* provides a dedicated buffer per-thread:
*
* http://msdn.microsoft.com/en-us/library/0z9czt0w.aspx
*/
memcpy(result, gmtime(timep), sizeof(struct tm));
return result;
}
#endif /* !defined(HAVE_GMTIME_R) && defined(_WIN32) */
#ifndef HAVE_TIMEGM
/*
* Source: Android Bionic libc
* Copyright (c) 2007-2008 Michael G Schwern
* Originally derived from Paul Sheer's pivotal_gmtime_r.c
* License: MIT
*/
static const int julian_days_by_month[2][12] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};
static const int length_of_year[2] = { 365, 366 };
static const int days_in_gregorian_cycle = ((365 * 400) + 100 - 4 + 1);
#define IS_LEAP(n) ((!(((n) + 1900) % 400) || \
(!(((n) + 1900) % 4) && \
(((n) + 1900) % 100))) != 0)
time_t stoken__timegm(struct tm *date)
{
time_t days = 0;
time_t seconds = 0;
int64_t year;
int64_t orig_year = (int64_t) date->tm_year;
int cycles = 0;
if (orig_year > 100) {
cycles = (orig_year - 100) / 400;
orig_year -= cycles * 400;
days += (time_t) cycles *days_in_gregorian_cycle;
} else if (orig_year < -300) {
cycles = (orig_year - 100) / 400;
orig_year -= cycles * 400;
days += (time_t) cycles *days_in_gregorian_cycle;
}
if (orig_year > 70) {
year = 70;
while (year < orig_year) {
days += length_of_year[IS_LEAP(year)];
year++;
}
} else if (orig_year < 70) {
year = 69;
do {
days -= length_of_year[IS_LEAP(year)];
year--;
} while (year >= orig_year);
}
days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
days += date->tm_mday - 1;
seconds = days * 60 * 60 * 24;
seconds += date->tm_hour * 60 * 60;
seconds += date->tm_min * 60;
seconds += date->tm_sec;
return (seconds);
}
#endif /* HAVE_TIMEGM */

466
src/gui.c Normal file
View File

@ -0,0 +1,466 @@
/*
* gui.c - stoken gtk+ interface
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include "common.h"
#include "securid.h"
#define WINDOW_TITLE "Software Token"
#define EXP_WARN_DAYS 14
#ifdef _WIN32
#undef UIDIR
#define UIDIR "."
#define PIXMAP_DIR "."
#else
#define PIXMAP_DIR DATA_DIR "/pixmaps"
#endif
static GtkWidget *tokencode_text, *next_tokencode_text, *progress_bar;
static char tokencode_str[16];
static char next_tokencode_str[16];
static int last_sec = -1;
static int token_sec;
static long time_adjustment;
static int token_days_left;
static int token_interval;
static int token_uses_pin;
static int skipped_pin;
static gboolean delete_callback(GtkWidget *widget, GdkEvent *event,
gpointer data)
{
gtk_main_quit();
return FALSE;
}
static void copy_tokencode(gpointer user_data)
{
GdkDisplay *disp = gdk_display_get_default();
GtkClipboard *clip;
char *str = user_data;
/* CLIPBOARD - Control-V in most applications */
clip = gtk_clipboard_get_for_display(disp, GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_text(clip, str, -1);
/* PRIMARY - middle-click in xterm */
clip = gtk_clipboard_get_for_display(disp, GDK_SELECTION_PRIMARY);
gtk_clipboard_set_text(clip, str, -1);
}
static void clicked_to_clipboard(GtkButton *button, gpointer user_data)
{
copy_tokencode(user_data);
}
static gboolean press_to_clipboard(GtkWidget *widget, GdkEvent *event,
gpointer user_data)
{
copy_tokencode(user_data);
return TRUE;
}
static gboolean draw_progress_bar_callback(GtkWidget *widget, cairo_t *cr,
gpointer data)
{
guint width, height, boundary;
width = gtk_widget_get_allocated_width(widget);
height = gtk_widget_get_allocated_height(widget);
boundary = width * token_sec / (token_interval - 1);
cairo_set_source_rgb(cr, 0.3, 0.4, 0.5);
cairo_rectangle(cr, 0, 0, boundary, height);
cairo_fill(cr);
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
cairo_rectangle(cr, boundary, 0, width - boundary, height);
cairo_fill(cr);
return FALSE;
}
static time_t adjusted_time(void)
{
return time(NULL) + time_adjustment;
}
static void parse_opt_use_time(void)
{
long new_time;
if (!opt_use_time)
return;
else if (sscanf(opt_use_time, "+%ld", &new_time) == 1)
time_adjustment = new_time;
else if (sscanf(opt_use_time, "-%ld", &new_time) == 1)
time_adjustment = -new_time;
else
die("error: 'stoken-gui --use-time' must specify a +/- offset\n");
}
static gint update_tokencode(gpointer data)
{
time_t now = adjusted_time();
struct tm *tm;
char str[128], *formatted;
tm = gmtime(&now);
if ((tm->tm_sec >= 30 && last_sec < 30) ||
(tm->tm_sec < 30 && last_sec >= 30) ||
last_sec == -1) {
last_sec = tm->tm_sec;
securid_compute_tokencode(current_token, now, tokencode_str);
securid_compute_tokencode(current_token, now + token_interval,
next_tokencode_str);
}
token_sec = token_interval - (tm->tm_sec % token_interval) - 1;
gtk_widget_queue_draw(GTK_WIDGET(progress_bar));
formatted = stoken_format_tokencode(tokencode_str);
if (!formatted)
die("out of memory\n");
snprintf(str, sizeof(str),
"<span size=\"xx-large\" weight=\"bold\">%s</span>",
formatted);
gtk_label_set_markup(GTK_LABEL(tokencode_text), str);
free(formatted);
if (next_tokencode_text) {
formatted = stoken_format_tokencode(next_tokencode_str);
if (!formatted)
die("out of memory\n");
gtk_label_set_text(GTK_LABEL(next_tokencode_text), formatted);
free(formatted);
}
return TRUE;
}
static void __error_dialog(GtkWindow *parent, const char *heading,
const char *msg, int is_warning)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new(parent,
parent ? GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT : 0,
is_warning ? GTK_MESSAGE_WARNING : GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK, "%s", heading);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", msg);
gtk_window_set_title(GTK_WINDOW(dialog), WINDOW_TITLE);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
if (!is_warning)
exit(1);
}
static void error_dialog(const char *heading, const char *msg)
{
return __error_dialog(NULL, heading, msg, 0);
}
static void warning_dialog(GtkWidget *parent, const char *heading,
const char *msg)
{
return __error_dialog(GTK_WINDOW(parent), heading, msg, 1);
}
static GtkWidget *create_app_window_common(GtkBuilder *builder)
{
GtkWidget *widget;
progress_bar = GTK_WIDGET(
gtk_builder_get_object(builder, "progress_bar"));
g_signal_connect(progress_bar, "draw",
G_CALLBACK(draw_progress_bar_callback), NULL);
tokencode_text = GTK_WIDGET(
gtk_builder_get_object(builder, "tokencode_text"));
widget = GTK_WIDGET(gtk_builder_get_object(builder, "app_window"));
g_signal_connect(widget, "delete-event", G_CALLBACK(delete_callback),
NULL);
return widget;
}
static void set_red_label(GtkWidget *widget, const char *text)
{
char tmp[BUFLEN];
snprintf(tmp, BUFLEN,
"<span weight=\"bold\" foreground=\"red\">%s</span>", text);
gtk_label_set_markup(GTK_LABEL(widget), tmp);
}
static void format_exp_date(GtkWidget *widget)
{
time_t exp = securid_unix_exp_date(current_token);
char tmp[BUFLEN];
/* FIXME: localization */
strftime(tmp, BUFLEN, "%Y-%m-%d", gmtime(&exp));
if (token_days_left < EXP_WARN_DAYS)
set_red_label(widget, tmp);
else
gtk_label_set_text(GTK_LABEL(widget), tmp);
}
/* gtk_builder_new_from_file() requires libgtk >= 3.10 */
static GtkBuilder *__gtk_builder_new_from_file(const gchar *filename)
{
GtkBuilder *builder;
builder = gtk_builder_new();
if (gtk_builder_add_from_file(builder, filename, NULL) == 0)
die("can't import '%s'\n", filename);
return builder;
}
static GtkWidget *create_app_window(void)
{
GtkBuilder *builder;
GtkWidget *widget;
builder = __gtk_builder_new_from_file(UIDIR "/tokencode-detail.ui");
/* static token info */
widget = GTK_WIDGET(gtk_builder_get_object(builder, "token_sn_text"));
gtk_label_set_text(GTK_LABEL(widget), current_token->serial);
widget = GTK_WIDGET(gtk_builder_get_object(builder, "exp_date_text"));
format_exp_date(widget);
widget = GTK_WIDGET(gtk_builder_get_object(builder, "using_pin_text"));
if (!token_uses_pin)
gtk_label_set_text(GTK_LABEL(widget), "Not required");
else if (skipped_pin)
set_red_label(widget, "No");
else
gtk_label_set_text(GTK_LABEL(widget), "Yes");
/* buttons */
widget = GTK_WIDGET(gtk_builder_get_object(builder, "copy_button"));
g_signal_connect(widget, "clicked", G_CALLBACK(clicked_to_clipboard),
&tokencode_str);
/* next tokencode */
next_tokencode_text = GTK_WIDGET(
gtk_builder_get_object(builder, "next_tokencode_text"));
widget = GTK_WIDGET(gtk_builder_get_object(builder,
"next_tokencode_eventbox"));
g_signal_connect(widget, "button-press-event",
G_CALLBACK(press_to_clipboard), &next_tokencode_str);
return create_app_window_common(builder);
}
static GtkWidget *create_small_app_window(void)
{
GtkBuilder *builder;
GtkWidget *widget;
builder = __gtk_builder_new_from_file(UIDIR "/tokencode-small.ui");
widget = GTK_WIDGET(gtk_builder_get_object(builder, "event_box"));
g_signal_connect(widget, "button-press-event",
G_CALLBACK(press_to_clipboard), &tokencode_str);
return create_app_window_common(builder);
}
static char *do_password_dialog(const char *ui_file)
{
GtkBuilder *builder;
GtkWidget *widget, *dialog;
gint resp;
char *ret = NULL;
builder = __gtk_builder_new_from_file(ui_file);
dialog = GTK_WIDGET(gtk_builder_get_object(builder, "dialog_window"));
gtk_widget_show_all(dialog);
resp = gtk_dialog_run(GTK_DIALOG(dialog));
if (resp == GTK_RESPONSE_OK) {
widget = GTK_WIDGET(gtk_builder_get_object(builder, "password"));
ret = strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
}
gtk_widget_destroy(dialog);
return ret;
}
static int request_credentials(struct securid_token *t)
{
int rc, pass_required = 0, pin_required = 0;
if (securid_pass_required(t)) {
pass_required = 1;
if (opt_password) {
rc = securid_decrypt_seed(t, opt_password, NULL);
if (rc == ERR_DECRYPT_FAILED)
warn("warning: --password parameter is incorrect\n");
else if (rc != ERR_NONE)
error_dialog("Token decrypt error",
stoken_errstr[rc]);
else
pass_required = 0;
}
} else {
rc = securid_decrypt_seed(t, opt_password, NULL);
if (rc != ERR_NONE)
error_dialog("Token decrypt error", stoken_errstr[rc]);
}
while (pass_required) {
const char *pass =
do_password_dialog(UIDIR "/password-dialog.ui");
if (!pass)
return ERR_MISSING_PASSWORD;
rc = securid_decrypt_seed(t, pass, NULL);
if (rc == ERR_NONE) {
if (t->enc_pin_str) {
rc = securid_decrypt_pin(t->enc_pin_str,
pass, t->pin);
if (rc != ERR_NONE)
error_dialog("PIN decrypt error",
stoken_errstr[rc]);
}
pass_required = 0;
} else if (rc == ERR_DECRYPT_FAILED)
warning_dialog(NULL, "Bad password",
"Please enter the correct password for this seed.");
else
error_dialog("Token decrypt error", stoken_errstr[rc]);
}
if (securid_pin_required(t)) {
pin_required = 1;
if (opt_pin) {
if (securid_pin_format_ok(opt_pin) == ERR_NONE) {
xstrncpy(t->pin, opt_pin, MAX_PIN + 1);
pin_required = 0;
} else
warn("warning: --pin argument is invalid\n");
} else if (strlen(t->pin) || t->enc_pin_str)
pin_required = 0;
}
while (pin_required) {
const char *pin =
do_password_dialog(UIDIR "/pin-dialog.ui");
if (!pin) {
skipped_pin = 1;
xstrncpy(t->pin, "0000", MAX_PIN + 1);
break;
}
if (securid_pin_format_ok(pin) != ERR_NONE) {
warning_dialog(NULL, "Bad PIN",
"Please enter 4-8 digits, or click Skip for no PIN.");
} else {
xstrncpy(t->pin, pin, MAX_PIN + 1);
break;
}
}
return ERR_NONE;
}
int main(int argc, char **argv)
{
GtkWidget *window;
char *cmd;
gtk_init(&argc, &argv);
gtk_window_set_default_icon_from_file(
PIXMAP_DIR "/stoken-gui.png", NULL);
cmd = parse_cmdline(argc, argv, IS_GUI);
/* check for a couple of error conditions */
if (common_init(cmd))
error_dialog("Application error",
"Unable to initialize crypto library.");
if (!current_token)
error_dialog("Missing token",
"Please use 'stoken import' to add a new seed.");
if (securid_devid_required(current_token))
error_dialog("Unsupported token",
"Please use 'stoken' to handle tokens encrypted with a device ID.");
/* check for token expiration */
parse_opt_use_time();
token_days_left = securid_check_exp(current_token, adjusted_time());
if (!opt_force && !opt_small) {
if (token_days_left < 0)
error_dialog("Token expired",
"Please obtain a new token from your administrator.");
if (token_days_left < EXP_WARN_DAYS) {
char msg[BUFLEN];
sprintf(msg, "This token will expire in %d day%s.",
token_days_left,
token_days_left == 1 ? "" : "s");
warning_dialog(NULL, "Expiration warning", msg);
}
}
/* request password / PIN, if missing */
if (request_credentials(current_token) != ERR_NONE)
return 1;
token_interval = securid_token_interval(current_token);
token_uses_pin = securid_pin_required(current_token);
window = opt_small ? create_small_app_window() : create_app_window();
update_tokencode(NULL);
gtk_widget_show_all(window);
g_timeout_add(250, update_tokencode, NULL);
gtk_main();
return 0;
}

491
src/jni.c Normal file
View File

@ -0,0 +1,491 @@
/*
* jni.c - stoken Java Native Interface
*
* Copyright 2014 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <jni.h>
#include "stoken.h"
/* these need to match LibStoken.java */
#define SUCCESS ( 0)
#define INVALID_FORMAT (-1)
#define IO_ERROR (-2)
#define FILE_NOT_FOUND (-3)
struct libctx {
JNIEnv *jenv;
jobject jobj;
struct stoken_ctx *instance;
};
static void throw_excep(JNIEnv *jenv, const char *exc, int line)
{
jclass excep;
char msg[64];
snprintf(msg, 64, "%s:%d", __FILE__, line);
(*jenv)->ExceptionClear(jenv);
excep = (*jenv)->FindClass(jenv, exc);
if (excep)
(*jenv)->ThrowNew(jenv, excep, msg);
}
#define OOM(jenv) do { throw_excep(jenv, "java/lang/OutOfMemoryError", __LINE__); } while (0)
static int translate_errno(JNIEnv *jenv, int err)
{
switch (err) {
case 0:
return SUCCESS;
case -EINVAL:
return INVALID_FORMAT;
case -ENOENT:
return FILE_NOT_FOUND;
case -ENOMEM:
throw_excep(jenv, "java/lang/OutOfMemoryError", __LINE__);
/* falls through */
case -EIO:
default:
return IO_ERROR;
}
}
static struct libctx *getctx(JNIEnv *jenv, jobject jobj)
{
jclass jcls = (*jenv)->GetObjectClass(jenv, jobj);
jfieldID jfld = (*jenv)->GetFieldID(jenv, jcls, "libctx", "J");
if (!jfld)
return NULL;
return (void *)(unsigned long)(*jenv)->GetLongField(jenv, jobj, jfld);
}
static int set_int(struct libctx *ctx, jobject jobj, const char *name, int value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "I");
if (!jfld)
return -1;
(*ctx->jenv)->SetIntField(ctx->jenv, jobj, jfld, value);
return 0;
}
static int set_long(struct libctx *ctx, jobject jobj, const char *name, uint64_t value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "J");
if (!jfld)
return -1;
(*ctx->jenv)->SetLongField(ctx->jenv, jobj, jfld, (jlong)value);
return 0;
}
static int set_bool(struct libctx *ctx, jobject jobj, const char *name, int value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "Z");
if (!jfld)
return -1;
(*ctx->jenv)->SetBooleanField(ctx->jenv, jobj, jfld, value);
return 0;
}
static jstring dup_to_jstring(JNIEnv *jenv, const char *in)
{
/*
* Many implementations of NewStringUTF() will return NULL on
* NULL input, but that isn't guaranteed:
* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35979
*/
return in ? (*jenv)->NewStringUTF(jenv, in) : NULL;
}
static int set_string(struct libctx *ctx, jobject jobj, const char *name, const char *value)
{
jclass jcls = (*ctx->jenv)->GetObjectClass(ctx->jenv, jobj);
jfieldID jfld = (*ctx->jenv)->GetFieldID(ctx->jenv, jcls, name, "Ljava/lang/String;");
jstring jarg;
if (!jfld)
return -1;
jarg = dup_to_jstring(ctx->jenv, value);
if (value && !jarg)
return -1;
(*ctx->jenv)->SetObjectField(ctx->jenv, jobj, jfld, jarg);
return 0;
}
JNIEXPORT jlong JNICALL Java_org_stoken_LibStoken_init(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = calloc(1, sizeof(*ctx));
if (!ctx)
goto bad;
ctx->jenv = jenv;
ctx->jobj = (*jenv)->NewGlobalRef(jenv, jobj);
if (!ctx->jobj)
goto bad_free_ctx;
ctx->instance = stoken_new();
if (!ctx->instance)
goto bad_delete_ref;
return (jlong)(unsigned long)ctx;
bad_delete_ref:
(*jenv)->DeleteGlobalRef(jenv, ctx->jobj);
bad_free_ctx:
free(ctx);
bad:
OOM(jenv);
return 0;
}
JNIEXPORT void JNICALL Java_org_stoken_LibStoken_free(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
if (!ctx)
return;
stoken_destroy(ctx->instance);
(*jenv)->DeleteGlobalRef(jenv, ctx->jobj);
free(ctx);
}
JNIEXPORT jint JNICALL Java_org_stoken_LibStoken_importRCFile(
JNIEnv *jenv, jobject jobj, jstring jarg0)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0;
int ret;
if (!jarg0)
return translate_errno(jenv, -EINVAL);
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
ret = -ENOMEM;
else
ret = stoken_import_rcfile(ctx->instance, arg0);
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return translate_errno(jenv, ret);
}
JNIEXPORT jint JNICALL Java_org_stoken_LibStoken_importString(
JNIEnv *jenv, jobject jobj, jstring jarg0)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0;
int ret;
if (!jarg0)
return translate_errno(jenv, -EINVAL);
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
ret = -ENOMEM;
else
ret = stoken_import_string(ctx->instance, arg0);
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return translate_errno(jenv, ret);
}
JNIEXPORT jobject JNICALL Java_org_stoken_LibStoken_getInfo(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
struct stoken_info *info;
jmethodID mid;
jclass jcls;
jcls = (*ctx->jenv)->FindClass(ctx->jenv,
"org/stoken/LibStoken$StokenInfo");
if (jcls == NULL)
return NULL;
mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, "<init>", "()V");
if (!mid)
return NULL;
jobj = (*ctx->jenv)->NewObject(ctx->jenv, jcls, mid);
if (!jobj)
return NULL;
info = stoken_get_info(ctx->instance);
if (!info)
return NULL;
if (set_string(ctx, jobj, "serial", info->serial) ||
set_long(ctx, jobj, "unixExpDate", info->exp_date) ||
set_int(ctx, jobj, "interval", info->interval) ||
set_int(ctx, jobj, "tokenVersion", info->token_version) ||
set_bool(ctx, jobj, "usesPin", info->uses_pin))
jobj = NULL;
free(info);
return jobj;
}
JNIEXPORT jint JNICALL Java_org_stoken_LibStoken_getMinPIN(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
int min_pin, max_pin;
stoken_pin_range(ctx->instance, &min_pin, &max_pin);
return min_pin;
}
JNIEXPORT jint JNICALL Java_org_stoken_LibStoken_getMaxPIN(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
int min_pin, max_pin;
stoken_pin_range(ctx->instance, &min_pin, &max_pin);
return max_pin;
}
JNIEXPORT jboolean JNICALL Java_org_stoken_LibStoken_isPINRequired(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
return !!stoken_pin_required(ctx->instance);
}
JNIEXPORT jboolean JNICALL Java_org_stoken_LibStoken_isPassRequired(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
return !!stoken_pass_required(ctx->instance);
}
JNIEXPORT jboolean JNICALL Java_org_stoken_LibStoken_isDevIDRequired(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
return !!stoken_devid_required(ctx->instance);
}
JNIEXPORT jboolean JNICALL Java_org_stoken_LibStoken_checkPIN(
JNIEnv *jenv, jobject jobj, jstring jarg0)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0;
int ret;
if (!jarg0)
return translate_errno(jenv, -EINVAL);
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
ret = -ENOMEM;
else
ret = stoken_check_pin(ctx->instance, arg0);
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return !translate_errno(jenv, ret);
}
JNIEXPORT jboolean JNICALL Java_org_stoken_LibStoken_checkDevID(
JNIEnv *jenv, jobject jobj, jstring jarg0)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0;
int ret;
if (!jarg0)
return translate_errno(jenv, -EINVAL);
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
ret = -ENOMEM;
else
ret = stoken_check_devid(ctx->instance, arg0);
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return !translate_errno(jenv, ret);
}
JNIEXPORT jobject JNICALL Java_org_stoken_LibStoken_getGUIDList(
JNIEnv *jenv, jobject jobj)
{
struct libctx *ctx = getctx(jenv, jobj);
jmethodID mid;
jclass jcls;
const struct stoken_guid *guidlist = stoken_get_guid_list();
int i, len;
jobjectArray jarr;
for (len = 0; guidlist[len].tag != NULL; len++)
;
jcls = (*ctx->jenv)->FindClass(ctx->jenv,
"org/stoken/LibStoken$StokenGUID");
if (jcls == NULL)
return NULL;
mid = (*ctx->jenv)->GetMethodID(ctx->jenv, jcls, "<init>", "()V");
if (!mid)
return NULL;
jarr = (*ctx->jenv)->NewObjectArray(ctx->jenv, len, jcls, NULL);
if (!jarr)
return NULL;
for (i = 0; i < len; i++) {
const struct stoken_guid *g = &guidlist[i];
jobj = (*ctx->jenv)->NewObject(ctx->jenv, jcls, mid);
if (!jobj)
return NULL;
if (set_string(ctx, jobj, "tag", g->tag) ||
set_string(ctx, jobj, "longName", g->long_name) ||
set_string(ctx, jobj, "GUID", g->guid))
return NULL;
(*ctx->jenv)->SetObjectArrayElement(ctx->jenv, jarr, i, jobj);
}
return jarr;
}
JNIEXPORT jint JNICALL Java_org_stoken_LibStoken_decryptSeed(
JNIEnv *jenv, jobject jobj, jstring jarg0, jstring jarg1)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0 = NULL, *arg1 = NULL;
int ret = -ENOMEM;
if (jarg0) {
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
goto out;
}
if (jarg1) {
arg1 = (*jenv)->GetStringUTFChars(jenv, jarg1, NULL);
if (!arg1)
goto out;
}
ret = stoken_decrypt_seed(ctx->instance, arg0, arg1);
out:
if (arg1)
(*jenv)->ReleaseStringUTFChars(jenv, jarg1, arg1);
if (arg0)
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return translate_errno(jenv, ret);
}
JNIEXPORT jstring JNICALL Java_org_stoken_LibStoken_encryptSeed(
JNIEnv *jenv, jobject jobj, jstring jarg0, jstring jarg1)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *arg0 = NULL, *arg1 = NULL;
char *ret;
jstring jret = NULL;
if (jarg0) {
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
goto out;
}
if (jarg1) {
arg1 = (*jenv)->GetStringUTFChars(jenv, jarg1, NULL);
if (!arg1)
goto out;
}
ret = stoken_encrypt_seed(ctx->instance, arg0, arg1);
jret = ret ? (*jenv)->NewStringUTF(jenv, ret) : NULL;
free(ret);
out:
if (arg1)
(*jenv)->ReleaseStringUTFChars(jenv, jarg1, arg1);
if (arg0)
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return jret;
}
JNIEXPORT jstring JNICALL Java_org_stoken_LibStoken_computeTokencode(
JNIEnv *jenv, jobject jobj, jlong jwhen, jstring jpin)
{
struct libctx *ctx = getctx(jenv, jobj);
const char *pin = NULL;
time_t when = jwhen ? jwhen : time(NULL);
char tokencode[STOKEN_MAX_TOKENCODE + 1];
jstring ret = NULL;
if (jpin) {
pin = (*jenv)->GetStringUTFChars(jenv, jpin, NULL);
if (!pin) {
OOM(jenv);
return NULL;
}
}
if (stoken_compute_tokencode(ctx->instance, when, pin, tokencode) == 0)
ret = (*jenv)->NewStringUTF(jenv, tokencode);
if (jpin)
(*jenv)->ReleaseStringUTFChars(jenv, jpin, pin);
return ret;
}
JNIEXPORT jstring JNICALL Java_org_stoken_LibStoken_formatTokencode(
JNIEnv *jenv, jobject jobj, jstring jarg0)
{
const char *arg0;
char *ret;
jstring jret = NULL;
if (!jarg0)
return NULL;
arg0 = (*jenv)->GetStringUTFChars(jenv, jarg0, NULL);
if (!arg0)
return NULL;
ret = stoken_format_tokencode(arg0);
jret = (*jenv)->NewStringUTF(jenv, ret);
free(ret);
(*jenv)->ReleaseStringUTFChars(jenv, jarg0, arg0);
return jret;
}

503
src/library.c Normal file
View File

@ -0,0 +1,503 @@
/*
* library.c - libstoken library implementation
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "securid.h"
#include "sdtid.h"
#include "stoken-internal.h"
struct stoken_ctx {
struct securid_token *t;
struct stoken_cfg cfg;
};
static struct stoken_guid stoken_guid_list[] = {
{ "iphone", "iPhone", "556f1985-33dd-442c-9155-3a0e994f21b1" },
{ "android", "Android", "a01c4380-fc01-4df0-b113-7fb98ec74694" },
{ "bb", "BlackBerry", "868c28f8-31bf-4911-9876-ebece5c3f2ab" },
{ "bb10", "BlackBerry 10", "b77a1d06-d505-4200-90d3-1bb397748704" },
{ "winphone", "Windows Phone", "c483b592-63f0-4f19-b4cb-a6bce8e57159" },
{ "win", "Windows", "8f94b226-d362-4204-ac52-3b21fa333b6f" },
{ "mac", "Mac OSX", "d0955a53-569b-4ecc-9cf7-6c2a59d4e775" },
{ },
};
/***********************************************************************
* Internal functions (only called from within the stoken package)
***********************************************************************/
static int strstarts(const char *str, const char *prefix)
{
return strncmp(str, prefix, strlen(prefix)) == 0;
}
int __stoken_parse_and_decode_token(const char *str, struct securid_token *t,
int interactive)
{
char buf[BUFLEN];
const char *p;
int i, ret;
memset(t, 0, sizeof(*t));
t->interactive = interactive;
do {
/* try to handle broken quoted-printable input */
p = strcasestr(str, "ctfData=3D");
if (p) {
p += 10;
break;
}
/* normal iPhone/Android soft token URLs */
p = strcasestr(str, "ctfData=");
if (p) {
p += 8;
break;
}
/* sdtid (XML) token format */
p = strcasestr(str, "<?xml ");
if (p)
return sdtid_decode(p, t);
p = str;
if (isdigit(*p))
break;
/* bogus token string */
return ERR_GENERAL;
} while (0);
if (p[0] == '1' || p[0] == '2') {
for (i = 0; *p; p++) {
if (i >= BUFLEN - 1)
return ERR_BAD_LEN;
if (isdigit(*p))
buf[i++] = *p;
else if (*p != '-')
break;
}
} else if (p[0] == 'A') {
for (i = 0; *p; p++) {
if (i >= BUFLEN - 1)
return ERR_BAD_LEN;
buf[i++] = *p;
}
} else
return ERR_GENERAL;
buf[i] = 0;
ret = securid_decode_token(buf, t);
if (strstarts(str, "com.rsa.securid.iphone://ctf") ||
strstarts(str, "com.rsa.securid://ctf") ||
strstarts(str, "http://127.0.0.1/securid/ctf"))
t->is_smartphone = 1;
return ret;
}
static int next_token(char **in, char *tok, int maxlen)
{
int len;
for (len = 0; len < BUFLEN - 1; (*in)++) {
if (**in == 0 || **in == '\r' || **in == '\n') {
if (len == 0)
return -1;
goto done;
}
if (**in == ' ' || **in == '\t') {
if (len != 0)
goto done;
continue;
}
*(tok++) = **in;
len++;
}
/* if the loop terminates here, truncate the line and return success */
done:
*tok = 0;
return 0;
}
static int parse_rcline(struct stoken_cfg *cfg, int linenum, char *line,
warn_fn_t warn_fn)
{
char *p = line, key[BUFLEN], val[BUFLEN], **dst;
if (next_token(&p, key, BUFLEN) < 0)
return ERR_NONE; /* empty line */
if (key[0] == '#')
return ERR_NONE; /* comment */
if (next_token(&p, val, BUFLEN) < 0) {
warn_fn("rcfile:%d: missing argument for '%s'\n", linenum, key);
return ERR_GENERAL;
}
dst = NULL;
if (strcasecmp(key, "version") == 0)
dst = &cfg->rc_ver;
else if (strcasecmp(key, "token") == 0)
dst = &cfg->rc_token;
else if (strcasecmp(key, "pin") == 0)
dst = &cfg->rc_pin;
if (!dst) {
/* this isn't treated as a fatal error */
warn_fn("rcfile:%d: unrecognized option '%s'\n", linenum, key);
return ERR_NONE;
}
free(*dst);
*dst = strdup(val);
if (!*dst) {
warn_fn("rcfile:%d: out of memory\n", linenum);
return ERR_GENERAL;
}
return ERR_NONE;
}
static int fopen_rcfile(const char *override, const char *mode,
warn_fn_t warn_fn, FILE **f)
{
char *homedir;
const char *file = override;
char filename[BUFLEN];
mode_t old_umask;
if (!override) {
homedir = getenv("HOME");
if (!homedir) {
homedir = getenv("USERPROFILE");
}
if (!homedir) {
warn_fn("rcfile: HOME is not set so I can't read '%s'\n",
RC_NAME);
return ERR_GENERAL;
}
snprintf(filename, BUFLEN, "%s/%s", homedir, RC_NAME);
file = filename;
}
/* force mode 0600 on creation */
old_umask = umask(0177);
*f = fopen(file, mode);
umask(old_umask);
if (!*f && override)
warn_fn("rcfile: can't open '%s'\n", override);
return *f ? ERR_NONE : ERR_GENERAL;
}
void __stoken_zap_rcfile_data(struct stoken_cfg *cfg)
{
free(cfg->rc_ver);
free(cfg->rc_token);
free(cfg->rc_pin);
memset(cfg, 0, sizeof(*cfg));
}
int __stoken_read_rcfile(const char *override, struct stoken_cfg *cfg,
warn_fn_t warn_fn)
{
FILE *f;
char buf[BUFLEN];
int linenum = 1, ret;
__stoken_zap_rcfile_data(cfg);
/* XXX: kind of dumb return code here, but it gets the job done */
ret = fopen_rcfile(override, "r", warn_fn, &f);
if (ret != ERR_NONE)
return ERR_MISSING_PASSWORD;
while (fgets(buf, BUFLEN, f) != NULL) {
int ret2 = parse_rcline(cfg, linenum++, buf, warn_fn);
if (ret2 != ERR_NONE)
ret = ret2;
}
if (ferror(f)) {
ret = ERR_GENERAL;
warn_fn("rcfile: read error(s) were detected\n");
}
fclose(f);
if (ret == ERR_NONE) {
if (!cfg->rc_ver || !cfg->rc_token)
return ERR_GENERAL;
if (atoi(cfg->rc_ver) != RC_VER) {
warn_fn("rcfile: version mismatch, ignoring contents\n");
return ERR_TOKEN_VERSION;
}
}
return ret;
}
int __stoken_write_rcfile(const char *override, const struct stoken_cfg *cfg,
warn_fn_t warn_fn)
{
FILE *f;
int ret;
ret = fopen_rcfile(override, "w", warn_fn, &f);
if (ret != ERR_NONE)
return ret;
if (cfg->rc_ver)
fprintf(f, "version %s\n", cfg->rc_ver);
if (cfg->rc_token)
fprintf(f, "token %s\n", cfg->rc_token);
if (cfg->rc_pin)
fprintf(f, "pin %s\n", cfg->rc_pin);
if (ferror(f))
ret = ERR_GENERAL;
fclose(f);
return ret;
}
static void zap_current_token(struct stoken_ctx *ctx)
{
if (ctx->t) {
free(ctx->t->v3);
sdtid_free(ctx->t->sdtid);
free(ctx->t);
}
ctx->t = NULL;
}
static int clone_token(struct stoken_ctx *ctx, struct securid_token *tmp)
{
ctx->t = malloc(sizeof(*tmp));
if (!ctx->t)
return -EIO;
memcpy(ctx->t, tmp, sizeof(*tmp));
return 0;
}
/***********************************************************************
* Exported functions
***********************************************************************/
struct stoken_ctx *stoken_new(void)
{
struct stoken_ctx *ctx;
ctx = calloc(1, sizeof(*ctx));
if (!ctx)
return NULL;
return ctx;
}
void stoken_destroy(struct stoken_ctx *ctx)
{
zap_current_token(ctx);
__stoken_zap_rcfile_data(&ctx->cfg);
free(ctx);
}
int stoken_import_rcfile(struct stoken_ctx *ctx, const char *path)
{
struct securid_token tmp;
int rc;
zap_current_token(ctx);
rc = __stoken_read_rcfile(path, &ctx->cfg, &__stoken_warn_empty);
if (rc == ERR_MISSING_PASSWORD)
return -ENOENT;
else if (rc != ERR_NONE)
goto bad;
if (__stoken_parse_and_decode_token(ctx->cfg.rc_token, &tmp, 0) !=
ERR_NONE)
goto bad;
if (ctx->cfg.rc_pin) {
if (tmp.flags & FL_PASSPROT)
tmp.enc_pin_str = ctx->cfg.rc_pin;
else {
if (securid_pin_format_ok(ctx->cfg.rc_pin) == ERR_NONE)
strncpy(tmp.pin, ctx->cfg.rc_pin,
MAX_PIN + 1);
else
goto bad;
}
}
return clone_token(ctx, &tmp);
bad:
__stoken_zap_rcfile_data(&ctx->cfg);
return -EINVAL;
}
int stoken_import_string(struct stoken_ctx *ctx, const char *token_string)
{
struct securid_token tmp;
zap_current_token(ctx);
if (__stoken_parse_and_decode_token(token_string, &tmp, 0) != ERR_NONE)
return -EINVAL;
return clone_token(ctx, &tmp);
}
struct stoken_info *stoken_get_info(struct stoken_ctx *ctx)
{
struct stoken_info *info = calloc(1, sizeof(*info));
if (!info)
return NULL;
strncpy(info->serial, ctx->t->serial, sizeof(info->serial) - 1);
info->exp_date = securid_unix_exp_date(ctx->t);
info->interval = securid_token_interval(ctx->t);
info->token_version = ctx->t->version;
info->uses_pin = securid_pin_required(ctx->t);
return info;
}
void stoken_pin_range(struct stoken_ctx *ctx, int *min_pin, int *max_pin)
{
*min_pin = MIN_PIN;
*max_pin = MAX_PIN;
}
int stoken_pin_required(struct stoken_ctx *ctx)
{
/* don't prompt for a PIN if it was saved in the rcfile */
if (ctx->t->enc_pin_str || strlen(ctx->t->pin))
return 0;
return securid_pin_required(ctx->t);
}
int stoken_pass_required(struct stoken_ctx *ctx)
{
return securid_pass_required(ctx->t);
}
int stoken_devid_required(struct stoken_ctx *ctx)
{
return securid_devid_required(ctx->t);
}
int stoken_check_pin(struct stoken_ctx *ctx, const char *pin)
{
return securid_pin_format_ok(pin) == ERR_NONE ? 0 : -EINVAL;
}
const struct stoken_guid *stoken_get_guid_list(void)
{
return stoken_guid_list;
}
int stoken_check_devid(struct stoken_ctx *ctx, const char *devid)
{
if (securid_check_devid(ctx->t, devid) == ERR_NONE)
return 0;
return -EINVAL;
}
int stoken_decrypt_seed(struct stoken_ctx *ctx, const char *pass,
const char *devid)
{
if (securid_decrypt_seed(ctx->t, pass, devid) != ERR_NONE)
return -EINVAL;
if (ctx->t->enc_pin_str) {
if (securid_decrypt_pin(ctx->t->enc_pin_str, pass,
ctx->t->pin) != ERR_NONE)
return -EINVAL;
}
return 0;
}
char *stoken_encrypt_seed(struct stoken_ctx *ctx, const char *pass,
const char *devid)
{
char *ret;
if (!ctx->t || !ctx->t->has_dec_seed)
return NULL;
ret = calloc(1, MAX_TOKEN_CHARS + 1);
if (!ret)
return NULL;
if (securid_encode_token(ctx->t, pass, devid, 2, ret) != ERR_NONE) {
free(ret);
return NULL;
}
return ret;
}
int stoken_compute_tokencode(struct stoken_ctx *ctx, time_t when,
const char *pin, char *out)
{
if (securid_pin_required(ctx->t)) {
if (pin && strlen(pin)) {
if (securid_pin_format_ok(pin) != ERR_NONE)
return -EINVAL;
strncpy(ctx->t->pin, pin, MAX_PIN + 1);
} else if (stoken_pin_required(ctx)) {
return -EINVAL;
}
}
securid_compute_tokencode(ctx->t, when, out);
return 0;
}
char *stoken_format_tokencode(const char *tokencode)
{
int code_len = strlen(tokencode);
char *str = malloc(code_len + 2);
int i, j;
if (!str)
return NULL;
for (i = 0, j = 0; i < code_len; i++) {
if (i == code_len / 2)
str[j++] = ' ';
str[j++] = tokencode[i];
}
str[j] = 0;
return str;
}

1349
src/sdtid.c Normal file

File diff suppressed because it is too large Load Diff

37
src/sdtid.h Normal file
View File

@ -0,0 +1,37 @@
/*
* sdtid.h - SecurID sdtid/xml internal interfaces
*
* Copyright 2014 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __STOKEN_SDTID_H__
#define __STOKEN_SDTID_H__
#include "stoken-internal.h"
struct securid_token;
struct sdtid;
STOKEN_EXPORT int sdtid_decode(const char *in, struct securid_token *t);
STOKEN_EXPORT int sdtid_decrypt(struct securid_token *t, const char *pass);
STOKEN_EXPORT int sdtid_issue(const char *filename, const char *pass,
const char *devid);
STOKEN_EXPORT int sdtid_export(const char *filename, struct securid_token *t,
const char *pass, const char *devid);
STOKEN_EXPORT void sdtid_free(struct sdtid *s);
#endif /* __STOKEN_SDTID_H__ */

1065
src/securid.c Normal file

File diff suppressed because it is too large Load Diff

180
src/securid.h Normal file
View File

@ -0,0 +1,180 @@
/*
* securid.h - SecurID-related definitions
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __STOKEN_SECURID_H__
#define __STOKEN_SECURID_H__
#include "config.h"
#include <stdint.h>
#include <time.h>
#include "stoken-internal.h"
#define AES_BLOCK_SIZE 16
#define AES_KEY_SIZE 16
#define SHA256_BLOCK_SIZE 64
#define SHA256_HASH_SIZE 32
#define MIN_PIN 4
#define MAX_PIN 8
#define MAX_PASS 40
#define MAGIC_LEN 6
#define VER_CHARS 1
#define SERIAL_CHARS 12
#define CHECKSUM_BITS 15
#define CHECKSUM_CHARS (CHECKSUM_BITS / TOKEN_BITS_PER_CHAR)
#define BINENC_BITS 189
#define BINENC_CHARS (BINENC_BITS / TOKEN_BITS_PER_CHAR)
#define BINENC_OFS (VER_CHARS + SERIAL_CHARS)
#define CHECKSUM_OFS (BINENC_OFS + BINENC_CHARS)
#define DEVID_CHARS 40
#define V3_DEVID_CHARS 48
#define V3_NONCE_BYTES 16
#define TOKEN_BITS_PER_CHAR 3
#define MIN_TOKEN_BITS 189
#define MAX_TOKEN_BITS 255
#define MAX_TOKEN_CHARS (MAX_TOKEN_BITS / TOKEN_BITS_PER_CHAR)
#define MIN_TOKEN_CHARS ((MIN_TOKEN_BITS / TOKEN_BITS_PER_CHAR) + \
SERIAL_CHARS + VER_CHARS + CHECKSUM_CHARS)
/*
* This matches src/misc/base64/base64_encode.c in tomcrypt.
* base64 produces 4 output characters (each carrying 6 bits of data) for
* up to 3 input bytes (each carrying 8 bits of data). Plus one terminating
* NUL at the end.
*/
#define BASE64_INPUT_LEN(x) ((4 * ((x) + 2) / 3) + 1)
/* decoded size (binary) */
#define V3_BASE64_BYTES 0x123
/* worst case, including terminating NUL */
#define V3_BASE64_SIZE (BASE64_INPUT_LEN(V3_BASE64_BYTES))
/* '+' and '/' expand to "%2B" and "%2F", so worst case... */
#define V3_BASE64_URL_SIZE (3*BASE64_INPUT_LEN(V3_BASE64_BYTES))
/* strlen() of smallest possible encoded size */
#define V3_BASE64_MIN_CHARS (V3_BASE64_BYTES * 4 / 3)
#define BIT(x) (1 << (x))
#define FL_128BIT BIT(14)
#define FL_PASSPROT BIT(13)
#define FL_SNPROT BIT(12)
#define FL_APPSEEDS BIT(11)
#define FL_FEAT4 BIT(10)
#define FL_TIMESEEDS BIT(9)
#define FLD_DIGIT_SHIFT 6
#define FLD_DIGIT_MASK (0x07 << FLD_DIGIT_SHIFT)
#define FL_FEAT6 BIT(5)
#define FLD_PINMODE_SHIFT 3
#define FLD_PINMODE_MASK (0x03 << FLD_PINMODE_SHIFT)
#define FLD_NUMSECONDS_SHIFT 0
#define FLD_NUMSECONDS_MASK (0x03 << FLD_NUMSECONDS_SHIFT)
/* UNIX time_t for 2000/01/01 00:00:00 GMT */
#define SECURID_EPOCH 946684800
#define SECURID_EPOCH_DAYS (SECURID_EPOCH / (24*60*60))
/* V3 tokens use 1970/01/01 as the epoch, but each day has 337500 ticks */
#define SECURID_V3_DAY 337500
/* Avoid 32-bit time_t overflows (January 2038) */
#define MAX_TIME_T 0x7fffffff
#define SECURID_MAX_SECS (MAX_TIME_T - SECURID_EPOCH)
#define SECURID_MAX_DATE (SECURID_MAX_SECS / (24*60*60) - 1)
struct sdtid;
struct v3_token;
struct securid_token {
int version;
char serial[SERIAL_CHARS + 1];
uint16_t flags;
uint16_t exp_date;
int is_smartphone;
int has_enc_seed;
uint8_t enc_seed[AES_KEY_SIZE];
uint16_t dec_seed_hash;
uint16_t device_id_hash;
int has_dec_seed;
uint8_t dec_seed[AES_KEY_SIZE];
int pinmode;
char pin[MAX_PIN + 1];
char *enc_pin_str;
struct sdtid *sdtid;
int interactive;
struct v3_token *v3;
};
STOKEN_EXPORT int securid_decode_token(const char *in,
struct securid_token *t);
STOKEN_EXPORT int securid_decrypt_seed(struct securid_token *t,
const char *pass,
const char *devid);
STOKEN_EXPORT int securid_check_devid(struct securid_token *t,
const char *devid);
STOKEN_EXPORT void securid_compute_tokencode(struct securid_token *t,
time_t now,
char *code_out);
STOKEN_EXPORT void securid_token_info(const struct securid_token *t,
void (*callback)(const char *key, const char *value));
STOKEN_EXPORT int securid_encode_token(const struct securid_token *t,
const char *pass,
const char *devid,
int version,
char *out);
STOKEN_EXPORT int securid_random_token(struct securid_token *t);
STOKEN_EXPORT int securid_check_exp(struct securid_token *t, time_t now);
STOKEN_EXPORT time_t securid_unix_exp_date(const struct securid_token *t);
STOKEN_EXPORT int securid_token_interval(const struct securid_token *t);
STOKEN_EXPORT char *securid_encrypt_pin(const char *pin, const char *password);
STOKEN_EXPORT int securid_decrypt_pin(const char *enc_pin,
const char *password,
char *pin);
STOKEN_EXPORT int securid_pin_format_ok(const char *pin);
STOKEN_EXPORT int securid_pin_required(const struct securid_token *t);
STOKEN_EXPORT int securid_pass_required(const struct securid_token *t);
STOKEN_EXPORT int securid_devid_required(const struct securid_token *t);
STOKEN_EXPORT int securid_rand(void *out, int len, int paranoid);
#endif /* !__STOKEN_SECURID_H__ */

196
src/stc-nettle.c Normal file
View File

@ -0,0 +1,196 @@
/*
* stc-nettle.c - stoken crypto wrappers for nettle
*
* Copyright 2014 Nikos Mavrogiannopoulos <nmav@redhat.com>
* Copyright 2014 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <nettle/aes.h>
#include <nettle/base64.h>
#include <nettle/bignum.h>
#include <nettle/cbc.h>
#include <nettle/hmac.h>
#include <nettle/rsa.h>
#include <nettle/sha.h>
#include "stoken-internal.h"
int stc_standalone_init(void)
{
return ERR_NONE;
}
void stc_aes128_ecb_encrypt(const uint8_t *key, const uint8_t *in, uint8_t *out)
{
struct aes_ctx ctx;
aes_set_encrypt_key(&ctx, 128/8, key);
aes_encrypt(&ctx, AES_BLOCK_SIZE, out, in);
}
void stc_aes128_ecb_decrypt(const uint8_t *key, const uint8_t *in, uint8_t *out)
{
struct aes_ctx ctx;
aes_set_decrypt_key(&ctx, 128/8, key);
aes_decrypt(&ctx, AES_BLOCK_SIZE, out, in);
}
void stc_aes256_cbc_decrypt(const uint8_t *key, const uint8_t *in, int in_len,
const uint8_t *iv, uint8_t *out)
{
struct CBC_CTX(struct aes_ctx, AES_BLOCK_SIZE) ctx;
aes_set_decrypt_key(&ctx.ctx, 256/8, key);
CBC_SET_IV(&ctx, iv);
CBC_DECRYPT(&ctx, aes_decrypt, in_len, out, in);
}
void stc_aes256_cbc_encrypt(const uint8_t *key, const uint8_t *in, int in_len,
const uint8_t *iv, uint8_t *out)
{
struct CBC_CTX(struct aes_ctx, AES_BLOCK_SIZE) ctx;
aes_set_encrypt_key(&ctx.ctx, 256/8, key);
CBC_SET_IV(&ctx, iv);
CBC_ENCRYPT(&ctx, aes_encrypt, in_len, out, in);
}
void stc_sha1_hash(uint8_t *out, ...)
{
va_list ap;
struct sha1_ctx md;
sha1_init(&md);
va_start(ap, out);
while (1) {
const uint8_t *in = va_arg(ap, const uint8_t *);
int in_len;
if (!in)
break;
in_len = va_arg(ap, int);
sha1_update(&md, in_len, in);
}
va_end(ap);
sha1_digest(&md, SHA1_DIGEST_SIZE, out);
}
void stc_sha256_hash(uint8_t *out, ...)
{
va_list ap;
struct sha256_ctx md;
sha256_init(&md);
va_start(ap, out);
while (1) {
const uint8_t *in = va_arg(ap, const uint8_t *);
int in_len;
if (!in)
break;
in_len = va_arg(ap, int);
sha256_update(&md, in_len, in);
}
va_end(ap);
sha256_digest(&md, SHA256_DIGEST_SIZE, out);
}
int stc_b64_encode(const uint8_t *in, unsigned long len,
uint8_t *out, unsigned long *outlen)
{
struct base64_encode_ctx ctx;
unsigned size = 0;
base64_encode_init(&ctx);
size = base64_encode_update(&ctx, out, len, in);
size += base64_encode_final(&ctx, out+size);
out[size] = 0;
*outlen = size;
return ERR_NONE;
}
int stc_b64_decode(const uint8_t *in, unsigned long len,
uint8_t *out, unsigned long *outlen)
{
struct base64_decode_ctx ctx;
char tmp[BASE64_DECODE_LENGTH(len)];
int ret;
#ifdef NETTLE_OLD_BASE64_API
unsigned dst_length = sizeof(tmp);
#else
size_t dst_length;
#endif
base64_decode_init(&ctx);
ret = base64_decode_update(&ctx, &dst_length, tmp, len, in);
if (ret == 0) {
return ERR_GENERAL;
}
if (*outlen >= dst_length) {
memcpy(out, tmp, dst_length);
} else {
return ERR_GENERAL;
}
*outlen = dst_length;
return ERR_NONE;
}
int stc_rsa_sha1_sign_digest(const uint8_t *privkey_der, size_t privkey_len,
const uint8_t *digest,
uint8_t *out, unsigned long *outlen)
{
struct rsa_private_key key;
struct rsa_public_key pub;
mpz_t msig;
int ret;
rsa_private_key_init(&key);
rsa_public_key_init(&pub);
mpz_init(msig);
ret = rsa_keypair_from_der(&pub, &key, 1025,
privkey_len - 1, privkey_der);
if (ret == 0) {
ret = ERR_GENERAL;
goto cleanup;
}
ret = rsa_sha1_sign_digest(&key, digest, msig);
if (ret == 0) {
ret = ERR_GENERAL;
goto cleanup;
}
nettle_mpz_get_str_256(nettle_mpz_sizeinbase_256_u(msig), out, msig);
ret = ERR_NONE;
cleanup:
rsa_private_key_clear(&key);
rsa_public_key_clear(&pub);
mpz_clear(msig);
return ret;
}

204
src/stc-tomcrypt.c Normal file
View File

@ -0,0 +1,204 @@
/*
* stc-tomcrypt.c - stoken crypto wrappers for libtomcrypt
*
* Copyright 2014 Nikos Mavrogiannopoulos <nmav@redhat.com>
* Copyright 2014 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <tomcrypt.h>
#include <unistd.h>
#include "stoken-internal.h"
/* These are redundant, but stc-* files shouldn't include securid.h */
#define AES_BLOCK_SIZE 16
#define AES_KEY_SIZE 16
#define AES256_KEY_SIZE 32
/* Backwards compatibility support for pre-1.18 versions of libtomcrypt */
#ifdef LIBTOMCRYPT_OLD_PKCS_NAMES
#define LTC_PKCS_1_V1_5 LTC_LTC_PKCS_1_V1_5
#endif
int stc_standalone_init(void)
{
/* libtomcrypt init for sdtid BatchSignature generation */
ltc_mp = ltm_desc;
if (register_hash(&sha1_desc) == -1)
return ERR_GENERAL;
return ERR_NONE;
}
void stc_aes128_ecb_encrypt(const uint8_t *key, const uint8_t *in, uint8_t *out)
{
symmetric_key skey;
uint8_t tmp[AES_BLOCK_SIZE];
/* these shouldn't allocate memory or fail */
if (rijndael_setup(key, AES_KEY_SIZE, 0, &skey) != CRYPT_OK ||
rijndael_ecb_encrypt(in, tmp, &skey) != CRYPT_OK)
abort();
rijndael_done(&skey);
/* in case "in" and "out" point to the same buffer */
memcpy(out, tmp, AES_BLOCK_SIZE);
}
void stc_aes128_ecb_decrypt(const uint8_t *key, const uint8_t *in, uint8_t *out)
{
symmetric_key skey;
uint8_t tmp[AES_BLOCK_SIZE];
if (rijndael_setup(key, AES_KEY_SIZE, 0, &skey) != CRYPT_OK ||
rijndael_ecb_decrypt(in, tmp, &skey) != CRYPT_OK)
abort();
rijndael_done(&skey);
memcpy(out, tmp, AES_BLOCK_SIZE);
}
void stc_aes256_cbc_decrypt(const uint8_t *key, const uint8_t *in, int in_len,
const uint8_t *iv, uint8_t *out)
{
symmetric_key skey;
int i, j;
uint8_t local_iv[AES_BLOCK_SIZE];
rijndael_setup(key, AES256_KEY_SIZE, 0, &skey);
memcpy(local_iv, iv, AES_BLOCK_SIZE);
for (i = 0; i < in_len; i += AES_BLOCK_SIZE) {
rijndael_ecb_decrypt(in, out, &skey);
for (j = 0; j < AES_BLOCK_SIZE; j++)
out[j] ^= local_iv[j];
memcpy(local_iv, in, AES_BLOCK_SIZE);
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
rijndael_done(&skey);
}
void stc_aes256_cbc_encrypt(const uint8_t *key, const uint8_t *in, int in_len,
const uint8_t *iv, uint8_t *out)
{
symmetric_key skey;
int i, j;
uint8_t xored_in[AES_BLOCK_SIZE];
rijndael_setup(key, AES256_KEY_SIZE, 0, &skey);
for (i = 0; i < in_len; i += AES_BLOCK_SIZE) {
for (j = 0; j < AES_BLOCK_SIZE; j++) {
xored_in[j] = in[j] ^
(i ? out[j - AES_BLOCK_SIZE] : iv[j]);
}
rijndael_ecb_encrypt(xored_in, out, &skey);
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
rijndael_done(&skey);
}
void stc_sha1_hash(uint8_t *out, ...)
{
va_list ap;
hash_state md;
sha1_init(&md);
va_start(ap, out);
while (1) {
const uint8_t *in = va_arg(ap, const uint8_t *);
int in_len;
if (!in)
break;
in_len = va_arg(ap, int);
sha1_process(&md, in, in_len);
}
va_end(ap);
sha1_done(&md, out);
}
void stc_sha256_hash(uint8_t *out, ...)
{
va_list ap;
hash_state md;
sha256_init(&md);
va_start(ap, out);
while (1) {
const uint8_t *in = va_arg(ap, const uint8_t *);
int in_len;
if (!in)
break;
in_len = va_arg(ap, int);
sha256_process(&md, in, in_len);
}
va_end(ap);
sha256_done(&md, out);
}
int stc_b64_encode(const uint8_t *in, unsigned long len,
uint8_t *out, unsigned long *outlen)
{
return base64_encode(in, len, out, outlen) == CRYPT_OK ?
ERR_NONE : ERR_GENERAL;
}
int stc_b64_decode(const uint8_t *in, unsigned long len,
uint8_t *out, unsigned long *outlen)
{
return base64_decode(in, len, out, outlen) == CRYPT_OK ?
ERR_NONE : ERR_GENERAL;
}
int stc_rsa_sha1_sign_digest(const uint8_t *privkey_der, size_t privkey_len,
const uint8_t *digest,
uint8_t *out, unsigned long *outlen)
{
int hash_idx, rc = ERR_NONE;
rsa_key key;
/*
* NOTE: This is set up in common.c. If we ever decide to let library
* callers generate sdtid files, we will have to figure out how to
* call register_sha1() and set ltc_mp without disturbing other
* libtomcrypt users who might coexist in the same process.
*/
hash_idx = find_hash("sha1");
if (hash_idx < 0)
return ERR_GENERAL;
if (rsa_import(privkey_der, privkey_len, &key) != CRYPT_OK)
return ERR_GENERAL;
if (rsa_sign_hash_ex(digest, (160 / 8), out, outlen,
LTC_PKCS_1_V1_5, NULL, 0,
hash_idx, 0, &key) != CRYPT_OK)
rc = ERR_GENERAL;
rsa_free(&key);
return rc;
}

114
src/stoken-internal.h Normal file
View File

@ -0,0 +1,114 @@
/*
* stoken-internal.h - internal functions called within the stoken package
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __STOKEN_INTERNAL_H__
#define __STOKEN_INTERNAL_H__
#include <stdint.h>
#include "stoken.h"
#define BUFLEN 2048
#define RC_NAME ".stokenrc"
#define RC_VER 1
struct stoken_cfg {
char *rc_ver;
char *rc_token;
char *rc_pin;
};
struct securid_token;
/* keep this in sync with stoken_errstr */
enum {
ERR_NONE = 0,
ERR_GENERAL,
ERR_BAD_LEN,
ERR_TOKEN_VERSION,
ERR_CHECKSUM_FAILED,
ERR_BAD_PASSWORD,
ERR_MISSING_PASSWORD,
ERR_DECRYPT_FAILED,
ERR_BAD_DEVID,
ERR_NO_MEMORY,
ERR_FILE_READ,
ERR_MULTIPLE_TOKENS,
};
typedef void (warn_fn_t)(const char *, ...);
static inline void __stoken_warn_empty(const char *fmt, ...) { }
STOKEN_EXPORT int __stoken_parse_and_decode_token(const char *str,
struct securid_token *t,
int interactive);
STOKEN_EXPORT int __stoken_read_rcfile(const char *override,
struct stoken_cfg *cfg,
warn_fn_t warn_fn);
STOKEN_EXPORT int __stoken_write_rcfile(const char *override,
const struct stoken_cfg *cfg,
warn_fn_t warn_fn);
STOKEN_EXPORT void __stoken_zap_rcfile_data(struct stoken_cfg *cfg);
#ifdef __ANDROID__
/* Sigh. This exists but it isn't in the Bionic headers. */
int mkstemps(char *path, int slen);
#elif !defined(HAVE_MKSTEMPS)
#define mkstemps stoken__mkstemps
STOKEN_EXPORT int stoken__mkstemps(char *path, int slen);
#endif
#ifndef HAVE_STRCASESTR
#define strcasestr stoken__strcasestr
STOKEN_EXPORT char *stoken__strcasestr(const char *haystack,
const char *needle);
#endif
#ifndef HAVE_GMTIME_R
#define gmtime_r stoken__gmtime_r
struct tm *stoken__gmtime_r(const time_t *timep, struct tm *result);
#endif
#ifndef HAVE_TIMEGM
#define timegm stoken__timegm
time_t stoken__timegm(struct tm *tm);
#endif
/* crypto wrappers */
STOKEN_EXPORT int stc_standalone_init(void);
void stc_aes128_ecb_decrypt(const uint8_t *key, const uint8_t *in, uint8_t *out);
void stc_aes128_ecb_encrypt(const uint8_t *key, const uint8_t *in, uint8_t *out);
void stc_aes256_cbc_decrypt(const uint8_t *key, const uint8_t *in, int in_len,
const uint8_t *iv, uint8_t *out);
void stc_aes256_cbc_encrypt(const uint8_t *key, const uint8_t *in, int in_len,
const uint8_t *iv, uint8_t *out);
void stc_sha1_hash(uint8_t *out, ...);
void stc_sha256_hash(uint8_t *out, ...);
int stc_b64_encode(const uint8_t *in, unsigned long len,
uint8_t *out, unsigned long *outlen);
int stc_b64_decode(const uint8_t *in, unsigned long len,
uint8_t *out, unsigned long *outlen);
int stc_rsa_sha1_sign_digest(const uint8_t *privkey_der, size_t privkey_len,
const uint8_t *digest,
uint8_t *out, unsigned long *outlen);
#endif /* !__STOKEN_INTERNAL_H__ */

248
src/stoken.h Normal file
View File

@ -0,0 +1,248 @@
/*
* stoken.h - public libstoken library interface
*
* Copyright 2012 Kevin Cernekee <cernekee@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __STOKEN_H__
#define __STOKEN_H__
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
#define STOKEN_API_VER_MAJOR 1
#define STOKEN_API_VER_MINOR 3
/* Before API version 1.3 (stoken 0.8) this macro didn't exist.
* Somewhat ironic, that the API version check itself needs to be
* conditionally used depending on the API version. A very simple way
* for users to handle this with an approximately correct answer is
* #include <stoken.h>
* #ifndef STOKEN_CHECK_VER
* #define STOKEN_CHECK_VER(x,y) 0
* #endif
*/
#define STOKEN_CHECK_VER(maj, min) \
(STOKEN_API_VER_MAJOR > (maj) || \
(STOKEN_API_VER_MAJOR == (maj) && \
STOKEN_API_VER_MINOR >= (min)))
#define STOKEN_MAX_TOKENCODE 8
#if defined(_WIN32) && defined(LIBSTOKEN_BUILD)
#define STOKEN_EXPORT __declspec(dllexport)
#elif defined(_WIN32)
#define STOKEN_EXPORT __declspec(dllimport)
#else
#define STOKEN_EXPORT
#endif
struct stoken_ctx;
struct stoken_info {
char serial[16];
time_t exp_date;
int interval;
int token_version;
int uses_pin;
};
struct stoken_guid {
const char *tag;
const char *long_name;
const char *guid;
};
/*
* Create/destroy library context.
* stoken_new() returns NULL on error.
*/
STOKEN_EXPORT struct stoken_ctx *stoken_new(void);
STOKEN_EXPORT void stoken_destroy(struct stoken_ctx *ctx);
/*
* Load a token from an existing .stokenrc file (PATH can be NULL to use
* $HOME/.stokenrc).
*
* Return values:
*
* 0: success; token is now stored in CTX
* -ENOENT: missing input file
* -EINVAL: invalid input file format
* -EIO: any other failure (e.g. ran out of memory)
*/
STOKEN_EXPORT int stoken_import_rcfile(struct stoken_ctx *ctx,
const char *path);
/*
* Parse a token string (nominally a string of ~71 digits, starting with
* '1' or '2').
*
* Return values:
*
* 0: success; token is now stored in CTX
* -EINVAL: invalid input string format
* -EIO: any other failure (e.g. ran out of memory)
*/
STOKEN_EXPORT int stoken_import_string(struct stoken_ctx *ctx,
const char *token_string);
/*
* Retrieve metadata for the currently imported token. This returns a
* callee-allocated, caller-freed struct, which may grow larger in the future.
*
* In general this should be called after stoken_decrypt_seed(), as
* most of the token metadata is encrypted with the devid and/or password on
* v3 tokens.
*
* Return values:
*
* ptr: success
* NULL: any failure (e.g. ran out of memory)
*/
STOKEN_EXPORT struct stoken_info *stoken_get_info(struct stoken_ctx *ctx);
/*
* Set *MIN_PIN and *MAX_PIN to reflect the valid range of PIN lengths
* (e.g. 4-8).
*/
STOKEN_EXPORT void stoken_pin_range(struct stoken_ctx *ctx,
int *min_pin,
int *max_pin);
/*
* Returns nonzero if the token in CTX requires a PIN, and doesn't have one
* saved (i.e. you need to prompt for it). stoken_info->uses_pin returns
* nonzero if a PIN is used in the calculation. If stoken_info->uses_pin is
* 0, a PIN is not needed to generate the tokencode but you may need to
* request and concatenate a PIN in order to log in to a protected resource:
* PASSCODE = PIN + TOKENCODE
*/
STOKEN_EXPORT int stoken_pin_required(struct stoken_ctx *ctx);
/* returns nonzero if the token in CTX needs a password to decrypt the seed */
STOKEN_EXPORT int stoken_pass_required(struct stoken_ctx *ctx);
/* returns nonzero if the token in CTX needs a device ID to decrypt the seed */
STOKEN_EXPORT int stoken_devid_required(struct stoken_ctx *ctx);
/*
* Check the PIN for proper format. This does not validate whether the PIN
* matches the PIN on file for the user's account; only the server knows that.
*
* Return values:
*
* 0: success
* -EINVAL: invalid format
*/
STOKEN_EXPORT int stoken_check_pin(struct stoken_ctx *ctx, const char *pin);
/*
* Obtain the list of known "class GUIDs" used to bind a token to a specific
* type of device (e.g. iPhone).
*/
STOKEN_EXPORT const struct stoken_guid *stoken_get_guid_list(void);
/*
* Check the device ID by performing a partial seed decrypt. This helps
* callers provide better user feedback after a stoken_decrypt_seed() failure.
*
* Return values:
*
* 0: success
* -EINVAL: DEVID MAC failed
* -EIO: any other failure (e.g. ran out of memory)
*/
STOKEN_EXPORT int stoken_check_devid(struct stoken_ctx *ctx,
const char *devid);
/*
* Try to decrypt the seed stored in CTX, and compare the MAC to see if
* decryption was successful.
*
* PASS may be NULL if stoken_needs_pass() == 0
* DEVID may be NULL if stoken_needs_devid() == 0
*
* Return values:
*
* 0: success
* -EINVAL: MAC failed (PASS or DEVID is probably incorrect)
* -EIO: any other failure (e.g. ran out of memory)
*/
STOKEN_EXPORT int stoken_decrypt_seed(struct stoken_ctx *ctx,
const char *pass,
const char *devid);
/*
* Generate a new token string for the previously-decrypted seed stored
* in CTX. PASS and DEVID may be NULL. The returned string must be freed
* by the caller.
*
* Return values:
*
* ptr: on success, a pointer to a new string
* NULL: on failure
*/
STOKEN_EXPORT char *stoken_encrypt_seed(struct stoken_ctx *ctx,
const char *pass,
const char *devid);
/*
* Generate a tokencode from the decrypted seed, for UNIX time WHEN.
* OUT is allocated by the caller, and must be able to store at least
* (STOKEN_MAX_TOKENCODE + 1) bytes.
*
* This can be called over and over again, as needed.
*
* If stoken_pin_required() returns 0, PIN may be NULL. If PIN is not
* NULL and the user stored a PIN in ~/.stokenrc, the PIN string passed
* into this function will override the stored PIN. This will affect
* subsequent calls to stoken_compute_tokencode() but the change will not
* be stored on disk.
*
* Return values:
*
* 0: success
* -EINVAL: invalid PIN format
* -EIO: general failure
*/
STOKEN_EXPORT int stoken_compute_tokencode(struct stoken_ctx *ctx,
time_t when,
const char *pin,
char *out);
/*
* Inject a space in the middle of the code, e.g. "1234 5678".
* Typical libstoken users would use the formatted tokencode for display
* purposes only, and use the unformatted tokencode for "copy to clipboard",
* pasting into login forms, etc.
*
* The returned string must be freed by the caller. Returns NULL on malloc
* failure.
*/
STOKEN_EXPORT char *stoken_format_tokencode(const char *tokencode);
#ifdef __cplusplus
}
#endif
#endif /* !__STOKEN_H__ */

71
stoken-gui.1 Normal file
View File

@ -0,0 +1,71 @@
.\"
.\" Man page for stoken-gui
.\"
.TH stoken-gui 1 2012-09-09
.nh
.SH NAME
stoken-gui \- GTK+ software token
.SH SYNOPSIS
\fBstoken-gui\fP [\fIopts\fP]
.SH "DESCRIPTION"
.PP
\fBstoken-gui\fP is a software token compatible with RSA SecurID 128-bit (AES)
tokens. The graphical user interface provides limited support for password
entry and viewing the current token. Initial token importation and other
configuration items are handled through the command-line interface,
\fBstoken(1)\fP.
.PP
After using \fBstoken import\fP to populate \fI~/.stokenrc\fP,
\fBstoken-gui\fP may be invoked to display the tokencode and counter. The
tokencode changes once a minute; \fBstoken-gui\fP may be left running
forever and will copy the current tokencode to the clipboard on demand.
.PP
If a password and/or PIN is required, \fBstoken-gui\fP will pop up a
dialog box requesting the appropriate information.
.SH "OPTIONS"
.TP
\fB\-\-small\fP
Show a minimal window that only displays a tokencode and a progress bar.
Clicking on the tokencode copies it to the clipboard.
.TP
\fB\-\-rcfile=\fIfile\fP
Use an alternate \fI.stokenrc\fP file. This is typically used to support
multiple tokens on the same user account.
.TP
\fB\-\-password=\fIpassword\fP, \fB\-p\fP \fIpassword\fP
Use a password supplied from the command line, instead of prompting the user.
See notes in \fBSECURITY CONSIDERATIONS\fP on the \fBstoken(1)\fP man page.
.TP
\fB\-\-pin=\fIpin\fP, \fB\-n\fP \fIpin\fP
Use a PIN supplied from the command line, instead of prompting the user.
See notes in \fBSECURITY CONSIDERATIONS\fP on the \fBstoken(1)\fP man page.
.TP
\fB\-\-force\fP, \fB\-f\fP
Override token expiration date check.
.TP
\fB\-\-file=\fIfile\fP
Use a token from \fIfile\fP instead of the \fI.stokenrc\fP file. For
testing purposes only.
.TP
\fB\-\-token=\fItoken_string\fP
Use a token from the command line instead of the \fI.stokenrc\fP file. See
above notes on \fB\-\-file\fP.
.TP
\fB\-\-random\fP
Generate a random token on the fly. Used for testing or demonstrations only.
These tokens should \fBnot\fP be used for real authentication.
.TP
\fB\-\-help\fP, \fB\-h\fP
Display basic usage information.
.TP
\fB\-\-version\fP, \fB\-v\fP
Display version information.
.SH "SEE ALSO"
.PP
\fBstoken\fP(1).
.SH FILES
.TP
~/.stokenrc
Default configuration file.
.SH "AUTHOR"
Kevin Cernekee <cernekee@gmail.com>

335
stoken.1 Normal file
View File

@ -0,0 +1,335 @@
.\"
.\" Man page for stoken
.\"
.TH stoken 1 2012-09-09
.nh
.SH NAME
stoken \- software token for cryptographic authentication
.SH SYNOPSIS
\fBstoken\fP [\fBtokencode\fP] [\fB\-\-stdin\fP] [\fB\-\-force\fP]
[\fB\-\-next\fP] [\fIopts\fP]
.PP
\fBstoken\fP \fBimport\fP
{\fB\-\-file=\fP\fIfile\fP | \fB\-\-token=\fP\fItoken_string\fP}
[\fB\-\-force\fP] [\fIopts\fP]
.PP
\fBstoken\fP \fBsetpin\fP [\fIopts\fP]
.PP
\fBstoken\fP \fBsetpass\fP [\fIopts\fP]
.PP
\fBstoken\fP \fBshow\fP [\fB\-\-seed\fP] [\fIopts\fP]
.PP
\fBstoken\fP \fBexport\fP
[{\fB\-\-blocks\fP | \fB\-\-iphone\fP | \fB\-\-android\fP | \fB\-\-v3\fP |
\fB\-\-sdtid\fP | \fB\-\-qr=\fP\fIfile.png\fP | \fB\-\-show\-qr\fP}]
[\fIopts\fP]
.PP
\fBstoken\fP \fBissue\fP [\-\-\fBtemplate\fP=\fIfile\fP]
.PP
\fBstoken\fP \fBhelp\fP
.PP
\fBstoken\fP \fBversion\fP
.SH "DESCRIPTION"
.PP
\fBstoken\fP is a software token compatible with RSA SecurID 128-bit (AES)
tokens. The command-line interface provides facilities for importing new
tokens, displaying the current tokencode, encrypting the seed with a
user-specified password, storing the user's PIN alongside the token, and
viewing or exporting the token data.
.SH "BASIC USAGE"
.PP
Use \fBstoken import\fP to decode a token string and write it into
\fI~/.stokenrc\fP. This may prompt for a device ID and/or password,
depending on what options your administrator used to create the token.
The token string can be provided on the command line, or read from a
text file.
.PP
\fBstoken\fP will autodetect the following types of token strings:
.TP
.B 286510182209303756117707012447003320623006...
.PD 0
.TP
.B 29658\-21098\-45467\-64675\-65731\-01441\-11337...
.PD
Pure numeric (81-digit) "ctf" (compressed token format) strings, with or
without dashes. These may have been furnished as-is, or they could have
been derived from an \fIsdtid\fP file by the RSA \fITokenConverter\fP program.
.TP
.B com.rsa.securid.iphone://ctf?ctfData=229639330774927764401...
iPhone-compatible token strings.
.TP
.B http://127.0.0.1/securid/ctf?ctfData=250494932146245277466...
.PD 0
.TP
.B http://127.0.0.1/securid/ctf?ctfData=AwAAfBc3QSopPxxjLGnxf...
Android-compatible token strings.
.PD
.TP
.B <?xml version=...
RSA \fIsdtid\fP-formatted XML files. These should be imported from a file:
\fBstoken import \-\-file=FILE.SDTID\fP.
.PD
.PP
Tokens supplied as QR codes can be converted back to standard URIs by running
\fBzbarimg\fP(1) on the image file.
.PP
The device ID, if used, can be viewed in the "about" menu for the RSA soft
token app on the phone. Numeric ctf strings and smartphone tokens bound
to a device ID contain a seed that is encrypted using the device ID, so the
ID must be furnished before stoken can successfully import the token.
\fIsdtid\fP files can be imported without knowledge of the device ID, as
long as the password (if any) is known.
.PP
By default, \fBstoken import\fP will refuse to overwrite an existing token in
\fI~/.stokenrc\fP. The \fB\-\-force\fP switch overrides this check.
.PP
\fBstoken import\fP will normally prompt for a new password, which is used
to encrypt the seed before storing it in \fI~/.stokenrc\fP. This can be
bypassed by entering an empty password, or specifying
\fB\-\-new\-password=''\fP on the command line. It is recommended to
choose a longer, hard-to-guess passphrase for this purpose.
.PP
After a token has been imported, running \fBstoken\fP with no arguments
will prompt for any required password or PIN, then display the current
tokencode.
.PP
Tokencodes are computed from the raw (decrypted) seed data, the current
time of day, and the PIN. If the same seed is installed on multiple
devices, they should all produce identical tokencodes. If they do not,
double-check the timezone setting and consider using NTP to synchronize
the system time to a known good source.
.PP
\fBstoken setpin\fP can be used to save the PIN in \fI~/.stokenrc\fP.
Not all tokens will require a PIN; this can be configured by the SecurID
administrator when generating new tokens. Setting an empty PIN will remove
the PIN from \fI~/.stokenrc\fP so that the user will be prompted every
time it is required. See the \fBSECURITY CONSIDERATIONS\fP section below
for additional details.
.PP
\fBstoken setpass\fP encrypts the seed and PIN (if present) in
\fI~/.stokenrc\fP with a user-selectable password or passphrase. If an
empty password is entered, the password will be removed. See the
\fBSECURITY CONSIDERATIONS\fP section below for additional details.
.SH "VIEWING TOKENS"
.PP
\fBstoken show\fP displays information about the current token, typically
read from \fI~/.stokenrc\fP. The \fB\-\-seed\fP option displays the
encrypted and decrypted seed bytes (which should be treated as sensitive
data, as they can be used to derive tokencodes).
.PP
\fBstoken export\fP translates the current token into a format suitable
for importation to another device.
.PP
\fBstoken issue\fP generates a new software token in XML \fIsdtid\fP
format. A template file, itself in \fIsdtid\fP format, may be
provided to override some or all of the human-readable fields. This would
permit appropriate serial numbers, expiration dates, usernames, etc. to be
specified. If Secret, Seed, or MAC fields are present in the template
file, they will be ignored.
.SH "GLOBAL OPTIONS"
.TP
\fB\-\-rcfile=\fIfile\fP
Use an alternate \fI.stokenrc\fP configuration file. This is typically
used to support multiple tokens on the same user's UNIX account. Note that
the \fI.stokenrc\fP file stores additional data (such as the PIN), so it
cannot be parsed as a "raw" token string by \fBstoken \-\-file\fP.
.TP
\fB\-\-password=\fIpassword\fP, \fB\-p\fP \fIpassword\fP
Use a password supplied from the command line, instead of prompting the user.
See notes in \fBSECURITY CONSIDERATIONS\fP below.
.TP
\fB\-\-pin=\fIpin\fP, \fB\-n\fP \fIpin\fP
Use a PIN supplied from the command line, instead of prompting the user.
See notes in \fBSECURITY CONSIDERATIONS\fP below. If you save your PIN
in \fI~/.stokenrc\fP, note that \fB\-\-pin=0000\fP is often required when
activating a new soft token for the first time.
.TP
\fB\-\-devid=\fIdevid\fP
Use a device ID supplied from the command line to decrypt the token. A
token can be bound to a class GUID device ID (i.e. a certain type of device,
such as "iPhone" or "Android"), a unique device ID (one specific unit), or
nothing. \fBstoken\fP will attempt to autodetect matches with a class GUID,
but on rare occasions this results in false positives due to hash collisions.
In these cases, the bound device ID should be specified on the command line to
override autodetection.
.SH "EXPORT OPTIONS"
.TP
\fB\-\-new\-password=\fIpassword\fP
Supply the encryption password from the command line for operations that
write out a token string or \fI.stokenrc\fP file: \fBimport\fP, \fBexport\fP,
\fBsetpass\fP, and \fBissue\fP. See notes in \fBSECURITY CONSIDERATIONS\fP
below.
.TP
\fB\-\-keep\-password\fP
If the token in the \fI.stokenrc\fP file is protected with a password, retain
the same password when exporting the token. By default, the \fBexport\fP
operation will not encrypt the token with a password; note that it may not
be possible to enter all possible passwords on devices with limited text
input capabilities (such as feature phones).
.TP
\fB\-\-new\-pin=\fIpin\fP
Supply a new PIN from the command line for the \fBsetpin\fP operation.
See notes in \fBSECURITY CONSIDERATIONS\fP below.
.TP
\fB\-\-new\-devid=\fIdevid\fP
Used with the \fBexport\fP or \fBissue\fP command to encrypt the new token
with a specific device ID. This is only used for testing purposes.
.TP
\fB\-\-blocks\fP, \fB\-\-iphone\fP, \fB\-\-android\fP, \fB\-\-v3\fP
Used with the \fBexport\fP command to select the output format. See examples
in \fBBASIC USAGE\fP. By default, the \fBexport\fP command will print an
unformatted 81-digit string to standard output.
.TP
\fB\-\-sdtid\fP, \fB\-\-xml\fP
These options are synonyms. Both export a token to standard output in
RSA's \fIsdtid\fP XML format.
.TP
\fB\-\-qr=\fIfile.png\fP
Encode the token as a QR code and write it to \fIfile.png\fP. This requires
the \fBqrencode\fP program to be installed.
.TP
\fB\-\-show\-qr\fP
Encode the token as a QR code and immediately display it on the screen.
This requires the \fBqrencode\fP program to be installed. If the
\fBQR_VIEWER\fP environment variable is set, \fBstoken\fP will use that
program as the preferred viewer. Otherwise it will try to execute a few
common Linux image viewers, and give up if none of them exist.
.TP
\fB\-\-template=\fIfile\fP
Used with the \fBexport\fP or \fBissue\fP commands to override fields in
the XML output. The template file should look like any standard \fIsdtid\fP
file, but all fields are optional and will default to reasonably sane
values if omitted. This can be used to force the output XML to use a
specific serial number, user name, expiration date, etc. Correct MAC
checksums will be (re)computed on the provided values. See the
\fIexamples\fP directory in the source distribution for more information.
.SH "OTHER OPTIONS"
.TP
\fB\-\-use\-time=\fP{\fIunix_time\fP|\fB+\fIoffset\fP|\fB-\fIoffset\fP}
Instead of generating a tokencode based on the current time of day,
force a specific time, or adjust the current time based on a positive
or negative offset (specified in seconds). This is only used for testing
purposes.
.TP
\fB\-\-next\fP
Generate the next tokencode instead of the current tokencode. For a 60-second
token, this is equivalent to \fB\-\-use\-time=+60\fP.
.TP
\fB\-\-stdin\fP, \fB\-s\fP
When generating a tokencode that requires \fIeither\fP a password or PIN,
read the password or PIN as single line from standard input. This is
intended to allow external programs to call \fIstoken\fP to generate
single-use passwords without user intervention; see \fBNON-INTERACTIVE USE\fP
below.
.TP
\fB\-\-force\fP, \fB\-f\fP
Override token expiration date checks (for \fBtokencode\fP) or token
overwrite checks (for \fBimport\fP).
.TP
\fB\-\-batch\fP, \fB\-b\fP
Abort with an error exit code if any user input is required. Intended for
automated operation and testing.
.TP
\fB\-\-file=\fIfile\fP
Read a ctf string, an Android/iPhone URI, or an XML \fIsdtid\fP token from
\fIfile\fP instead of the \fI.stokenrc\fP configuration. Most \fBstoken\fP
commands accept this flag, but it is expected that the typical
user will save his token in \fI~/.stokenrc\fP instead of supplying it by
hand on every invocation. Typically \fB\-\-file\fP and \fB\-\-token\fP
are only used for the \fBimport\fP command.
.TP
\fB\-\-token=\fItoken_string\fP
Use a token from the command line instead of the \fI.stokenrc\fP file. See
above notes on \fB\-\-file\fP.
.TP
\fB\-\-random\fP
Generate a random token on the fly. Used for testing or demonstrations only.
These tokens should \fBnot\fP be used for real authentication.
.TP
\fB\-\-help\fP, \fB\-h\fP
Display basic usage information.
.TP
\fB\-\-version\fP, \fB\-v\fP
Display version information.
.SH "SECURITY CONSIDERATIONS"
.PP
Software tokens, unlike hardware tokens, are relatively easy to replicate.
Systems that store soft token seeds should be carefully guarded to prevent
unauthorized disclosure. The use of whole-disk encryption, such as TrueCrypt,
is strongly recommended for laptops and other portable devices that are
easily lost or stolen.
.PP
\fBstoken\fP permits users to store their PIN in \fI~/.stokenrc\fP to
allow for automated (scriptable) generation of tokencodes, but the risks of
this approach should be carefully weighed against the benefits.
.PP
Using the \fBsetpass\fP command to encrypt the seed and PIN in
\fI~/.stokenrc\fP provides some degree of protection against unauthorized
access, but does not necessarily cover all possible attack vectors. A
host that is already compromised (e.g. running a keylogger) will not
provide adequate protection for any seed(s) stored on it.
.PP
\fBstoken\fP encryption passwords may be up to 40 characters long.
A longer passphrase constructed from several random words can provide
more protection from brute-force attacks than a shorter password.
.PP
Entering a password or PIN on the command line is generally unsafe on
multiuser systems, as other users may be able to view the command line
arguments in \fBps\fP or similar utilities. The command line could
also be cached in shell history files.
.PP
Encoding QR tokens may expose the seed data through \fBps\fP, and
the \fB\-\-show\-qr\fP option writes temporary PNG files in \fB/tmp\fP.
.PP
\fBstoken\fP attempts to lock pages to prevent swapping out to disk, but
does not scrub secrets from process memory.
.SH "NON\-INTERACTIVE USE"
.PP
Other applications, such as VPN clients, may want to invoke \fBstoken\fP
non-interactively to generate single-use passwords. Three usage modes are
supported, depending on the level of security (and/or convenience) that is
required:
.SS No password or PIN
.PP
The user configures \fBstoken\fP to print a tokencode immediately upon
invocation, with no prompts, by using \fBsetpin\fP to store the PIN in
\fI~/.stokenrc\fP and using \fBsetpass\fP to set an empty password.
The other application can then invoke \fBstoken \-\-batch\fP and read
the tokencode through a pipe from standard output.
.PP
This provides no security for the seed, but may be useful in applications
where (re-)authentication is frequent or unattended operation is required.
.SS Save the PIN and set a password
.PP
The user configures \fBstoken\fP to encrypt the \fI~/.stokenrc\fP secrets
with a password using \fBsetpass\fP, then saves the PIN with \fBsetpin\fP.
The PIN and the seed will both be encrypted with the password. The other
application will request the password from the user, then call
\fBstoken \-\-stdin\fP, write the password to \fBstoken\fP's standard input
through a pipe, and read back a tokencode from \fBstoken\fP's standard
output.
.SS No password; prompt for the PIN
.PP
Similar to above, but set an empty password using \fBsetpass\fP, do not
save the PIN in \fI~/.stokenrc\fP, and pass the PIN to \fBstoken \-\-stdin\fP
via standard input.
.SH "BUGS/TODO"
.PP
\fIsdtid\fP support is still new and may choke on unexpected input.
As a short\-term workaround you can try commenting out the
sanity checks in \fBsdtid_decrypt()\fP to see if the problem goes away.
.PP
Features under development include:
hardware token seeds (and the \fBstoken split\fP command needed to work with
them), and support for non\-Linux hosts.
.PP
Patches are always welcome.
.SH "SEE ALSO"
.PP
\fBstoken\-gui\fP(1).
.SH FILES
.TP
~/.stokenrc
Default configuration file.
.SH "AUTHOR"
Kevin Cernekee <cernekee@gmail.com>

11
stoken.pc.in Normal file
View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: stoken
Description: Software token
Version: @VERSION@
Requires.private: libxml-2.0, @DEPS_PC@
Libs: -L${libdir} -lstoken @EXTRA_PC_LIBS@
Cflags: -I${includedir}

2
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.log
*.trs

50
tests/devid-passwd.sdtid Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<TKNBatch>
<TKNHeader>
<Version>0</Version>
<Origin>N/A</Origin>
<Dest>N/A</Dest>
<Name>N/A</Name>
<FirstToken>N/A</FirstToken>
<LastToken>N/A</LastToken>
<NumTokens>0</NumTokens>
<Secret>DhSQ8e/0CDoq3TdNPDjvMw==</Secret>
<DefBirth>2000/01/01</DefBirth>
<DefDeath>2038/01/18</DefDeath>
<DefDigits>8</DefDigits>
<DefInterval>60</DefInterval>
<DefAlg>1</DefAlg>
<DefMode>0</DefMode>
<DefPrecision>2400</DefPrecision>
<DefSmallWin>630</DefSmallWin>
<DefMediumWin>4320</DefMediumWin>
<DefLargeWin>4320</DefLargeWin>
<DefAddPIN>1</DefAddPIN>
<DefLocalPIN>1</DefLocalPIN>
<DefCopyProtection>1</DefCopyProtection>
<DefPinType>0</DefPinType>
<DefKeypad>1</DefKeypad>
<DefProtLevel>0</DefProtLevel>
<DefRevision>0</DefRevision>
<DefTimeDerivedSeeds>1</DefTimeDerivedSeeds>
<DefAppDerivedSeeds>0</DefAppDerivedSeeds>
<DefFormFactor>20000001</DefFormFactor>
<HeaderMAC>dY+8gSNYrxbywmBd2ou18Q==</HeaderMAC>
</TKNHeader>
<TKN>
<SN>584917508172</SN>
<Seed>=EAn5SLIT+nMMYUC3JH+eDw==</Seed>
<UserFirstName> </UserFirstName>
<UserLastName> </UserLastName>
<UserLogin> </UserLogin>
<TokenAttributes>
<DeviceSerialNumber>a01c4380-fc01-4df0-b113-7fb98ec74694</DeviceSerialNumber>
<Nickname> </Nickname>
</TokenAttributes>
<TokenMAC>KDXMn8UWRprjjjmdO055oA==</TokenMAC>
</TKN>
<TKNTrailer>
<BatchSignature>Mnq7atVBljepJU3ybPaF4iB+7i8tpwAPI5Fh6ahYS5UuuxnAsDEN4IFd93FfAoyAGG2Wuq9HuZ7uii87vRbF3GbvnzwHzF7XI3W9uC5TIA67YUyCuErXgHCcA+1w9gE4t25VgQ6R2hsREjjTA4RgoogQqFHDus/PGdWiMdVMQRg=</BatchSignature>
<BatchCertificate>MIICeTCCAWGgAwIBAgIQM0NFNTU4NTMzMDlGODEzMDANBgkqhkiG9w0BAQQFADBBMT8wPQYDVQQDEzZTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMsIEluYy4gUHJpbWFyeSBDQSBSb290IDEwHhcNMDIwNTE3MTkyMTU1WhcNMjIwNTEyMTkyMTU1WjA0MTIwMAYDVQQDEylTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMgQUNFL1NlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1np1DIf3HOHAK2ahcRzZCJsqIC1QMEqtsdanKSEn5CGtLCdLv9LbLUYo6cQxKSJtwvigpeDgBAb/UYcUNXy/7dY7rA5WpYlsaA9h5C9qzPMBHxVGSIe5k61uUbAwdFhCMfLh776wR//VZ7cuypo5d3cCbvgHGwqw4ZuECbKvONMCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAq8MMJs1SczwpfcZqn9loM+2RhFmN1IZiXyevz1VvGD9GUrlLalm/Et989zR/dVhciGXmAAxYnV/MoZmshjXozmJgjRmfqqHLS46UJ9nLZ2BuEVcrHnn6f9meIjeMWm+Dvh+8Vi9KJOLozYbDoaUMm+5F7ywKsUuBPRSJ1ykGJG6dOCBZlJGmM3kbZ54lRAK2TYcu2JM21i7BKdeE9xItyabJzEk3QCsX0erY7h3V//okIfKWLh8LieoWbV4+VtrQEoiUwyqdYswwgMOyRiKuGTkk3DhHdoqhG8SqHSxkPto42hEnpOx9j2rqcsOWosvNyfm9nwkqJfhuJClLwOzw5Q==</BatchCertificate>
</TKNTrailer>
</TKNBatch>

View File

@ -0,0 +1 @@
$STOKEN export --android $tok0

View File

@ -0,0 +1 @@
http://127.0.0.1/securid/ctf?ctfData=258491750817210752367175001073261277346642631755724762324173166222072476671635706

View File

@ -0,0 +1 @@
$STOKEN export --iphone $tok0 --new-password asdf

View File

@ -0,0 +1 @@
com.rsa.securid.iphone://ctf?ctfData=258491750817271376337025556032745736615071405660444767006173166222072476671610011

View File

@ -0,0 +1,2 @@
$STOKEN export --v3 $tok0
$STOKEN export --blocks --token $out

1
tests/export-read-v3.ref Normal file
View File

@ -0,0 +1 @@
25849-17508-17210-75236-71750-01073-26127-73466-42631-75572-47623-24172-16622-20724-72716-77463-6

View File

@ -0,0 +1 @@
$STOKEN export $tok0 --sdtid --template $TESTDIR/fixed-secret.xml --new-devid 'a01c4380-fc01-4df0-b113-7fb98ec74694' --new-password 'Correct_horse!battery&staple'

View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<TKNBatch>
<TKNHeader>
<Version>0</Version>
<Origin>N/A</Origin>
<Dest>N/A</Dest>
<Name>N/A</Name>
<FirstToken>N/A</FirstToken>
<LastToken>N/A</LastToken>
<NumTokens>0</NumTokens>
<Secret>WTFNzycMvOXcvS7UKDpKWA==</Secret>
<DefBirth>2000/01/01</DefBirth>
<DefDeath>2038/01/18</DefDeath>
<DefDigits>8</DefDigits>
<DefInterval>60</DefInterval>
<DefAlg>1</DefAlg>
<DefMode>0</DefMode>
<DefPrecision>2400</DefPrecision>
<DefSmallWin>630</DefSmallWin>
<DefMediumWin>4320</DefMediumWin>
<DefLargeWin>4320</DefLargeWin>
<DefAddPIN>1</DefAddPIN>
<DefLocalPIN>1</DefLocalPIN>
<DefCopyProtection>1</DefCopyProtection>
<DefPinType>0</DefPinType>
<DefKeypad>1</DefKeypad>
<DefProtLevel>0</DefProtLevel>
<DefRevision>0</DefRevision>
<DefTimeDerivedSeeds>1</DefTimeDerivedSeeds>
<DefAppDerivedSeeds>0</DefAppDerivedSeeds>
<DefFormFactor>20000001</DefFormFactor>
<HeaderMAC>JrxjfjsTffwm7ztFeqD++w==</HeaderMAC>
</TKNHeader>
<TKN>
<SN>584917508172</SN>
<Seed>=y5PhhfjspvRPB/ZbrMZKfg==</Seed>
<UserFirstName> </UserFirstName>
<UserLastName> </UserLastName>
<UserLogin> </UserLogin>
<TokenAttributes>
<DeviceSerialNumber>a01c4380-fc01-4df0-b113-7fb98ec74694</DeviceSerialNumber>
<Nickname> </Nickname>
</TokenAttributes>
<TokenMAC>3RVitQbpQDnjBQQjdqVbSw==</TokenMAC>
</TKN>
<TKNTrailer>
<BatchSignature>Vr3pVXbhsCuyUuGFYGaF+UwqmfdfkhMZdcDR5+s3y5O8vH9jth7AtQj2idPr2L3GIGFAShGx9OPYF7044nS7pIeMMTgtAttnQvNaBK/FFK3D+qDdsv9asKOG546LlbXBP/DIkOr0PbAxBhilY8CuUiMxUbBqkqP8dzMCxUOV/a8=</BatchSignature>
<BatchCertificate>MIICeTCCAWGgAwIBAgIQM0NFNTU4NTMzMDlGODEzMDANBgkqhkiG9w0BAQQFADBBMT8wPQYDVQQDEzZTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMsIEluYy4gUHJpbWFyeSBDQSBSb290IDEwHhcNMDIwNTE3MTkyMTU1WhcNMjIwNTEyMTkyMTU1WjA0MTIwMAYDVQQDEylTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMgQUNFL1NlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1np1DIf3HOHAK2ahcRzZCJsqIC1QMEqtsdanKSEn5CGtLCdLv9LbLUYo6cQxKSJtwvigpeDgBAb/UYcUNXy/7dY7rA5WpYlsaA9h5C9qzPMBHxVGSIe5k61uUbAwdFhCMfLh776wR//VZ7cuypo5d3cCbvgHGwqw4ZuECbKvONMCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAq8MMJs1SczwpfcZqn9loM+2RhFmN1IZiXyevz1VvGD9GUrlLalm/Et989zR/dVhciGXmAAxYnV/MoZmshjXozmJgjRmfqqHLS46UJ9nLZ2BuEVcrHnn6f9meIjeMWm+Dvh+8Vi9KJOLozYbDoaUMm+5F7ywKsUuBPRSJ1ykGJG6dOCBZlJGmM3kbZ54lRAK2TYcu2JM21i7BKdeE9xItyabJzEk3QCsX0erY7h3V//okIfKWLh8LieoWbV4+VtrQEoiUwyqdYswwgMOyRiKuGTkk3DhHdoqhG8SqHSxkPto42hEnpOx9j2rqcsOWosvNyfm9nwkqJfhuJClLwOzw5Q==</BatchCertificate>
</TKNTrailer>
</TKNBatch>

View File

@ -0,0 +1,2 @@
$STOKEN export --v3 --file $TESTDIR/pinmode-2.sdtid
$STOKEN export --token $out --sdtid --template $TESTDIR/fixed-secret.xml

50
tests/export-v3-sdtid.ref Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<TKNBatch>
<TKNHeader>
<Version>0</Version>
<Origin>N/A</Origin>
<Dest>N/A</Dest>
<Name>N/A</Name>
<FirstToken>N/A</FirstToken>
<LastToken>N/A</LastToken>
<NumTokens>0</NumTokens>
<Secret>WTFNzycMvOXcvS7UKDpKWA==</Secret>
<DefBirth>2000/01/01</DefBirth>
<DefDeath>2038/01/18</DefDeath>
<DefDigits>8</DefDigits>
<DefInterval>60</DefInterval>
<DefAlg>1</DefAlg>
<DefMode>0</DefMode>
<DefPrecision>2400</DefPrecision>
<DefSmallWin>630</DefSmallWin>
<DefMediumWin>4320</DefMediumWin>
<DefLargeWin>4320</DefLargeWin>
<DefAddPIN>1</DefAddPIN>
<DefLocalPIN>0</DefLocalPIN>
<DefCopyProtection>1</DefCopyProtection>
<DefPinType>0</DefPinType>
<DefKeypad>1</DefKeypad>
<DefProtLevel>0</DefProtLevel>
<DefRevision>0</DefRevision>
<DefTimeDerivedSeeds>1</DefTimeDerivedSeeds>
<DefAppDerivedSeeds>0</DefAppDerivedSeeds>
<DefFormFactor>20000001</DefFormFactor>
<HeaderMAC>zOwik/DkqmPnv7ZlHejHNw==</HeaderMAC>
</TKNHeader>
<TKN>
<SN>127456102283</SN>
<Seed>=f9XQ2HTtHfdLO7DUQ5+V4Q==</Seed>
<UserFirstName> </UserFirstName>
<UserLastName> </UserLastName>
<UserLogin> </UserLogin>
<TokenAttributes>
<DeviceSerialNumber> </DeviceSerialNumber>
<Nickname> </Nickname>
</TokenAttributes>
<TokenMAC>x4rl0smdX32WW9hYiWkgsg==</TokenMAC>
</TKN>
<TKNTrailer>
<BatchSignature>eiePYA7QXEsqy8IhKzMs0/7QHJUPBzvb1YJorh4wbVirsq+FyJy60Oa8N1smABcj6LBottggtshY75E59VRa5OGlxU1DiS2O+UpVeaqLvBpfEl2DR1ZkLZCsKmCY++zrnQrOS8xcmAEQcgrkX4rQI0C91mbQb3NDf3COtT12OmM=</BatchSignature>
<BatchCertificate>MIICeTCCAWGgAwIBAgIQM0NFNTU4NTMzMDlGODEzMDANBgkqhkiG9w0BAQQFADBBMT8wPQYDVQQDEzZTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMsIEluYy4gUHJpbWFyeSBDQSBSb290IDEwHhcNMDIwNTE3MTkyMTU1WhcNMjIwNTEyMTkyMTU1WjA0MTIwMAYDVQQDEylTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMgQUNFL1NlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1np1DIf3HOHAK2ahcRzZCJsqIC1QMEqtsdanKSEn5CGtLCdLv9LbLUYo6cQxKSJtwvigpeDgBAb/UYcUNXy/7dY7rA5WpYlsaA9h5C9qzPMBHxVGSIe5k61uUbAwdFhCMfLh776wR//VZ7cuypo5d3cCbvgHGwqw4ZuECbKvONMCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAq8MMJs1SczwpfcZqn9loM+2RhFmN1IZiXyevz1VvGD9GUrlLalm/Et989zR/dVhciGXmAAxYnV/MoZmshjXozmJgjRmfqqHLS46UJ9nLZ2BuEVcrHnn6f9meIjeMWm+Dvh+8Vi9KJOLozYbDoaUMm+5F7ywKsUuBPRSJ1ykGJG6dOCBZlJGmM3kbZ54lRAK2TYcu2JM21i7BKdeE9xItyabJzEk3QCsX0erY7h3V//okIfKWLh8LieoWbV4+VtrQEoiUwyqdYswwgMOyRiKuGTkk3DhHdoqhG8SqHSxkPto42hEnpOx9j2rqcsOWosvNyfm9nwkqJfhuJClLwOzw5Q==</BatchCertificate>
</TKNTrailer>
</TKNBatch>

6
tests/fixed-secret.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0"?>
<TKNBatch>
<TKNHeader>
<Secret>WTFNzycMvOXcvS7UKDpKWA==</Secret>
</TKNHeader>
</TKNBatch>

1
tests/mac-align.pipe Normal file
View File

@ -0,0 +1 @@
$STOKEN export --android --file $TESTDIR/mac-align.sdtid

1
tests/mac-align.ref Normal file
View File

@ -0,0 +1 @@
http://127.0.0.1/securid/ctf?ctfData=299792244709622667530306243203205436512412555314055336764172166222314456671666125

104
tests/mac-align.sdtid Normal file
View File

@ -0,0 +1,104 @@
<?xml version="1.0"?>
<!--
This tests the "bug compatibility" code in recursive_hash(), which provides
special handling for hashing short strings. Here is an example of a BAD
hash:
00000000 54 4b 4e 2e 53 4e 20 39 39 37 39 32 32 34 34 37 |TKN.SN 997922447|
00000010 30 39 36 0a 54 4b 4e 2e 53 65 65 64 20 3d 79 4f |096.TKN.Seed =yO|
00000020 45 4d 6e 6a 59 2f 62 4f 61 73 52 61 5a 6b 39 4c |EMnjY/bOasRaZk9L|
00000030 74 33 6b 51 3d 3d 0a 00 00 00 00 54 4b 4e 2e 55 |t3kQ==.....TKN.U|
00000040 73 65 72 46 69 72 73 74 4e 61 6d 65 20 41 6c 69 |serFirstName Ali|
00000050 67 6e 6d 65 6e 74 0a 00 00 00 00 00 00 00 00 00 |gnment..........|
00000060 00 00 54 4b 4e 2e 55 73 65 72 4c 61 73 74 4e 61 |..TKN.UserLastNa|
00000070 6d 65 20 54 65 73 74 20 43 73 0a 00 00 54 4b 4e |me Test Cs...TKN|
00000080 2e 55 73 65 72 4c 6f 67 69 6e 20 61 6c 69 67 6e |.UserLogin align|
00000090 74 65 73 74 0a 00 00 00 00 00 00 00 00 00 00 00 |test............|
000000a0 00 00 54 4b 4e 2e 41 64 64 50 49 4e 20 31 0a 00 |..TKN.AddPIN 1..|
000000b0 00 54 4b 4e 2e 4c 6f 63 61 6c 50 49 4e 20 31 0a |.TKN.LocalPIN 1.|
000000c0 00 54 4b 4e 2e 54 6f 6b 65 6e 41 74 74 72 69 62 |.TKN.TokenAttrib|
000000d0 75 74 65 73 2e 44 65 76 69 63 65 53 65 72 69 61 |utes.DeviceSeria|
000000e0 6c 4e 75 6d 62 65 72 20 20 0a 00 54 4b 4e 2e 54 |lNumber ..TKN.T|
000000f0 6f 6b 65 6e 41 74 74 72 69 62 75 74 65 73 2e 4e |okenAttributes.N|
00000100 69 63 6b 6e 61 6d 65 20 20 0a 00 00 00 00 00 00 |ickname .......|
00000110 00 00 00 00 00 |.....|
TKN.AddPin starts and ends within a single AES block, so it should be
aligned to the start of the block (offset 0xa0). The correct version looks
like this:
00000000 54 4b 4e 2e 53 4e 20 39 39 37 39 32 32 34 34 37 |TKN.SN 997922447|
00000010 30 39 36 0a 54 4b 4e 2e 53 65 65 64 20 3d 79 4f |096.TKN.Seed =yO|
00000020 45 4d 6e 6a 59 2f 62 4f 61 73 52 61 5a 6b 39 4c |EMnjY/bOasRaZk9L|
00000030 74 33 6b 51 3d 3d 0a 00 00 00 00 54 4b 4e 2e 55 |t3kQ==.....TKN.U|
00000040 73 65 72 46 69 72 73 74 4e 61 6d 65 20 41 6c 69 |serFirstName Ali|
00000050 67 6e 6d 65 6e 74 0a 00 00 00 00 00 00 00 00 00 |gnment..........|
00000060 00 00 54 4b 4e 2e 55 73 65 72 4c 61 73 74 4e 61 |..TKN.UserLastNa|
00000070 6d 65 20 54 65 73 74 20 43 73 0a 00 00 54 4b 4e |me Test Cs...TKN|
00000080 2e 55 73 65 72 4c 6f 67 69 6e 20 61 6c 69 67 6e |.UserLogin align|
00000090 74 65 73 74 0a 00 00 00 00 00 00 00 00 00 00 00 |test............|
000000a0 54 4b 4e 2e 41 64 64 50 49 4e 20 31 0a 00 00 54 |TKN.AddPIN 1...T|
000000b0 4b 4e 2e 4c 6f 63 61 6c 50 49 4e 20 31 0a 00 00 |KN.LocalPIN 1...|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 54 4b 4e |.............TKN|
000000d0 2e 54 6f 6b 65 6e 41 74 74 72 69 62 75 74 65 73 |.TokenAttributes|
000000e0 2e 44 65 76 69 63 65 53 65 72 69 61 6c 4e 75 6d |.DeviceSerialNum|
000000f0 62 65 72 20 20 0a 00 00 00 00 00 00 00 00 00 00 |ber ...........|
00000100 00 00 00 54 4b 4e 2e 54 6f 6b 65 6e 41 74 74 72 |...TKN.TokenAttr|
00000110 69 62 75 74 65 73 2e 4e 69 63 6b 6e 61 6d 65 20 |ibutes.Nickname |
00000120 20 0a 00 00 00 | ....|
-->
<TKNBatch>
<TKNHeader>
<Version>0</Version>
<Origin>N/A</Origin>
<Dest>N/A</Dest>
<Name>N/A</Name>
<FirstToken>N/A</FirstToken>
<LastToken>N/A</LastToken>
<NumTokens>0</NumTokens>
<Secret>WTFNzycMvOXcvS7UKDpKWA==</Secret>
<DefBirth>2000/01/01</DefBirth>
<DefDeath>2038/01/18</DefDeath>
<DefDigits>8</DefDigits>
<DefInterval>60</DefInterval>
<DefAlg>1</DefAlg>
<DefMode>0</DefMode>
<DefPrecision>2400</DefPrecision>
<DefSmallWin>630</DefSmallWin>
<DefMediumWin>4320</DefMediumWin>
<DefLargeWin>4320</DefLargeWin>
<DefAddPIN>1</DefAddPIN>
<DefLocalPIN>0</DefLocalPIN>
<DefCopyProtection>1</DefCopyProtection>
<DefPinType>0</DefPinType>
<DefKeypad>1</DefKeypad>
<DefProtLevel>0</DefProtLevel>
<DefRevision>0</DefRevision>
<DefTimeDerivedSeeds>1</DefTimeDerivedSeeds>
<DefAppDerivedSeeds>0</DefAppDerivedSeeds>
<DefFormFactor>20000001</DefFormFactor>
<HeaderMAC>zOwik/DkqmPnv7ZlHejHNw==</HeaderMAC>
</TKNHeader>
<TKN>
<SN>997922447096</SN>
<Seed>=yOEMnjY/bOasRaZk9Lt3kQ==</Seed>
<UserFirstName>Alignment</UserFirstName>
<UserLastName>Test Cs</UserLastName>
<UserLogin>aligntest</UserLogin>
<AddPIN>1</AddPIN>
<LocalPIN>0</LocalPIN>
<TokenAttributes>
<DeviceSerialNumber> </DeviceSerialNumber>
<Nickname> </Nickname>
</TokenAttributes>
<TokenMAC>7rhsO4+hQZ1vKSDnule/4g==</TokenMAC>
</TKN>
<TKNTrailer>
<BatchSignature>qauF4qGbpAKrnKgzS7JoCZGQ1Srpk0digJ/usaf2pdfEISPhEXdqsBaDQ8c+gBb36QGL8LEOmNpbaQBZeTMnKz6fwVfHTgfYRU0uXDPLKQdwsMT7B3KCAMopEhMyM+tkkkeaxhQPl1ZuxBCt5CN9tsdpml2ywAC5QAGBN2ujUT4=</BatchSignature>
<BatchCertificate>MIICeTCCAWGgAwIBAgIQM0NFNTU4NTMzMDlGODEzMDANBgkqhkiG9w0BAQQFADBBMT8wPQYDVQQDEzZTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMsIEluYy4gUHJpbWFyeSBDQSBSb290IDEwHhcNMDIwNTE3MTkyMTU1WhcNMjIwNTEyMTkyMTU1WjA0MTIwMAYDVQQDEylTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMgQUNFL1NlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1np1DIf3HOHAK2ahcRzZCJsqIC1QMEqtsdanKSEn5CGtLCdLv9LbLUYo6cQxKSJtwvigpeDgBAb/UYcUNXy/7dY7rA5WpYlsaA9h5C9qzPMBHxVGSIe5k61uUbAwdFhCMfLh776wR//VZ7cuypo5d3cCbvgHGwqw4ZuECbKvONMCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAq8MMJs1SczwpfcZqn9loM+2RhFmN1IZiXyevz1VvGD9GUrlLalm/Et989zR/dVhciGXmAAxYnV/MoZmshjXozmJgjRmfqqHLS46UJ9nLZ2BuEVcrHnn6f9meIjeMWm+Dvh+8Vi9KJOLozYbDoaUMm+5F7ywKsUuBPRSJ1ykGJG6dOCBZlJGmM3kbZ54lRAK2TYcu2JM21i7BKdeE9xItyabJzEk3QCsX0erY7h3V//okIfKWLh8LieoWbV4+VtrQEoiUwyqdYswwgMOyRiKuGTkk3DhHdoqhG8SqHSxkPto42hEnpOx9j2rqcsOWosvNyfm9nwkqJfhuJClLwOzw5Q==</BatchCertificate>
</TKNTrailer>
</TKNBatch>

50
tests/pinmode-2.sdtid Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<TKNBatch>
<TKNHeader>
<Version>0</Version>
<Origin>N/A</Origin>
<Dest>N/A</Dest>
<Name>N/A</Name>
<FirstToken>N/A</FirstToken>
<LastToken>N/A</LastToken>
<NumTokens>0</NumTokens>
<Secret>WTFNzycMvOXcvS7UKDpKWA==</Secret>
<DefBirth>2000/01/01</DefBirth>
<DefDeath>2038/01/18</DefDeath>
<DefDigits>8</DefDigits>
<DefInterval>60</DefInterval>
<DefAlg>1</DefAlg>
<DefMode>0</DefMode>
<DefPrecision>2400</DefPrecision>
<DefSmallWin>630</DefSmallWin>
<DefMediumWin>4320</DefMediumWin>
<DefLargeWin>4320</DefLargeWin>
<DefAddPIN>1</DefAddPIN>
<DefLocalPIN>0</DefLocalPIN>
<DefCopyProtection>1</DefCopyProtection>
<DefPinType>0</DefPinType>
<DefKeypad>1</DefKeypad>
<DefProtLevel>0</DefProtLevel>
<DefRevision>0</DefRevision>
<DefTimeDerivedSeeds>1</DefTimeDerivedSeeds>
<DefAppDerivedSeeds>0</DefAppDerivedSeeds>
<DefFormFactor>20000001</DefFormFactor>
<HeaderMAC>zOwik/DkqmPnv7ZlHejHNw==</HeaderMAC>
</TKNHeader>
<TKN>
<SN>127456102283</SN>
<Seed>=f9XQ2HTtHfdLO7DUQ5+V4Q==</Seed>
<UserFirstName> </UserFirstName>
<UserLastName> </UserLastName>
<UserLogin> </UserLogin>
<TokenAttributes>
<DeviceSerialNumber> </DeviceSerialNumber>
<Nickname> </Nickname>
</TokenAttributes>
<TokenMAC>x4rl0smdX32WW9hYiWkgsg==</TokenMAC>
</TKN>
<TKNTrailer>
<BatchSignature>eiePYA7QXEsqy8IhKzMs0/7QHJUPBzvb1YJorh4wbVirsq+FyJy60Oa8N1smABcj6LBottggtshY75E59VRa5OGlxU1DiS2O+UpVeaqLvBpfEl2DR1ZkLZCsKmCY++zrnQrOS8xcmAEQcgrkX4rQI0C91mbQb3NDf3COtT12OmM=</BatchSignature>
<BatchCertificate>MIICeTCCAWGgAwIBAgIQM0NFNTU4NTMzMDlGODEzMDANBgkqhkiG9w0BAQQFADBBMT8wPQYDVQQDEzZTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMsIEluYy4gUHJpbWFyeSBDQSBSb290IDEwHhcNMDIwNTE3MTkyMTU1WhcNMjIwNTEyMTkyMTU1WjA0MTIwMAYDVQQDEylTZWN1cml0eSBEeW5hbWljcyBUZWNobm9sb2dpZXMgQUNFL1NlcnZlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1np1DIf3HOHAK2ahcRzZCJsqIC1QMEqtsdanKSEn5CGtLCdLv9LbLUYo6cQxKSJtwvigpeDgBAb/UYcUNXy/7dY7rA5WpYlsaA9h5C9qzPMBHxVGSIe5k61uUbAwdFhCMfLh776wR//VZ7cuypo5d3cCbvgHGwqw4ZuECbKvONMCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAq8MMJs1SczwpfcZqn9loM+2RhFmN1IZiXyevz1VvGD9GUrlLalm/Et989zR/dVhciGXmAAxYnV/MoZmshjXozmJgjRmfqqHLS46UJ9nLZ2BuEVcrHnn6f9meIjeMWm+Dvh+8Vi9KJOLozYbDoaUMm+5F7ywKsUuBPRSJ1ykGJG6dOCBZlJGmM3kbZ54lRAK2TYcu2JM21i7BKdeE9xItyabJzEk3QCsX0erY7h3V//okIfKWLh8LieoWbV4+VtrQEoiUwyqdYswwgMOyRiKuGTkk3DhHdoqhG8SqHSxkPto42hEnpOx9j2rqcsOWosvNyfm9nwkqJfhuJClLwOzw5Q==</BatchCertificate>
</TKNTrailer>
</TKNBatch>

44
tests/pipe-wrapper.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/bash
# Each .pipe file contains a "pipeline" of commands. Compare the output
# of the FINAL command to <foo>.ref
#
# Variables available to the .pipe test cases:
# $STOKEN - path to stoken executable
# $out - the last command's output
# $TESTDIR - path to test directory
# $tok0 - sample v2 ctf token (no devid/pass)
#
# To regenerate all output files, use "make check TESTGEN=1"
set -ex
base="$1"
if [[ "$base" != *.pipe ]]; then
echo "Invalid test file: $base"
exit 1
fi
base="${base%.pipe}"
TESTDIR="${TESTDIR:-.}"
STOKEN="${STOKEN:-../stoken}"
LIBTOOL="${LIBTOOL:-../libtool}"
if ! test -z "${VALGRIND}"; then
STOKEN="${LIBTOOL} --mode=execute ${VALGRIND} ${STOKEN}"
fi
tok0="--token=258491750817210752367175001073261277346642631755724762324173166222072472716737543"
out=""
while read x; do
out=`eval $x`
done < ${base}.pipe
if [ "$TESTGEN" = "1" ]; then
echo "$out" > ${base}.ref
else
ref=`cat ${base}.ref`
[ "$out" != "$ref" ] && exit 1
fi
exit 0

View File

@ -0,0 +1 @@
$STOKEN tokencode --file $TESTDIR/devid-passwd.sdtid --use-time 1410710132 --pin 1234 --password 'Correct_horse!battery&staple'

View File

@ -0,0 +1 @@
27957523

1
tests/tokencode-v2.pipe Normal file
View File

@ -0,0 +1 @@
$STOKEN tokencode --token 'http://127.0.0.1/securid/ctf?ctfData=258491750817210752367175001073261277346642631755724762324173166222072472716737543' --use-time 1409757465 --pin 9999

1
tests/tokencode-v2.ref Normal file
View File

@ -0,0 +1 @@
65365425

1
tests/tokencode-v3.pipe Normal file
View File

@ -0,0 +1 @@
$STOKEN tokencode --token 'http://127.0.0.1/securid/ctf?ctfData=AwEBWoDfCnTYFHKM8RvGCXEbSiReGdGgA88EDrIP6EhAe8tzPkIGiAaXXtInt6UHsgM1NFmwuTVjOlJXIpNXxmj7Iud0hfL2kLmIdPgRiS6jP%2FO8q9Fcpwo%2F8tLukZRoIU7gdFjpSl3teO%2FMWlr9rJBZtkTW4q0mAehJ1tl4l0vGjcDycwmIgyzeods7F43ljVETNZjlHkDTudosNSvmS%2Bl643vFrM6NGT%2BHLrlCX0igfo5i4yaUKwDDS4AiAEq%2Bpp0dv8ZzkpZIEJikRzeWaxpfml%2BmsakJ%2BYAVFcfBoR2%2BLzr1%2Flp7mX%2BwMw4TFDZ4hS88BMY3P7uV9%2BGNz08Euaru779p4XDde0JxrPGPuGjWxUBt%2BN5aUjJkcXvAtswhfirK' --use-time 1410710132 --pin 1234 --password 'Correct_horse!battery&staple' --devid 'a01c4380-fc01-4df0-b113-7fb98ec74694'

1
tests/tokencode-v3.ref Normal file
View File

@ -0,0 +1 @@
27957523