Import Upstream version 2.6.1

This commit is contained in:
Cong Liu 2022-11-15 17:13:15 +08:00
commit 6dab3e834c
93 changed files with 68728 additions and 0 deletions

1
.tarball-version Normal file
View File

@ -0,0 +1 @@
2.6.1

1
.version Normal file
View File

@ -0,0 +1 @@
2.5.3.2-c119-dirty

504
COPYING 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 St, 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 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 St, 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!

4728
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

181
Makefile.am Normal file
View File

@ -0,0 +1,181 @@
SUBDIRS = src
ACLOCAL_AMFLAGS = --install -I m4
lib_LTLIBRARIES = libcacard.la
libcacard_la_SOURCES = \
src/cac.c \
src/cac-aca.c \
src/cac-aca.h \
src/gp.c \
src/gp.h \
src/capcsc.h \
src/card_7816.c \
src/common.c \
src/common.h \
src/event.c \
src/glib-compat.h \
src/simpletlv.c \
src/simpletlv.h \
src/vcard.c \
src/vcard_emul_nss.c \
src/vcard_emul_type.c \
src/vcardt.c \
src/vcardt_internal.h \
src/vreader.c \
$(NULL)
if ENABLE_PCSC
libcacard_la_SOURCES += src/capcsc.c
endif
libcacard_includedir = $(includedir)/cacard
libcacard_include_HEADERS = \
src/cac.h \
src/card_7816.h \
src/card_7816t.h \
src/eventt.h \
src/libcacard.h \
src/vcard.h \
src/vcard_emul.h \
src/vcard_emul_type.h \
src/vcardt.h \
src/vevent.h \
src/vreader.h \
src/vreadert.h \
src/vscard_common.h \
$(NULL)
libcacard_la_LIBADD = $(CACARD_LIBS) $(PCSC_LIBS)
libcacard_la_LDFLAGS = \
-export-symbols $(srcdir)/src/libcacard.syms \
-no-undefined \
-version-info 0:0:0 \
$(AM_LDFLAGS) \
$(NULL)
if OS_WIN32
libcacard_la_LIBADD += -lws2_32
endif
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libcacard.pc
include $(srcdir)/build-aux/glib-tap.mk
noinst_PROGRAMS += vscclient
vscclient_SOURCES = src/vscclient.c
vscclient_LDADD = libcacard.la $(GTHREAD_LIBS) $(PCSC_LIBS)
vscclient_CFLAGS = $(AM_CPPFLAGS) $(GTHREAD_CFLAGS) $(PCSC_CFLAGS)
if OS_WIN32
vscclient_CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
endif
tests/softhsm2.conf:
$(AM_V_GEN)(cd tests/ && $(abs_srcdir)/tests/setup-softhsm2.sh)
clean-local:
rm -rf tests/hwdb tests/tokens tests/softhsm2.conf
EXTRA_DIST += tests/setup-softhsm2.sh
AM_TESTS_ENVIRONMENT += \
SOFTHSM2_CONF=tests/softhsm2.conf
test_programs = \
tests/libcacard \
tests/simpletlv \
tests/hwtests \
$(NULL)
tests_libcacard_SOURCES = \
tests/common.c \
tests/common.h \
tests/libcacard.c \
$(NULL)
tests_libcacard_LDADD = \
libcacard.la \
src/common.lo \
src/simpletlv.lo \
$(NULL)
tests_simpletlv_LDADD = \
libcacard.la \
src/common.lo \
src/simpletlv.lo \
$(NULL)
tests_hwtests_SOURCES = \
tests/common.c \
tests/common.h \
tests/hwtests.c \
$(NULL)
tests_hwtests_LDADD = \
libcacard.la \
src/common.lo \
src/simpletlv.lo \
$(NULL)
tests_hwtests_DEPENDENCIES = tests/softhsm2.conf
@CODE_COVERAGE_RULES@
AM_CPPFLAGS = \
-DG_LOG_DOMAIN=\"libcacard\" \
-DLIBCACARD_COMPILATION \
$(CACARD_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(PCSC_CFLAGS) \
$(WARN_CFLAGS) \
-I$(srcdir)/src \
$(NULL)
AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS) $(WARN_LDFLAGS)
EXTRA_DIST += \
NEWS \
README.md \
docs/libcacard.txt \
src/libcacard.syms \
build-aux/tap-driver.sh \
build-aux/tap-test \
tests/db/cert8.db \
tests/db/key3.db \
tests/db/secmod.db \
tests/cert.cfg \
$(NULL)
# aclocal will copy m4 files from pkg-config/autoconf-archives
MAINTAINERCLEANFILES += $(srcdir)/m4/pkg.m4
MAINTAINERCLEANFILES += $(srcdir)/m4/ax_*.m4
MAINTAINERCLEANFILES += \
$(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \
$(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \
$(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL)
# see git-version-gen
dist-hook: gen-ChangeLog
echo $(VERSION) > $(distdir)/.tarball-version
$(top_srcdir)/.version:
echo $(VERSION) > $@-t && mv $@-t $@
BUILT_SOURCES += $(top_srcdir)/.version
EXTRA_DIST += \
$(top_srcdir)/.version \
build-aux/git-version-gen \
$(NULL)
gen-ChangeLog:
@if test -d "$(srcdir)/.git"; then \
echo Generating ChangeLog... ; \
( cd "$(srcdir)" \
&& $(top_srcdir)/build-aux/missing --run git log --stat ) > ChangeLog.tmp \
&& mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \
|| ( rm -f ChangeLog.tmp; \
echo Failed to generate ChangeLog >&2 ); \
else \
echo A git checkout is required to generate a ChangeLog >&2; \
fi
-include $(top_srcdir)/git.mk

2022
Makefile.in Normal file

File diff suppressed because it is too large Load Diff

50
NEWS Normal file
View File

@ -0,0 +1,50 @@
v2.6.1
======
- various bug fixes (memory corruption issues which would cause crashes in
spice-gtk)
v2.6.0
======
- provides implementation of GSC-IS 2.1 (aka CAC version 2) to improve
interoperatibility with guest software using the emulated or shared
smart cards. The previously implemented CACv1 specification is no
longer supported by any other application so the old code is gone
and any application depending on this old standard will not work
anymore.
- vscclient is no longer installed, as it is not an end-user support
solution
- various bug & leak fixes
v2.5.3
======
- fix memory leak in vcard_apdu_new
- do not fail, if the caller didn't pick up response
from previous call (to please opensc)
- some scan-build errors fixed
v2.5.2
======
- remove libcacard.h usage warning (to silence qemu 2.5.0 build)
v2.5.1
======
- fix mingw cross-compilation
- add error checking to vcard_emul_options()
- add VCARD_DIRECT implementation to provide direct communication with
a smartcard using libpcsclite (--enable-pcsc)
- add a top-level libcacard.h header compatible with c++
- add test & code coverage and travis CI integration
- bug fix: delete the reader entry after queueing an event, not before
v2.5.0
======
This is the first standalone release after the split from qemu.git
(the last release of libcacard was part of qemu 2.4). The hosting and
maintainance of libcacard is now part of the Spice project.

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# libcacard [![pipeline status](https://gitlab.freedesktop.org/spice/libcacard/badges/master/pipeline.svg)](https://gitlab.freedesktop.org/spice/libcacard/commits/master) [![coverage report](https://gitlab.freedesktop.org/spice/libcacard/badges/master/coverage.svg)](https://gitlab.freedesktop.org/spice/libcacard/commits/master)
CAC (Common Access Card) library
This library provides emulation of smart cards to a virtual card
reader running in a guest virtual machine.
It implements DoD CAC standard with separate pki containers
(compatible coolkey), using certificates read from NSS.
For more information and API documentation, read the docs/libcacard.txt file.
# History
This project used to be part of qemu until version 2.5. The history
has been preserved and it inherits the tags and version.
# Authors
This project was originally developped by:
- Alon Levy <alevy@redhat.com>
- Robert Relyea <rrelyea@redhat.com>
Extended to new GSC-IS 2.1 standard by:
- Jakub Jelen <jjelen@redhat.com>

1208
aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

348
build-aux/compile Executable file
View File

@ -0,0 +1,348 @@
#!/bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2016-01-11.22; # UTC
# Copyright (C) 1999-2017 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 | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.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: "UTC0"
# time-stamp-end: "; # UTC"
# End:

1476
build-aux/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1836
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=2016-01-11.22; # UTC
# Copyright (C) 1999-2017 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: "UTC0"
# time-stamp-end: "; # UTC"
# End:

225
build-aux/git-version-gen Executable file
View File

@ -0,0 +1,225 @@
#!/bin/sh
# Print a version string.
scriptversion=2012-12-31.23; # UTC
# Copyright (C) 2007-2013 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 3 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 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/>.
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
# It may be run two ways:
# - from a git repository in which the "git describe" command below
# produces useful output (thus requiring at least one signed tag)
# - from a non-git-repo directory containing a .tarball-version file, which
# presumes this script is invoked like "./git-version-gen .tarball-version".
# In order to use intra-version strings in your project, you will need two
# separate generated version string files:
#
# .tarball-version - present only in a distribution tarball, and not in
# a checked-out repository. Created with contents that were learned at
# the last time autoconf was run, and used by git-version-gen. Must not
# be present in either $(srcdir) or $(builddir) for git-version-gen to
# give accurate answers during normal development with a checked out tree,
# but must be present in a tarball when there is no version control system.
# Therefore, it cannot be used in any dependencies. GNUmakefile has
# hooks to force a reconfigure at distribution time to get the value
# correct, without penalizing normal development with extra reconfigures.
#
# .version - present in a checked-out repository and in a distribution
# tarball. Usable in dependencies, particularly for files that don't
# want to depend on config.h but do want to track version changes.
# Delete this file prior to any autoconf run where you want to rebuild
# files to pick up a version string change; and leave it stale to
# minimize rebuild time after unrelated changes to configure sources.
#
# As with any generated file in a VC'd directory, you should add
# /.version to .gitignore, so that you don't accidentally commit it.
# .tarball-version is never generated in a VC'd directory, so needn't
# be listed there.
#
# Use the following line in your configure.ac, so that $(VERSION) will
# automatically be up-to-date each time configure is run (and note that
# since configure.ac no longer includes a version string, Makefile rules
# should not depend on configure.ac for version updates).
#
# AC_INIT([GNU project],
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
# [bug-project@example])
#
# Then use the following lines in your Makefile.am, so that .version
# will be present for dependencies, and so that .version and
# .tarball-version will exist in distribution tarballs.
#
# EXTRA_DIST = $(top_srcdir)/.version
# BUILT_SOURCES = $(top_srcdir)/.version
# $(top_srcdir)/.version:
# echo $(VERSION) > $@-t && mv $@-t $@
# dist-hook:
# echo $(VERSION) > $(distdir)/.tarball-version
me=$0
version="git-version-gen $scriptversion
Copyright 2011 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
usage="\
Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT]
Print a version string.
Options:
--prefix prefix of git tags (default 'v')
--fallback fallback version to use if \"git --version\" fails
--help display this help and exit
--version output version information and exit
Running without arguments will suffice in most cases."
prefix=v
fallback=
while test $# -gt 0; do
case $1 in
--help) echo "$usage"; exit 0;;
--version) echo "$version"; exit 0;;
--prefix) shift; prefix="$1";;
--fallback) shift; fallback="$1";;
-*)
echo "$0: Unknown option '$1'." >&2
echo "$0: Try '--help' for more information." >&2
exit 1;;
*)
if test "x$tarball_version_file" = x; then
tarball_version_file="$1"
elif test "x$tag_sed_script" = x; then
tag_sed_script="$1"
else
echo "$0: extra non-option argument '$1'." >&2
exit 1
fi;;
esac
shift
done
if test "x$tarball_version_file" = x; then
echo "$usage"
exit 1
fi
tag_sed_script="${tag_sed_script:-s/x/x/}"
nl='
'
# Avoid meddling by environment variable of the same name.
v=
v_from_git=
# First see if there is a tarball-only version file.
# then try "git describe", then default.
if test -f $tarball_version_file
then
v=`cat $tarball_version_file` || v=
case $v in
*$nl*) v= ;; # reject multi-line output
[0-9]*) ;;
*) v= ;;
esac
test "x$v" = x \
&& echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2
fi
if test "x$v" != x
then
: # use $v
# Otherwise, if there is at least one git commit involving the working
# directory, and "git describe" output looks sensible, use that to
# derive a version string.
elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
&& v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \
|| git describe --abbrev=4 HEAD 2>/dev/null` \
&& v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
&& case $v in
$prefix[0-9]*) ;;
*) (exit 1) ;;
esac
then
# Is this a new git that lists number of commits since the last
# tag or the previous older version that did not?
# Newer: v6.10-77-g0f8faeb
# Older: v6.10-g0f8faeb
case $v in
*-*-*) : git describe is okay three part flavor ;;
*-*)
: git describe is older two part flavor
# Recreate the number of commits and rewrite such that the
# result is the same as if we were using the newer version
# of git describe.
vtag=`echo "$v" | sed 's/-.*//'`
commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \
|| { commit_list=failed;
echo "$0: WARNING: git rev-list failed" 1>&2; }
numcommits=`echo "$commit_list" | wc -l`
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
test "$commit_list" = failed && v=UNKNOWN
;;
esac
# Change the first '-' to a '.', so version-comparing tools work properly.
# Remove the "g" in git describe's output string, to save a byte.
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
v_from_git=1
elif test "x$fallback" = x || git --version >/dev/null 2>&1; then
v=UNKNOWN
else
v=$fallback
fi
v=`echo "$v" |sed "s/^$prefix//"`
# Test whether to append the "-dirty" suffix only if the version
# string we're using came from git. I.e., skip the test if it's "UNKNOWN"
# or if it came from .tarball-version.
if test "x$v_from_git" != x; then
# Don't declare a version "dirty" merely because a time stamp has changed.
git update-index --refresh > /dev/null 2>&1
dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty=
case "$dirty" in
'') ;;
*) # Append the suffix only if there isn't one already.
case $v in
*-dirty) ;;
*) v="$v-dirty" ;;
esac ;;
esac
fi
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
echo "$v" | tr -d "$nl"
# 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:

150
build-aux/glib-tap.mk Normal file
View File

@ -0,0 +1,150 @@
# GLIB - Library of useful C routines
AM_TESTS_ENVIRONMENT= \
G_TEST_SRCDIR="$(abs_srcdir)/tests" \
G_TEST_BUILDDIR="$(abs_builddir)/tests" \
G_DEBUG="gc-friendly cleanup" \
MALLOC_CHECK_=2 \
MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
LOG_COMPILER = $(top_srcdir)/build-aux/tap-test
NULL =
# initialize variables for unconditional += appending
BUILT_SOURCES =
BUILT_EXTRA_DIST =
CLEANFILES = *.log *.trs
DISTCLEANFILES =
MAINTAINERCLEANFILES =
EXTRA_DIST =
TESTS =
installed_test_LTLIBRARIES =
installed_test_PROGRAMS =
installed_test_SCRIPTS =
nobase_installed_test_DATA =
noinst_LTLIBRARIES =
noinst_PROGRAMS =
noinst_SCRIPTS =
noinst_DATA =
check_LTLIBRARIES =
check_PROGRAMS =
check_SCRIPTS =
check_DATA =
# We support a fairly large range of possible variables. It is expected that all types of files in a test suite
# will belong in exactly one of the following variables.
#
# First, we support the usual automake suffixes, but in lowercase, with the customary meaning:
#
# test_programs, test_scripts, test_data, test_ltlibraries
#
# The above are used to list files that are involved in both uninstalled and installed testing. The
# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite.
# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is
# installed in the same way as it appears in the package layout.
#
# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled',
# like so:
#
# installed_test_programs, uninstalled_test_programs
# installed_test_scripts, uninstalled_test_scripts
# installed_test_data, uninstalled_test_data
# installed_test_ltlibraries, uninstalled_test_ltlibraries
#
# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts
# that should not themselves be run as testcases (but exist to be used from other testcases):
#
# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs
# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts
#
# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data
# file automatically end up in the tarball.
#
# dist_test_scripts, dist_test_data, dist_test_extra_scripts
# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts
# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts
#
# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the
# standard automake convention of not disting programs scripts or data by default.
#
# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted
# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under
# gtester. That's a bit strange for scripts, but it's possible.
TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \
$(dist_test_scripts) $(dist_uninstalled_test_scripts)
# Note: build even the installed-only targets during 'make check' to ensure that they still work.
# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to
# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were
# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'.
all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \
$(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs)
all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \
$(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts)
all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \
$(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts)
all_test_scripts += $(all_dist_test_scripts)
EXTRA_DIST += $(all_dist_test_scripts)
all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data)
all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data)
all_test_data += $(all_dist_test_data)
EXTRA_DIST += $(all_dist_test_data)
all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries)
if ENABLE_ALWAYS_BUILD_TESTS
noinst_LTLIBRARIES += $(all_test_ltlibs)
noinst_PROGRAMS += $(all_test_programs)
noinst_SCRIPTS += $(all_test_scripts)
noinst_DATA += $(all_test_data)
else
check_LTLIBRARIES += $(all_test_ltlibs)
check_PROGRAMS += $(all_test_programs)
check_SCRIPTS += $(all_test_scripts)
check_DATA += $(all_test_data)
endif
if ENABLE_INSTALLED_TESTS
installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \
$(test_extra_programs) $(installed_test_extra_programs)
installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \
$(test_extra_scripts) $(test_installed_extra_scripts)
installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \
$(dist_installed_test_scripts) $(dist_installed_test_extra_scripts)
nobase_installed_test_DATA += $(test_data) $(installed_test_data)
nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data)
installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries)
installed_testcases = $(test_programs) $(installed_test_programs) \
$(test_scripts) $(installed_test_scripts) \
$(dist_test_scripts) $(dist_installed_test_scripts)
installed_test_meta_DATA = $(installed_testcases:=.test)
%.test: %$(EXEEXT) Makefile
$(AM_V_GEN) (echo '[Test]' > $@.tmp; \
echo 'Type=session' >> $@.tmp; \
echo 'Exec=$(installed_testdir)/$(notdir $<) --tap' >> $@.tmp; \
echo 'Output=TAP' >> $@.tmp; \
mv $@.tmp $@)
CLEANFILES += $(installed_test_meta_DATA)
endif
VALGRIND_ARGS = \
--leak-check=full \
--child-silent-after-fork=yes \
--suppressions=/usr/share/glib-2.0/valgrind/glib.supp \
--suppressions=$(abs_top_srcdir)/build-aux/nss.supp \
--num-callers=18 \
$(NULL)
memcheck-local: $(all_test_programs)
$(MAKE) check-am TESTS="$(all_test_programs)" \
TESTS_ENVIRONMENT="G_DEBUG='gc-friendly cleanup'" \
LOG_FLAGS="libtool --mode=execute valgrind $(VALGRIND_ARGS) --quiet --log-fd=7" \
AM_TESTS_FD_REDIRECT="7>&2"

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

@ -0,0 +1,501 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2016-01-11.22; # 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.
;;
*)
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
if (umask $mkdir_umask &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
ls_ld_tmpdir=`ls -ld "$tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/d" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
fi
trap '' 0;;
esac;;
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: "UTC0"
# time-stamp-end: "; # UTC"
# End:

11149
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=2016-01-11.22; # UTC
# Copyright (C) 1996-2017 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: "UTC0"
# time-stamp-end: "; # UTC"
# End:

652
build-aux/tap-driver.sh Executable file
View File

@ -0,0 +1,652 @@
#! /bin/sh
# Copyright (C) 2011-2013 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>.
scriptversion=2011-12-27.17; # UTC
# Make unconditional expansion of undefined variables an error. This
# helps a lot in preventing typo-related bugs.
set -u
me=tap-driver.sh
fatal ()
{
echo "$me: fatal: $*" >&2
exit 1
}
usage_error ()
{
echo "$me: $*" >&2
print_usage >&2
exit 2
}
print_usage ()
{
cat <<END
Usage:
tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH
[--expect-failure={yes|no}] [--color-tests={yes|no}]
[--enable-hard-errors={yes|no}] [--ignore-exit]
[--diagnostic-string=STRING] [--merge|--no-merge]
[--comments|--no-comments] [--] TEST-COMMAND
The \`--test-name', \`--log-file' and \`--trs-file' options are mandatory.
END
}
# TODO: better error handling in option parsing (in particular, ensure
# TODO: $log_file, $trs_file and $test_name are defined).
test_name= # Used for reporting.
log_file= # Where to save the result and output of the test script.
trs_file= # Where to save the metadata of the test run.
expect_failure=0
color_tests=0
merge=0
ignore_exit=0
comments=0
diag_string='#'
while test $# -gt 0; do
case $1 in
--help) print_usage; exit $?;;
--version) echo "$me $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) shift;; # No-op.
--merge) merge=1;;
--no-merge) merge=0;;
--ignore-exit) ignore_exit=1;;
--comments) comments=1;;
--no-comments) comments=0;;
--diagnostic-string) diag_string=$2; shift;;
--) shift; break;;
-*) usage_error "invalid option: '$1'";;
esac
shift
done
test $# -gt 0 || usage_error "missing test command"
case $expect_failure in
yes) expect_failure=1;;
*) expect_failure=0;;
esac
if test $color_tests = yes; then
init_colors='
color_map["red"]="" # Red.
color_map["grn"]="" # Green.
color_map["lgn"]="" # Light green.
color_map["blu"]="" # Blue.
color_map["mgn"]="" # Magenta.
color_map["std"]="" # No color.
color_for_result["ERROR"] = "mgn"
color_for_result["PASS"] = "grn"
color_for_result["XPASS"] = "red"
color_for_result["FAIL"] = "red"
color_for_result["XFAIL"] = "lgn"
color_for_result["SKIP"] = "blu"'
else
init_colors=''
fi
# :; is there to work around a bug in bash 3.2 (and earlier) which
# does not always set '$?' properly on redirection failure.
# See the Autoconf manual for more details.
:;{
(
# Ignore common signals (in this subshell only!), to avoid potential
# problems with Korn shells. Some Korn shells are known to propagate
# to themselves signals that have killed a child process they were
# waiting for; this is done at least for SIGINT (and usually only for
# it, in truth). Without the `trap' below, such a behaviour could
# cause a premature exit in the current subshell, e.g., in case the
# test command it runs gets terminated by a SIGINT. Thus, the awk
# script we are piping into would never seen the exit status it
# expects on its last input line (which is displayed below by the
# last `echo $?' statement), and would thus die reporting an internal
# error.
# For more information, see the Autoconf manual and the threads:
# <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
# <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
trap : 1 3 2 13 15
if test $merge -gt 0; then
exec 2>&1
else
exec 2>&3
fi
"$@"
echo $?
) | LC_ALL=C ${AM_TAP_AWK-awk} \
-v me="$me" \
-v test_script_name="$test_name" \
-v log_file="$log_file" \
-v trs_file="$trs_file" \
-v expect_failure="$expect_failure" \
-v merge="$merge" \
-v ignore_exit="$ignore_exit" \
-v comments="$comments" \
-v diag_string="$diag_string" \
'
# FIXME: the usages of "cat >&3" below could be optimized when using
# FIXME: GNU awk, and/on on systems that supports /dev/fd/.
# Implementation note: in what follows, `result_obj` will be an
# associative array that (partly) simulates a TAP result object
# from the `TAP::Parser` perl module.
## ----------- ##
## FUNCTIONS ##
## ----------- ##
function fatal(msg)
{
print me ": " msg | "cat >&2"
exit 1
}
function abort(where)
{
fatal("internal error " where)
}
# Convert a boolean to a "yes"/"no" string.
function yn(bool)
{
return bool ? "yes" : "no";
}
function add_test_result(result)
{
if (!test_results_index)
test_results_index = 0
test_results_list[test_results_index] = result
test_results_index += 1
test_results_seen[result] = 1;
}
# Whether the test script should be re-run by "make recheck".
function must_recheck()
{
for (k in test_results_seen)
if (k != "XFAIL" && k != "PASS" && k != "SKIP")
return 1
return 0
}
# Whether the content of the log file associated to this test should
# be copied into the "global" test-suite.log.
function copy_in_global_log()
{
for (k in test_results_seen)
if (k != "PASS")
return 1
return 0
}
# FIXME: this can certainly be improved ...
function get_global_test_result()
{
if ("ERROR" in test_results_seen)
return "ERROR"
if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
return "FAIL"
all_skipped = 1
for (k in test_results_seen)
if (k != "SKIP")
all_skipped = 0
if (all_skipped)
return "SKIP"
return "PASS";
}
function stringify_result_obj(result_obj)
{
if (result_obj["is_unplanned"] || result_obj["number"] != testno)
return "ERROR"
if (plan_seen == LATE_PLAN)
return "ERROR"
if (result_obj["directive"] == "TODO")
return result_obj["is_ok"] ? "XPASS" : "XFAIL"
if (result_obj["directive"] == "SKIP")
return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
if (length(result_obj["directive"]))
abort("in function stringify_result_obj()")
return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
}
function decorate_result(result)
{
color_name = color_for_result[result]
if (color_name)
return color_map[color_name] "" result "" color_map["std"]
# If we are not using colorized output, or if we do not know how
# to colorize the given result, we should return it unchanged.
return result
}
function report(result, details)
{
if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/)
{
msg = ": " test_script_name
add_test_result(result)
}
else if (result == "#")
{
msg = " " test_script_name ":"
}
else
{
abort("in function report()")
}
if (length(details))
msg = msg " " details
# Output on console might be colorized.
print decorate_result(result) msg
# Log the result in the log file too, to help debugging (this is
# especially true when said result is a TAP error or "Bail out!").
print result msg | "cat >&3";
}
function testsuite_error(error_message)
{
report("ERROR", "- " error_message)
}
function handle_tap_result()
{
details = result_obj["number"];
if (length(result_obj["description"]))
details = details " " result_obj["description"]
if (plan_seen == LATE_PLAN)
{
details = details " # AFTER LATE PLAN";
}
else if (result_obj["is_unplanned"])
{
details = details " # UNPLANNED";
}
else if (result_obj["number"] != testno)
{
details = sprintf("%s # OUT-OF-ORDER (expecting %d)",
details, testno);
}
else if (result_obj["directive"])
{
details = details " # " result_obj["directive"];
if (length(result_obj["explanation"]))
details = details " " result_obj["explanation"]
}
report(stringify_result_obj(result_obj), details)
}
# `skip_reason` should be empty whenever planned > 0.
function handle_tap_plan(planned, skip_reason)
{
planned += 0 # Avoid getting confused if, say, `planned` is "00"
if (length(skip_reason) && planned > 0)
abort("in function handle_tap_plan()")
if (plan_seen)
{
# Error, only one plan per stream is acceptable.
testsuite_error("multiple test plans")
return;
}
planned_tests = planned
# The TAP plan can come before or after *all* the TAP results; we speak
# respectively of an "early" or a "late" plan. If we see the plan line
# after at least one TAP result has been seen, assume we have a late
# plan; in this case, any further test result seen after the plan will
# be flagged as an error.
plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN)
# If testno > 0, we have an error ("too many tests run") that will be
# automatically dealt with later, so do not worry about it here. If
# $plan_seen is true, we have an error due to a repeated plan, and that
# has already been dealt with above. Otherwise, we have a valid "plan
# with SKIP" specification, and should report it as a particular kind
# of SKIP result.
if (planned == 0 && testno == 0)
{
if (length(skip_reason))
skip_reason = "- " skip_reason;
report("SKIP", skip_reason);
}
}
function extract_tap_comment(line)
{
if (index(line, diag_string) == 1)
{
# Strip leading `diag_string` from `line`.
line = substr(line, length(diag_string) + 1)
# And strip any leading and trailing whitespace left.
sub("^[ \t]*", "", line)
sub("[ \t]*$", "", line)
# Return what is left (if any).
return line;
}
return "";
}
# When this function is called, we know that line is a TAP result line,
# so that it matches the (perl) RE "^(not )?ok\b".
function setup_result_obj(line)
{
# Get the result, and remove it from the line.
result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0)
sub("^(not )?ok[ \t]*", "", line)
# If the result has an explicit number, get it and strip it; otherwise,
# automatically assing the next progresive number to it.
if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
{
match(line, "^[0-9]+")
# The final `+ 0` is to normalize numbers with leading zeros.
result_obj["number"] = substr(line, 1, RLENGTH) + 0
line = substr(line, RLENGTH + 1)
}
else
{
result_obj["number"] = testno
}
if (plan_seen == LATE_PLAN)
# No further test results are acceptable after a "late" TAP plan
# has been seen.
result_obj["is_unplanned"] = 1
else if (plan_seen && testno > planned_tests)
result_obj["is_unplanned"] = 1
else
result_obj["is_unplanned"] = 0
# Strip trailing and leading whitespace.
sub("^[ \t]*", "", line)
sub("[ \t]*$", "", line)
# This will have to be corrected if we have a "TODO"/"SKIP" directive.
result_obj["description"] = line
result_obj["directive"] = ""
result_obj["explanation"] = ""
if (index(line, "#") == 0)
return # No possible directive, nothing more to do.
# Directives are case-insensitive.
rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*"
# See whether we have the directive, and if yes, where.
pos = match(line, rx "$")
if (!pos)
pos = match(line, rx "[^a-zA-Z0-9_]")
# If there was no TAP directive, we have nothing more to do.
if (!pos)
return
# Let`s now see if the TAP directive has been escaped. For example:
# escaped: ok \# SKIP
# not escaped: ok \\# SKIP
# escaped: ok \\\\\# SKIP
# not escaped: ok \ # SKIP
if (substr(line, pos, 1) == "#")
{
bslash_count = 0
for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
bslash_count += 1
if (bslash_count % 2)
return # Directive was escaped.
}
# Strip the directive and its explanation (if any) from the test
# description.
result_obj["description"] = substr(line, 1, pos - 1)
# Now remove the test description from the line, that has been dealt
# with already.
line = substr(line, pos)
# Strip the directive, and save its value (normalized to upper case).
sub("^[ \t]*#[ \t]*", "", line)
result_obj["directive"] = toupper(substr(line, 1, 4))
line = substr(line, 5)
# Now get the explanation for the directive (if any), with leading
# and trailing whitespace removed.
sub("^[ \t]*", "", line)
sub("[ \t]*$", "", line)
result_obj["explanation"] = line
}
function get_test_exit_message(status)
{
if (status == 0)
return ""
if (status !~ /^[1-9][0-9]*$/)
abort("getting exit status")
if (status < 127)
exit_details = ""
else if (status == 127)
exit_details = " (command not found?)"
else if (status >= 128 && status <= 255)
exit_details = sprintf(" (terminated by signal %d?)", status - 128)
else if (status > 256 && status <= 384)
# We used to report an "abnormal termination" here, but some Korn
# shells, when a child process die due to signal number n, can leave
# in $? an exit status of 256+n instead of the more standard 128+n.
# Apparently, both behaviours are allowed by POSIX (2008), so be
# prepared to handle them both. See also Austing Group report ID
# 0000051 <http://www.austingroupbugs.net/view.php?id=51>
exit_details = sprintf(" (terminated by signal %d?)", status - 256)
else
# Never seen in practice.
exit_details = " (abnormal termination)"
return sprintf("exited with status %d%s", status, exit_details)
}
function write_test_results()
{
print ":global-test-result: " get_global_test_result() > trs_file
print ":recheck: " yn(must_recheck()) > trs_file
print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file
for (i = 0; i < test_results_index; i += 1)
print ":test-result: " test_results_list[i] > trs_file
close(trs_file);
}
BEGIN {
## ------- ##
## SETUP ##
## ------- ##
'"$init_colors"'
# Properly initialized once the TAP plan is seen.
planned_tests = 0
COOKED_PASS = expect_failure ? "XPASS": "PASS";
COOKED_FAIL = expect_failure ? "XFAIL": "FAIL";
# Enumeration-like constants to remember which kind of plan (if any)
# has been seen. It is important that NO_PLAN evaluates "false" as
# a boolean.
NO_PLAN = 0
EARLY_PLAN = 1
LATE_PLAN = 2
testno = 0 # Number of test results seen so far.
bailed_out = 0 # Whether a "Bail out!" directive has been seen.
# Whether the TAP plan has been seen or not, and if yes, which kind
# it is ("early" is seen before any test result, "late" otherwise).
plan_seen = NO_PLAN
## --------- ##
## PARSING ##
## --------- ##
is_first_read = 1
while (1)
{
# Involutions required so that we are able to read the exit status
# from the last input line.
st = getline
if (st < 0) # I/O error.
fatal("I/O error while reading from input stream")
else if (st == 0) # End-of-input
{
if (is_first_read)
abort("in input loop: only one input line")
break
}
if (is_first_read)
{
is_first_read = 0
nextline = $0
continue
}
else
{
curline = nextline
nextline = $0
$0 = curline
}
# Copy any input line verbatim into the log file.
print | "cat >&3"
# Parsing of TAP input should stop after a "Bail out!" directive.
if (bailed_out)
continue
# TAP test result.
if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/)
{
testno += 1
setup_result_obj($0)
handle_tap_result()
}
# TAP plan (normal or "SKIP" without explanation).
else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/)
{
# The next two lines will put the number of planned tests in $0.
sub("^1\\.\\.", "")
sub("[^0-9]*$", "")
handle_tap_plan($0, "")
continue
}
# TAP "SKIP" plan, with an explanation.
else if ($0 ~ /^1\.\.0+[ \t]*#/)
{
# The next lines will put the skip explanation in $0, stripping
# any leading and trailing whitespace. This is a little more
# tricky in truth, since we want to also strip a potential leading
# "SKIP" string from the message.
sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "")
sub("[ \t]*$", "");
handle_tap_plan(0, $0)
}
# "Bail out!" magic.
# Older versions of prove and TAP::Harness (e.g., 3.17) did not
# recognize a "Bail out!" directive when preceded by leading
# whitespace, but more modern versions (e.g., 3.23) do. So we
# emulate the latter, "more modern" behaviour.
else if ($0 ~ /^[ \t]*Bail out!/)
{
bailed_out = 1
# Get the bailout message (if any), with leading and trailing
# whitespace stripped. The message remains stored in `$0`.
sub("^[ \t]*Bail out![ \t]*", "");
sub("[ \t]*$", "");
# Format the error message for the
bailout_message = "Bail out!"
if (length($0))
bailout_message = bailout_message " " $0
testsuite_error(bailout_message)
}
# Maybe we have too look for dianogtic comments too.
else if (comments != 0)
{
comment = extract_tap_comment($0);
if (length(comment))
report("#", comment);
}
}
## -------- ##
## FINISH ##
## -------- ##
# A "Bail out!" directive should cause us to ignore any following TAP
# error, as well as a non-zero exit status from the TAP producer.
if (!bailed_out)
{
if (!plan_seen)
{
testsuite_error("missing test plan")
}
else if (planned_tests != testno)
{
bad_amount = testno > planned_tests ? "many" : "few"
testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
bad_amount, planned_tests, testno))
}
if (!ignore_exit)
{
# Fetch exit status from the last line.
exit_message = get_test_exit_message(nextline)
if (exit_message)
testsuite_error(exit_message)
}
}
write_test_results()
exit 0
} # End of "BEGIN" block.
'
# TODO: document that we consume the file descriptor 3 :-(
} 3>"$log_file"
test $? -eq 0 || fatal "I/O or internal error"
# 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:

5
build-aux/tap-test Executable file
View File

@ -0,0 +1,5 @@
#! /bin/sh
# run a GTest in tap mode. The test binary is passed as $1
$@ -k --tap

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

@ -0,0 +1,148 @@
#!/bin/sh
# test-driver - basic testsuite driver script.
scriptversion=2016-01-11.22; # UTC
# Copyright (C) 2011-2017 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: "UTC0"
# time-stamp-end: "; # UTC"
# End:

70
config.h.in Normal file
View File

@ -0,0 +1,70 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define if debugging is enabled */
#undef DEBUG
/* pcsc support */
#undef ENABLE_PCSC
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* 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 <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 <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 <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Define if debugging is disabled */
#undef NDEBUG
/* 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
/* Version number of package */
#undef VERSION

17189
configure vendored Executable file

File diff suppressed because it is too large Load Diff

75
configure.ac Normal file
View File

@ -0,0 +1,75 @@
AC_INIT([libcacard], [m4_esyscmd(build-aux/git-version-gen .tarball-version)],
[spice-devel@lists.freedesktop.org])
AX_IS_RELEASE([git-directory])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([foreign subdir-objects no-dist-gzip dist-xz])
AM_MAINTAINER_MODE([enable])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AC_CANONICAL_HOST
AC_MSG_CHECKING([for native Win32])
case "$host_os" in
*mingw*|*cygwin*)
os_win32=yes
;;
*)
os_win32=no
;;
esac
AC_MSG_RESULT([$os_win32])
AM_CONDITIONAL([OS_WIN32],[test "$os_win32" = "yes"])
AX_CHECK_ENABLE_DEBUG([yes],[DEBUG])
AC_PROG_CC
AM_PROG_CC_C_O
LT_INIT([disable-static win32-dll])
PKG_PROG_PKG_CONFIG
AX_COMPILER_FLAGS([WARN_CFLAGS],[WARN_LDFLAGS])
AX_CODE_COVERAGE()
PKG_CHECK_MODULES(CACARD, [glib-2.0 >= 2.22 nss >= 3.12.8])
PKG_CHECK_MODULES(GTHREAD, [gthread-2.0])
dnl === --enable-pcsc ==========================================================
AC_ARG_ENABLE([pcsc],
AS_HELP_STRING([--disable-pcsc],
[do not build passthrough support]),,
[enable_pcsc=auto])
if test "x$enable_pcsc" != "xno"; then
PKG_CHECK_MODULES(PCSC, [libpcsclite], [have_pcsc=yes], [have_pcsc=no])
if test "x$have_pcsc" = "xno" -a "x$enable_pcsc" = "xyes"; then
AC_MSG_ERROR([pcsc support explicitly requested, but libpcsclite couldn't be found])
fi
if test "x$have_pcsc" = "xyes"; then
enable_pcsc=yes
AC_DEFINE([ENABLE_PCSC], 1, [pcsc support])
else
enable_pcsc=no
fi
fi
AM_CONDITIONAL(ENABLE_PCSC, test "x$enable_pcsc" = "xyes")
GLIB_TESTS
AC_CONFIG_FILES([
Makefile
src/Makefile
libcacard.pc
])
AC_OUTPUT
AC_MSG_NOTICE([
libcacard - $VERSION
• Prefix: $prefix
• PCSC enabled: $enable_pcsc
• Code coverage: $enable_code_coverage
])

504
docs/libcacard.txt Normal file
View File

@ -0,0 +1,504 @@
This file documents the CAC (Common Access Card) library in the libcacard
subdirectory.
Virtual Smart Card Emulator
This emulator is designed to provide emulation of actual smart cards to a
virtual card reader running in a guest virtual machine. The emulated smart
cards can be representations of real smart cards, where the necessary functions
such as signing, card removal/insertion, etc. are mapped to real, physical
cards which are shared with the client machine the emulator is running on, or
the cards could be pure software constructs.
The emulator is structured to allow multiple replaceable or additional pieces,
so it can be easily modified for future requirements. The primary envisioned
modifications are:
1) The socket connection to the virtual card reader (presumably a CCID reader,
but other ISO-7816 compatible readers could be used). The code that handles
this is in vscclient.c.
2) The virtual card low level emulation. This is currently supplied by using
NSS. This emulation could be replaced by implementations based on other
security libraries, including but not limitted to openssl+pkcs#11 library,
raw pkcs#11, Microsoft CAPI, direct opensc calls, etc. The code that handles
this is in vcard_emul_nss.c.
3) Emulation for new types of cards. The current implementation emulates the
original DoD CAC standard with separate pki containers. This emulator lives in
cac.c. More than one card type emulator could be included. Other cards could
be emulated as well, including PIV, newer versions of CAC, PKCS #15, etc.
--------------------
Replacing the Socket Based Virtual Reader Interface.
The current implementation contains a replaceable module vscclient.c. The
current vscclient.c implements a sockets interface to the virtual ccid reader
on the guest. CCID commands that are pertinent to emulation are passed
across the socket, and their responses are passed back along that same socket.
The protocol that vscclient uses is defined in vscard_common.h and connects
to a qemu ccid usb device. Since this socket runs as a client, vscclient.c
implements a program with a main entry. It also handles argument parsing for
the emulator.
An application that wants to use the virtual reader can replace vscclient.c
with its own implementation that connects to its own CCID reader. The calls
that the CCID reader can call are:
VReaderList * vreader_get_reader_list();
This function returns a list of virtual readers. These readers may map to
physical devices, or simulated devices depending on vcard the back end. Each
reader in the list should represent a reader to the virtual machine. Virtual
USB address mapping is left to the CCID reader front end. This call can be
made any time to get an updated list. The returned list is a copy of the
internal list that can be referenced by the caller without locking. This copy
must be freed by the caller with vreader_list_delete when it is no longer
needed.
VReaderListEntry *vreader_list_get_first(VReaderList *);
This function gets the first entry on the reader list. Along with
vreader_list_get_next(), vreader_list_get_first() can be used to walk the
reader list returned from vreader_get_reader_list(). VReaderListEntries are
part of the list themselves and do not need to be freed separately from the
list. If there are no entries on the list, it will return NULL.
VReaderListEntry *vreader_list_get_next(VReaderListEntry *);
This function gets the next entry in the list. If there are no more entries
it will return NULL.
VReader * vreader_list_get_reader(VReaderListEntry *)
This function returns the reader stored in the reader List entry. Caller gets
a new reference to a reader. The caller must free its reference when it is
finished with vreader_free().
void vreader_free(VReader *reader);
This function frees a reference to a reader. Readers are reference counted
and are automatically deleted when the last reference is freed.
void vreader_list_delete(VReaderList *list);
This function frees the list, all the elements on the list, and all the
reader references held by the list.
VReaderStatus vreader_power_on(VReader *reader, char *atr, int *len);
This function simulates a card power on. A virtual card does not care about
the actual voltage and other physical parameters, but it does care that the
card is actually on or off. Cycling the card causes the card to reset. If
the caller provides enough space, vreader_power_on will return the ATR of
the virtual card. The amount of space provided in atr should be indicated
in *len. The function modifies *len to be the actual length of of the
returned ATR.
VReaderStatus vreader_power_off(VReader *reader);
This function simulates a power off of a virtual card.
VReaderStatus vreader_xfer_bytes(VReader *reader, unsigne char *send_buf,
int send_buf_len,
unsigned char *receive_buf,
int receive_buf_len);
This function sends a raw apdu to a card and returns the card's response.
The CCID front end should return the response back. Most of the emulation
is driven from these APDUs.
VReaderStatus vreader_card_is_present(VReader *reader);
This function returns whether or not the reader has a card inserted. The
vreader_power_on, vreader_power_off, and vreader_xfer_bytes will return
VREADER_NO_CARD.
const char *vreader_get_name(VReader *reader);
This function returns the name of the reader. The name comes from the card
emulator level and is usually related to the name of the physical reader.
VReaderID vreader_get_id(VReader *reader);
This function returns the id of a reader. All readers start out with an id
of -1. The application can set the id with vreader_set_id.
VReaderStatus vreader_get_id(VReader *reader, VReaderID id);
This function sets the reader id. The application is responsible for making
sure that the id is unique for all readers it is actively using.
VReader *vreader_find_reader_by_id(VReaderID id);
This function returns the reader which matches the id. If two readers match,
only one is returned. The function returns NULL if the id is -1.
Event *vevent_wait_next_vevent();
This function blocks waiting for reader and card insertion events. There
will be one event for each card insertion, each card removal, each reader
insertion and each reader removal. At start up, events are created for all
the initial readers found, as well as all the cards that are inserted.
Event *vevent_get_next_vevent();
This function returns a pending event if it exists, otherwise it returns
NULL. It does not block.
----------------
Card Type Emulator: Adding a New Virtual Card Type
The ISO 7816 card spec describes 2 types of cards:
1) File system cards, where the smartcard is managed by reading and writing
data to files in a file system. There is currently only boiler plate
implemented for file system cards.
2) VM cards, where the card has loadable applets which perform the card
functions. The current implementation supports VM cards.
In the case of VM cards, the difference between various types of cards is
really what applets have been installed in that card. This structure is
mirrored in card type emulators. The 7816 emulator already handles the basic
ISO 7186 commands. Card type emulators simply need to add the virtual applets
which emulate the real card applets. Card type emulators have exactly one
public entry point:
VCARDStatus xxx_card_init(VCard *card, const char *flags,
const unsigned char *cert[],
int cert_len[],
VCardKey *key[],
int cert_count);
The parameters for this are:
card - the virtual card structure which will represent this card.
flags - option flags that may be specific to this card type.
cert - array of binary certificates.
cert_len - array of lengths of each of the certificates specified in cert.
key - array of opaque key structures representing the private keys on
the card.
cert_count - number of entries in cert, cert_len, and key arrays.
Any cert, cert_len, or key with the same index are matching sets. That is
cert[0] is cert_len[0] long and has the corresponding private key of key[0].
The card type emulator is expected to own the VCardKeys, but it should copy
any raw cert data it wants to save. It can create new applets and add them to
the card using the following functions:
VCardApplet *vcard_new_applet(VCardProcessAPDU apdu_func,
VCardResetApplet reset_func,
const unsigned char *aid,
int aid_len);
This function creates a new applet. Applet structures store the following
information:
1) the AID of the applet (set by aid and aid_len).
2) a function to handle APDUs for this applet. (set by apdu_func, more on
this below).
3) a function to reset the applet state when the applet is selected.
(set by reset_func, more on this below).
3) applet private data, a data pointer used by the card type emulator to
store any data or state it needs to complete requests. (set by a
separate call).
4) applet private data free, a function used to free the applet private
data when the applet itself is destroyed.
The created applet can be added to the card with vcard_add_applet below.
void vcard_set_applet_private(VCardApplet *applet,
VCardAppletPrivate *private,
VCardAppletPrivateFree private_free);
This function sets the private data and the corresponding free function.
VCardAppletPrivate is an opaque data structure to the rest of the emulator.
The card type emulator can define it any way it wants by defining
struct VCardAppletPrivateStruct {};. If there is already a private data
structure on the applet, the old one is freed before the new one is set up.
passing two NULL clear any existing private data.
VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
Add an applet onto the list of applets attached to the card. Once an applet
has been added, it can be selected by its AID, and then commands will be
routed to it VCardProcessAPDU function. This function adopts the applet that
is passed into it. Note: 2 applets with the same AID should not be added to
the same card. It is permissible to add more than one applet. Multiple applets
may have the same VCardPRocessAPDU entry point.
The certs and keys should be attached to private data associated with one or
more appropriate applets for that card. Control will come to the card type
emulators once one of its applets are selected through the VCardProcessAPDU
function it specified when it created the applet.
The signature of VCardResetApplet is:
VCardStatus (*VCardResetApplet) (VCard *card, int channel);
This function will reset the any internal applet state that needs to be
cleared after a select applet call. It should return VCARD_DONE;
The signature of VCardProcessAPDU is:
VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
VCardResponse **response);
This function examines the APDU and determines whether it should process
the apdu directly, reject the apdu as invalid, or pass the apdu on to
the basic 7816 emulator for processing.
If the 7816 emulator should process the apdu, then the VCardProcessAPDU
should return VCARD_NEXT.
If there is an error, then VCardProcessAPDU should return an error
response using vcard_make_response and the appropriate 7816 error code
(see card_7816t.h) or vcard_make_response with a card type specific error
code. It should then return VCARD_DONE.
If the apdu can be processed correctly, VCardProcessAPDU should do so,
set the response value appropriately for that APDU, and return VCARD_DONE.
VCardProcessAPDU should always set the response if it returns VCARD_DONE.
It should always either return VCARD_DONE or VCARD_NEXT.
Parsing the APDU --
Prior to processing calling the card type emulator's VCardProcessAPDU function, the emulator has already decoded the APDU header and set several fields:
apdu->a_data - The raw apdu data bytes.
apdu->a_len - The len of the raw apdu data.
apdu->a_body - The start of any post header parameter data.
apdu->a_Lc - The parameter length value.
apdu->a_Le - The expected length of any returned data.
apdu->a_cla - The raw apdu class.
apdu->a_channel - The channel (decoded from the class).
apdu->a_secure_messaging_type - The decoded secure messaging type
(from class).
apdu->a_type - The decode class type.
apdu->a_gen_type - the generic class type (7816, PROPRIETARY, RFU, PTS).
apdu->a_ins - The instruction byte.
apdu->a_p1 - Parameter 1.
apdu->a_p2 - Parameter 2.
Creating a Response --
The expected result of any APDU call is a response. The card type emulator must
set *response with an appropriate VCardResponse value if it returns VCARD_DONE.
Responses could be as simple as returning a 2 byte status word response, to as
complex as returning a block of data along with a 2 byte response. Which is
returned will depend on the semantics of the APDU. The following functions will
create card responses.
VCardResponse *vcard_make_response(VCard7816Status status);
This is the most basic function to get a response. This function will
return a response the consists solely one 2 byte status code. If that status
code is defined in card_7816t.h, then this function is guaranteed to
return a response with that status. If a cart type specific status code
is passed and vcard_make_response fails to allocate the appropriate memory
for that response, then vcard_make_response will return a VCardResponse
of VCARD7816_STATUS_EXC_ERROR_MEMORY. In any case, this function is
guaranteed to return a valid VCardResponse.
VCardResponse *vcard_response_new(unsigned char *buf, int len,
VCard7816Status status);
This function is similar to vcard_make_response except it includes some
returned data with the response. It could also fail to allocate enough
memory, in which case it will return NULL.
VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
unsigned char sw2);
Sometimes in 7816 the response bytes are treated as two separate bytes with
split meanings. This function allows you to create a response based on
two separate bytes. This function could fail, in which case it will return
NULL.
VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len,
unsigned char sw1,
unsigned char sw2);
This function is the same as vcard_response_new except you may specify
the status as two separate bytes like vcard_response_new_status_bytes.
Implementing functionality ---
The following helper functions access information about the current card
and applet.
VCARDAppletPrivate *vcard_get_current_applet_private(VCard *card,
int channel);
This function returns any private data set by the card type emulator on
the currently selected applet. The card type emulator keeps track of the
current applet state in this data structure. Any certs and keys associated
with a particular applet is also stored here.
int vcard_emul_get_login_count(VCard *card);
This function returns the number of remaining login attempts for this
card. If the card emulator does not know, or the card does not have a
way of giving this information, this function returns -1.
VCard7816Status vcard_emul_login(VCard *card, unsigned char *pin,
int pin_len);
This function logs into the card and returns the standard 7816 status
word depending on the success or failure of the call.
void vcard_emul_delete_key(VCardKey *key);
This function frees the VCardKey passed in to xxxx_card_init. The card
type emulator is responsible for freeing this key when it no longer needs
it.
VCard7816Status vcard_emul_rsa_op(VCard *card, VCardKey *key,
unsigned char *buffer,
int buffer_size);
This function does a raw rsa op on the buffer with the given key.
int vcard_emul_rsa_bits(VCardKey *key);
This function returns the size of RSA key in bits.
unsigned char *vcard_emul_read_object(VCard *card,
const unsigned char *label,
unsigned int *ret_len);
This function reads generic data from underlying smart card by the label,
if avaialble.
The sample card type emulator is found in cac.c. It implements the cac specific
applets. Only those applets needed by the coolkey pkcs#11 driver on the guest
have been implemented. To support the full range CAC middleware, a complete CAC
card according to the CAC specs should be implemented here.
------------------------------
Virtual Card Emulator
This code accesses both real smart cards and simulated smart cards through
services provided on the client. The current implementation uses NSS, which
already knows how to talk to various PKCS #11 modules on the client, and is
portable to most operating systems. A particular emulator can have only one
virtual card implementation at a time.
The virtual card emulator consists of a series of virtual card services. In
addition to the services describe above (services starting with
vcard_emul_xxxx), the virtual card emulator also provides the following
functions:
VCardEmulError vcard_emul_init(cont VCardEmulOptions *options);
The options structure is built by another function in the virtual card
interface where a string of virtual card emulator specific strings are
mapped to the options. The actual structure is defined by the virtual card
emulator and is used to determine the configuration of soft cards, or to
determine which physical cards to present to the guest.
The vcard_emul_init function will build up sets of readers, create any
threads that are needed to watch for changes in the reader state. If readers
have cards present in them, they are also initialized.
Readers are created with the function.
VReader *vreader_new(VReaderEmul *reader_emul,
VReaderEmulFree reader_emul_free);
The freeFunc is used to free the VReaderEmul * when the reader is
destroyed. The VReaderEmul structure is an opaque structure to the
rest of the code, but defined by the virtual card emulator, which can
use it to store any reader specific state.
Once the reader has been created, it can be added to the front end with the
call:
VReaderStatus vreader_add_reader(VReader *reader);
This function will automatically generate the appropriate new reader
events and add the reader to the list.
To create a new card, the virtual card emulator will call a similar
function.
VCard *vcard_new(VCardEmul *card_emul,
VCardEmulFree card_emul_free);
Like vreader_new, this function takes a virtual card emulator specific
structure which it uses to keep track of the card state.
Once the card is created, it is attached to a card type emulator with the
following function:
VCardStatus vcard_init(VCard *vcard, VCardEmulType type,
const char *flags,
unsigned char *const *certs,
int *cert_len,
VCardKey *key[],
int cert_count);
The vcard is the value returned from vcard_new. The type is the
card type emulator that this card should presented to the guest as.
The flags are card type emulator specific options. The certs,
cert_len, and keys are all arrays of length cert_count. These are
the same of the parameters xxxx_card_init() accepts.
Finally the card is associated with its reader by the call:
VReaderStatus vreader_insert_card(VReader *vreader, VCard *vcard);
This function, like vreader_add_reader, will take care of any event
notification for the card insert.
VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
Force a card that is present to appear to be removed to the guest, even if
that card is a physical card and is present.
VCardEmulError vcard_emul_force_card_insert(VReader *reader);
Force a card that has been removed by vcard_emul_force_card_remove to be
reinserted from the point of view of the guest. This will only work if the
card is physically present (which is always true fro a soft card).
void vcard_emul_get_atr(Vcard *card, unsigned char *atr, int *atr_len);
Return the virtual ATR for the card. By convention this should be the value
VCARD_ATR_PREFIX(size) followed by several ascii bytes related to this
particular emulator. For instance the NSS emulator returns
{VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }. Do not return more data then *atr_len;
void vcard_emul_reset(VCard *card, VCardPower power)
Set the state of 'card' to the current power level and reset its internal
state (logout, etc).
-------------------------------------------------------
List of files and their function:
docs/libcacard.txt - This file
src/card_7816.c - emulate basic 7816 functionality. Parse APDUs.
src/card_7816.h - apdu and response services definitions.
src/card_7816t.h - 7816 specific structures, types and definitions.
src/event.c - event handling code.
src/event.h - event handling services definitions.
src/eventt.h - event handling structures and types
src/vcard.c - handle common virtual card services like creation, destruction,
and applet management.
src/vcard.h - common virtual card services function definitions.
src/vcardt.h - comon virtual card types
src/vreader.c - common virtual reader services.
src/vreader.h - common virtual reader services definitions.
src/vreadert.h - comon virtual reader types.
src/vcard_emul_type.c - manage the card type emulators.
src/vcard_emul_type.h - definitions for card type emulators.
src/cac.c - card type emulator for CAC cards
src/cac-aca.c - implementation of CAC's ACA applet related buffers
src/gp.c - basic Global Platform card manager emulation
src/vcard_emul.h - virtual card emulator service definitions.
src/vcard_emul_nss.c - virtual card emulator implementation for nss.
src/vscclient.c - socket connection to guest qemu usb driver.
src/vscard_common.h - common header with the guest qemu usb driver.
src/mutex.h - header file for machine independent mutexes.
src/common.c - Utilities functions
src/common.h - header file utilities functions
src/simpletlv.c - Simple TLV encoding functions
src/simpletlv.h - header file for Simple TLV encoding helpers
tests/libcacard.c - Test for the whole smart card emulation
tests/simpletlv.c - Unit tests for SimpleTLV encoding and decoding functions
tests/hwtests.c - Tests intended to be ran against real card if available

12
libcacard.pc.in Normal file
View File

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: cacard
Description: CA Card library
Version: @PACKAGE_VERSION@
Requires: glib-2.0
Requires.private: nss
Libs: -L${libdir} -lcacard
Cflags: -I${includedir}/cacard

View File

@ -0,0 +1,67 @@
# ============================================================================
# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
# ============================================================================
#
# SYNOPSIS
#
# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# For every FLAG1, FLAG2 it is checked whether the compiler works with the
# flag. If it does, the flag is added FLAGS-VARIABLE
#
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
# CFLAGS) is used. During the check the flag is always added to the
# current language's flags.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: This macro depends on the AX_APPEND_FLAG and
# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
# AX_APPEND_LINK_FLAGS.
#
# LICENSE
#
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 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 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 <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
for flag in $1; do
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4])
done
])dnl AX_APPEND_COMPILE_FLAGS

71
m4/ax_append_flag.m4 Normal file
View File

@ -0,0 +1,71 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
#
# DESCRIPTION
#
# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
# added in between.
#
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
# FLAG.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 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 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 <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 7
AC_DEFUN([AX_APPEND_FLAG],
[dnl
AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
AS_VAR_SET_IF(FLAGS,[
AS_CASE([" AS_VAR_GET(FLAGS) "],
[*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
[
AS_VAR_APPEND(FLAGS,[" $1"])
AC_RUN_LOG([: FLAGS="$FLAGS"])
])
],
[
AS_VAR_SET(FLAGS,[$1])
AC_RUN_LOG([: FLAGS="$FLAGS"])
])
AS_VAR_POPDEF([FLAGS])dnl
])dnl AX_APPEND_FLAG

View File

@ -0,0 +1,65 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# For every FLAG1, FLAG2 it is checked whether the linker works with the
# flag. If it does, the flag is added FLAGS-VARIABLE
#
# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
# used. During the check the flag is always added to the linker's flags.
#
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
# when the check is done. The check is thus made with the flags: "LDFLAGS
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
# issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
#
# LICENSE
#
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 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 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 <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AC_DEFUN([AX_APPEND_LINK_FLAGS],
[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
for flag in $1; do
AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4])
done
])dnl AX_APPEND_LINK_FLAGS

View File

@ -0,0 +1,74 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the current language's compiler
# or gives an error. (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 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 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 <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 5
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS

124
m4/ax_check_enable_debug.m4 Normal file
View File

@ -0,0 +1,124 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE])
#
# DESCRIPTION
#
# Check for the presence of an --enable-debug option to configure, with
# the specified default value used when the option is not present. Return
# the value in the variable $ax_enable_debug.
#
# Specifying 'yes' adds '-g -O0' to the compilation flags for all
# languages. Specifying 'info' adds '-g' to the compilation flags.
# Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to
# the linking flags. Otherwise, nothing is added.
#
# Define the variables listed in the second argument if debug is enabled,
# defaulting to no variables. Defines the variables listed in the third
# argument if debug is disabled, defaulting to NDEBUG. All lists of
# variables should be space-separated.
#
# If debug is not enabled, ensure AC_PROG_* will not add debugging flags.
# Should be invoked prior to any AC_PROG_* compiler checks.
#
# IS-RELEASE can be used to change the default to 'no' when making a
# release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it
# uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE
# macro, there is no need to pass this parameter.
#
# AX_IS_RELEASE([git-directory])
# AX_CHECK_ENABLE_DEBUG()
#
# LICENSE
#
# Copyright (c) 2011 Rhys Ulerich <rhys.ulerich@gmail.com>
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
#
# 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.
#serial 8
AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[
AC_BEFORE([$0],[AC_PROG_CC])dnl
AC_BEFORE([$0],[AC_PROG_CXX])dnl
AC_BEFORE([$0],[AC_PROG_F77])dnl
AC_BEFORE([$0],[AC_PROG_FC])dnl
AC_MSG_CHECKING(whether to enable debugging)
ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1])))
ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],,
[$ax_is_release],
[$4])))
# If this is a release, override the default.
AS_IF([test "$ax_enable_debug_is_release" = "yes"],
[ax_enable_debug_default="no"])
m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))])
m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))])
AC_ARG_ENABLE(debug,
[AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])],
[],enable_debug=$ax_enable_debug_default)
# empty mean debug yes
AS_IF([test "x$enable_debug" = "x"],
[enable_debug="yes"])
# case of debug
AS_CASE([$enable_debug],
[yes],[
AC_MSG_RESULT(yes)
CFLAGS="${CFLAGS} -g -O0"
CXXFLAGS="${CXXFLAGS} -g -O0"
FFLAGS="${FFLAGS} -g -O0"
FCFLAGS="${FCFLAGS} -g -O0"
OBJCFLAGS="${OBJCFLAGS} -g -O0"
],
[info],[
AC_MSG_RESULT(info)
CFLAGS="${CFLAGS} -g"
CXXFLAGS="${CXXFLAGS} -g"
FFLAGS="${FFLAGS} -g"
FCFLAGS="${FCFLAGS} -g"
OBJCFLAGS="${OBJCFLAGS} -g"
],
[profile],[
AC_MSG_RESULT(profile)
CFLAGS="${CFLAGS} -g -pg"
CXXFLAGS="${CXXFLAGS} -g -pg"
FFLAGS="${FFLAGS} -g -pg"
FCFLAGS="${FCFLAGS} -g -pg"
OBJCFLAGS="${OBJCFLAGS} -g -pg"
LDFLAGS="${LDFLAGS} -pg"
],
[
AC_MSG_RESULT(no)
dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags
dnl by setting any unset environment flag variables
AS_IF([test "x${CFLAGS+set}" != "xset"],
[CFLAGS=""])
AS_IF([test "x${CXXFLAGS+set}" != "xset"],
[CXXFLAGS=""])
AS_IF([test "x${FFLAGS+set}" != "xset"],
[FFLAGS=""])
AS_IF([test "x${FCFLAGS+set}" != "xset"],
[FCFLAGS=""])
AS_IF([test "x${OBJCFLAGS+set}" != "xset"],
[OBJCFLAGS=""])
])
dnl Define various variables if debugging is disabled.
dnl assert.h is a NOP if NDEBUG is defined, so define it by default.
AS_IF([test "x$enable_debug" = "xyes"],
[m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])],
[m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])])
ax_enable_debug=$enable_debug
])

74
m4/ax_check_link_flag.m4 Normal file
View File

@ -0,0 +1,74 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the linker or gives an error.
# (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the linker's default flags
# when the check is done. The check is thus made with the flags: "LDFLAGS
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
# issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_LINK_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.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 3 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 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 <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 5
AC_DEFUN([AX_CHECK_LINK_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
ax_check_save_flags=$LDFLAGS
LDFLAGS="$LDFLAGS $4 $1"
AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
LDFLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_LINK_FLAGS

264
m4/ax_code_coverage.m4 Normal file
View File

@ -0,0 +1,264 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CODE_COVERAGE()
#
# DESCRIPTION
#
# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS,
# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included
# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every
# build target (program or library) which should be built with code
# coverage support. Also defines CODE_COVERAGE_RULES which should be
# substituted in your Makefile; and $enable_code_coverage which can be
# used in subsequent configure output. CODE_COVERAGE_ENABLED is defined
# and substituted, and corresponds to the value of the
# --enable-code-coverage option, which defaults to being disabled.
#
# Test also for gcov program and create GCOV variable that could be
# substituted.
#
# Note that all optimization flags in CFLAGS must be disabled when code
# coverage is enabled.
#
# Usage example:
#
# configure.ac:
#
# AX_CODE_COVERAGE
#
# Makefile.am:
#
# @CODE_COVERAGE_RULES@
# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ...
# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ...
# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ...
#
# This results in a "check-code-coverage" rule being added to any
# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
# has been configured with --enable-code-coverage). Running `make
# check-code-coverage` in that directory will run the module's test suite
# (`make check`) and build a code coverage report detailing the code which
# was touched, then print the URI for the report.
#
# In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined
# instead of CODE_COVERAGE_LIBS. They are both still defined, but use of
# CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is
# deprecated. They have the same value.
#
# This code was derived from Makefile.decl in GLib, originally licenced
# under LGPLv2.1+.
#
# LICENSE
#
# Copyright (c) 2012, 2016 Philip Withnall
# Copyright (c) 2012 Xan Lopez
# Copyright (c) 2012 Christian Persch
# Copyright (c) 2012 Paolo Borelli
# Copyright (c) 2012 Dan Winship
# Copyright (c) 2015 Bastien ROUCARIES
#
# 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 program. If not, see <https://www.gnu.org/licenses/>.
#serial 25
AC_DEFUN([AX_CODE_COVERAGE],[
dnl Check for --enable-code-coverage
AC_REQUIRE([AC_PROG_SED])
# allow to override gcov location
AC_ARG_WITH([gcov],
[AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
[_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
[_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
AC_MSG_CHECKING([whether to build with code coverage support])
AC_ARG_ENABLE([code-coverage],
AS_HELP_STRING([--enable-code-coverage],
[Whether to enable code coverage support]),,
enable_code_coverage=no)
AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
AC_MSG_RESULT($enable_code_coverage)
AS_IF([ test "$enable_code_coverage" = "yes" ], [
# check for gcov
AC_CHECK_TOOL([GCOV],
[$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
[:])
AS_IF([test "X$GCOV" = "X:"],
[AC_MSG_ERROR([gcov is needed to do coverage])])
AC_SUBST([GCOV])
dnl Check if gcc is being used
AS_IF([ test "$GCC" = "no" ], [
AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
])
AC_CHECK_PROG([LCOV], [lcov], [lcov])
AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
AS_IF([ test -z "$LCOV" ], [
AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed])
])
AS_IF([ test -z "$GENHTML" ], [
AC_MSG_ERROR([Could not find genhtml from the lcov package])
])
dnl Build the code coverage flags
dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility
CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
CODE_COVERAGE_LIBS="-lgcov"
CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS"
AC_SUBST([CODE_COVERAGE_CPPFLAGS])
AC_SUBST([CODE_COVERAGE_CFLAGS])
AC_SUBST([CODE_COVERAGE_CXXFLAGS])
AC_SUBST([CODE_COVERAGE_LIBS])
AC_SUBST([CODE_COVERAGE_LDFLAGS])
[CODE_COVERAGE_RULES_CHECK='
-$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check
$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
']
[CODE_COVERAGE_RULES_CAPTURE='
$(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS)
$(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
-@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
$(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
@echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
']
[CODE_COVERAGE_RULES_CLEAN='
clean: code-coverage-clean
distclean: code-coverage-clean
code-coverage-clean:
-$(LCOV) --directory $(top_builddir) -z
-rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
-find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete
']
], [
[CODE_COVERAGE_RULES_CHECK='
@echo "Need to reconfigure with --enable-code-coverage"
']
CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK"
CODE_COVERAGE_RULES_CLEAN=''
])
[CODE_COVERAGE_RULES='
# Code coverage
#
# Optional:
# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
# Multiple directories may be specified, separated by whitespace.
# (Default: $(top_builddir))
# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
# by lcov for code coverage. (Default:
# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
# reports to be created. (Default:
# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
# set to 0 to disable it and leave empty to stay with the default.
# (Default: empty)
# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
# lcov instance. (Default: empty)
# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
#
# The generated report will be titled using the $(PACKAGE_NAME) and
# $(PACKAGE_VERSION). In order to add the current git hash to the title,
# use the git-version-gen script, available online.
# Optional variables
CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
CODE_COVERAGE_BRANCH_COVERAGE ?=
CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
CODE_COVERAGE_IGNORE_PATTERN ?=
GITIGNOREFILES ?=
GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\
$(CODE_COVERAGE_OUTPUT_FILE);
code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\
$(CODE_COVERAGE_IGNORE_PATTERN);
code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
code_coverage_quiet = $(code_coverage_quiet_$(V))
code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
code_coverage_quiet_0 = --quiet
# sanitizes the test-name: replaces with underscores: dashes and dots
code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
# Use recursive makes in order to ignore errors during check
check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"'
# Capture code coverage data
code-coverage-capture: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"'
# Hook rule executed before code-coverage-capture, overridable by the user
code-coverage-capture-hook:
'"$CODE_COVERAGE_RULES_CLEAN"'
A''M_DISTCHECK_CONFIGURE_FLAGS ?=
A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
']
AC_SUBST([CODE_COVERAGE_RULES])
m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
])

158
m4/ax_compiler_flags.m4 Normal file
View File

@ -0,0 +1,158 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_COMPILER_FLAGS([CFLAGS-VARIABLE], [LDFLAGS-VARIABLE], [IS-RELEASE], [EXTRA-BASE-CFLAGS], [EXTRA-YES-CFLAGS], [UNUSED], [UNUSED], [UNUSED], [EXTRA-BASE-LDFLAGS], [EXTRA-YES-LDFLAGS], [UNUSED], [UNUSED], [UNUSED])
#
# DESCRIPTION
#
# Check for the presence of an --enable-compile-warnings option to
# configure, defaulting to "error" in normal operation, or "yes" if
# IS-RELEASE is equal to "yes". Return the value in the variable
# $ax_enable_compile_warnings.
#
# Depending on the value of --enable-compile-warnings, different compiler
# warnings are checked to see if they work with the current compiler and,
# if so, are appended to CFLAGS-VARIABLE and LDFLAGS-VARIABLE. This
# allows a consistent set of baseline compiler warnings to be used across
# a code base, irrespective of any warnings enabled locally by individual
# developers. By standardising the warnings used by all developers of a
# project, the project can commit to a zero-warnings policy, using -Werror
# to prevent compilation if new warnings are introduced. This makes
# catching bugs which are flagged by warnings a lot easier.
#
# By providing a consistent --enable-compile-warnings argument across all
# projects using this macro, continuous integration systems can easily be
# configured the same for all projects. Automated systems or build
# systems aimed at beginners may want to pass the --disable-Werror
# argument to unconditionally prevent warnings being fatal.
#
# --enable-compile-warnings can take the values:
#
# * no: Base compiler warnings only; not even -Wall.
# * yes: The above, plus a broad range of useful warnings.
# * error: The above, plus -Werror so that all warnings are fatal.
# Use --disable-Werror to override this and disable fatal
# warnings.
#
# The set of base and enabled flags can be augmented using the
# EXTRA-*-CFLAGS and EXTRA-*-LDFLAGS variables, which are tested and
# appended to the output variable if --enable-compile-warnings is not
# "no". Flags should not be disabled using these arguments, as the entire
# point of AX_COMPILER_FLAGS is to enforce a consistent set of useful
# compiler warnings on code, using warnings which have been chosen for low
# false positive rates. If a compiler emits false positives for a
# warning, a #pragma should be used in the code to disable the warning
# locally. See:
#
# https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas
#
# The EXTRA-* variables should only be used to supply extra warning flags,
# and not general purpose compiler flags, as they are controlled by
# configure options such as --disable-Werror.
#
# IS-RELEASE can be used to disable -Werror when making a release, which
# is useful for those hairy moments when you just want to get the release
# done as quickly as possible. Set it to "yes" to disable -Werror. By
# default, it uses the value of $ax_is_release, so if you are using the
# AX_IS_RELEASE macro, there is no need to pass this parameter. For
# example:
#
# AX_IS_RELEASE([git-directory])
# AX_COMPILER_FLAGS()
#
# CFLAGS-VARIABLE defaults to WARN_CFLAGS, and LDFLAGS-VARIABLE defaults
# to WARN_LDFLAGS. Both variables are AC_SUBST-ed by this macro, but must
# be manually added to the CFLAGS and LDFLAGS variables for each target in
# the code base.
#
# If C++ language support is enabled with AC_PROG_CXX, which must occur
# before this macro in configure.ac, warning flags for the C++ compiler
# are AC_SUBST-ed as WARN_CXXFLAGS, and must be manually added to the
# CXXFLAGS variables for each target in the code base. EXTRA-*-CFLAGS can
# be used to augment the base and enabled flags.
#
# Warning flags for g-ir-scanner (from GObject Introspection) are
# AC_SUBST-ed as WARN_SCANNERFLAGS. This variable must be manually added
# to the SCANNERFLAGS variable for each GIR target in the code base. If
# extra g-ir-scanner flags need to be enabled, the AX_COMPILER_FLAGS_GIR
# macro must be invoked manually.
#
# AX_COMPILER_FLAGS may add support for other tools in future, in addition
# to the compiler and linker. No extra EXTRA-* variables will be added
# for those tools, and all extra support will still use the single
# --enable-compile-warnings configure option. For finer grained control
# over the flags for individual tools, use AX_COMPILER_FLAGS_CFLAGS,
# AX_COMPILER_FLAGS_LDFLAGS and AX_COMPILER_FLAGS_* for new tools.
#
# The UNUSED variables date from a previous version of this macro, and are
# automatically appended to the preceding non-UNUSED variable. They should
# be left empty in new uses of the macro.
#
# LICENSE
#
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
# Copyright (c) 2015 David King <amigadave@amigadave.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 14
# _AX_COMPILER_FLAGS_LANG([LANGNAME])
m4_defun([_AX_COMPILER_FLAGS_LANG],
[m4_ifdef([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [],
[m4_define([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [])dnl
AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_]$1[FLAGS])])dnl
])
AC_DEFUN([AX_COMPILER_FLAGS],[
# C support is enabled by default.
_AX_COMPILER_FLAGS_LANG([C])
# Only enable C++ support if AC_PROG_CXX is called. The redefinition of
# AC_PROG_CXX is so that a fatal error is emitted if this macro is called
# before AC_PROG_CXX, which would otherwise cause no C++ warnings to be
# checked.
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AX_COMPILER_FLAGS_LANG([CXX])],
[m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AX_COMPILER_FLAGS_LANG([CXX])])])
AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_LDFLAGS])
# Default value for IS-RELEASE is $ax_is_release
ax_compiler_flags_is_release=m4_tolower(m4_normalize(ifelse([$3],,
[$ax_is_release],
[$3])))
AC_ARG_ENABLE([compile-warnings],
AS_HELP_STRING([--enable-compile-warnings=@<:@no/yes/error@:>@],
[Enable compiler warnings and errors]),,
[AS_IF([test "$ax_compiler_flags_is_release" = "yes"],
[enable_compile_warnings="yes"],
[enable_compile_warnings="error"])])
AC_ARG_ENABLE([Werror],
AS_HELP_STRING([--disable-Werror],
[Unconditionally make all compiler warnings non-fatal]),,
[enable_Werror=maybe])
# Return the user's chosen warning level
AS_IF([test "$enable_Werror" = "no" -a \
"$enable_compile_warnings" = "error"],[
enable_compile_warnings="yes"
])
ax_enable_compile_warnings=$enable_compile_warnings
AX_COMPILER_FLAGS_CFLAGS([$1],[$ax_compiler_flags_is_release],
[$4],[$5 $6 $7 $8])
m4_ifdef([_AX_COMPILER_FLAGS_LANG_CXX_enabled],
[AX_COMPILER_FLAGS_CXXFLAGS([WARN_CXXFLAGS],
[$ax_compiler_flags_is_release],
[$4],[$5 $6 $7 $8])])
AX_COMPILER_FLAGS_LDFLAGS([$2],[$ax_compiler_flags_is_release],
[$9],[$10 $11 $12 $13])
AX_COMPILER_FLAGS_GIR([WARN_SCANNERFLAGS],[$ax_compiler_flags_is_release])
])dnl AX_COMPILER_FLAGS

View File

@ -0,0 +1,160 @@
# =============================================================================
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cflags.html
# =============================================================================
#
# SYNOPSIS
#
# AX_COMPILER_FLAGS_CFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
#
# DESCRIPTION
#
# Add warning flags for the C compiler to VARIABLE, which defaults to
# WARN_CFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be
# manually added to the CFLAGS variable for each target in the code base.
#
# This macro depends on the environment set up by AX_COMPILER_FLAGS.
# Specifically, it uses the value of $ax_enable_compile_warnings to decide
# which flags to enable.
#
# LICENSE
#
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
# Copyright (c) 2017, 2018 Reini Urban <rurban@cpan.org>
#
# 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 16
AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[
AC_REQUIRE([AC_PROG_SED])
AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS])
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
# Variable names
m4_define([ax_warn_cflags_variable],
[m4_normalize(ifelse([$1],,[WARN_CFLAGS],[$1]))])
AC_LANG_PUSH([C])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
[#ifndef __cplusplus
#error "no C++"
#endif]])],
[ax_compiler_cxx=yes;],
[ax_compiler_cxx=no;])
# Always pass -Werror=unknown-warning-option to get Clang to fail on bad
# flags, otherwise they are always appended to the warn_cflags variable, and
# Clang warns on them for every compilation unit.
# If this is passed to GCC, it will explode, so the flag must be enabled
# conditionally.
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
ax_compiler_flags_test="-Werror=unknown-warning-option"
],[
ax_compiler_flags_test=""
])
# Check that -Wno-suggest-attribute=format is supported
AX_CHECK_COMPILE_FLAG([-Wno-suggest-attribute=format],[
ax_compiler_no_suggest_attribute_flags="-Wno-suggest-attribute=format"
],[
ax_compiler_no_suggest_attribute_flags=""
])
# Base flags
AX_APPEND_COMPILE_FLAGS([ dnl
-fno-strict-aliasing dnl
$3 dnl
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
AS_IF([test "$ax_enable_compile_warnings" != "no"],[
# "yes" flags
AX_APPEND_COMPILE_FLAGS([ dnl
-Wall dnl
-Wextra dnl
-Wundef dnl
-Wwrite-strings dnl
-Wpointer-arith dnl
-Wmissing-declarations dnl
-Wredundant-decls dnl
-Wno-unused-parameter dnl
-Wno-missing-field-initializers dnl
-Wformat=2 dnl
-Wcast-align dnl
-Wformat-nonliteral dnl
-Wformat-security dnl
-Wsign-compare dnl
-Wstrict-aliasing dnl
-Wshadow dnl
-Winline dnl
-Wpacked dnl
-Wmissing-format-attribute dnl
-Wmissing-noreturn dnl
-Winit-self dnl
-Wredundant-decls dnl
-Wmissing-include-dirs dnl
-Wunused-but-set-variable dnl
-Warray-bounds dnl
-Wreturn-type dnl
-Wswitch-enum dnl
-Wswitch-default dnl
-Wduplicated-cond dnl
-Wduplicated-branches dnl
-Wlogical-op dnl
-Wrestrict dnl
-Wnull-dereference dnl
-Wdouble-promotion dnl
$4 dnl
$5 dnl
$6 dnl
$7 dnl
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
if test "$ax_compiler_cxx" = "no" ; then
# C-only flags. Warn in C++
AX_APPEND_COMPILE_FLAGS([ dnl
-Wnested-externs dnl
-Wmissing-prototypes dnl
-Wstrict-prototypes dnl
-Wdeclaration-after-statement dnl
-Wimplicit-function-declaration dnl
-Wold-style-definition dnl
-Wjump-misses-init dnl
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
fi
])
AS_IF([test "$ax_enable_compile_warnings" = "error"],[
# "error" flags; -Werror has to be appended unconditionally because
# it's not possible to test for
#
# suggest-attribute=format is disabled because it gives too many false
# positives
AX_APPEND_FLAG([-Werror],ax_warn_cflags_variable)
AX_APPEND_COMPILE_FLAGS([ dnl
[$ax_compiler_no_suggest_attribute_flags] dnl
],ax_warn_cflags_variable,[$ax_compiler_flags_test])
])
# In the flags below, when disabling specific flags, always add *both*
# -Wno-foo and -Wno-error=foo. This fixes the situation where (for example)
# we enable -Werror, disable a flag, and a build bot passes CFLAGS=-Wall,
# which effectively turns that flag back on again as an error.
for flag in $ax_warn_cflags_variable; do
AS_CASE([$flag],
[-Wno-*=*],[],
[-Wno-*],[
AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')],
ax_warn_cflags_variable,
[$ax_compiler_flags_test])
])
done
AC_LANG_POP([C])
# Substitute the variables
AC_SUBST(ax_warn_cflags_variable)
])dnl AX_COMPILER_FLAGS

View File

@ -0,0 +1,60 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_gir.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_COMPILER_FLAGS_GIR([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
#
# DESCRIPTION
#
# Add warning flags for the g-ir-scanner (from GObject Introspection) to
# VARIABLE, which defaults to WARN_SCANNERFLAGS. VARIABLE is AC_SUBST-ed
# by this macro, but must be manually added to the SCANNERFLAGS variable
# for each GIR target in the code base.
#
# This macro depends on the environment set up by AX_COMPILER_FLAGS.
# Specifically, it uses the value of $ax_enable_compile_warnings to decide
# which flags to enable.
#
# LICENSE
#
# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk>
#
# 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 6
AC_DEFUN([AX_COMPILER_FLAGS_GIR],[
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
# Variable names
m4_define([ax_warn_scannerflags_variable],
[m4_normalize(ifelse([$1],,[WARN_SCANNERFLAGS],[$1]))])
# Base flags
AX_APPEND_FLAG([$3],ax_warn_scannerflags_variable)
AS_IF([test "$ax_enable_compile_warnings" != "no"],[
# "yes" flags
AX_APPEND_FLAG([ dnl
--warn-all dnl
$4 dnl
$5 dnl
$6 dnl
$7 dnl
],ax_warn_scannerflags_variable)
])
AS_IF([test "$ax_enable_compile_warnings" = "error"],[
# "error" flags
AX_APPEND_FLAG([ dnl
--warn-error dnl
],ax_warn_scannerflags_variable)
])
# Substitute the variables
AC_SUBST(ax_warn_scannerflags_variable)
])dnl AX_COMPILER_FLAGS

View File

@ -0,0 +1,111 @@
# ==============================================================================
# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_ldflags.html
# ==============================================================================
#
# SYNOPSIS
#
# AX_COMPILER_FLAGS_LDFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
#
# DESCRIPTION
#
# Add warning flags for the linker to VARIABLE, which defaults to
# WARN_LDFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be
# manually added to the LDFLAGS variable for each target in the code base.
#
# This macro depends on the environment set up by AX_COMPILER_FLAGS.
# Specifically, it uses the value of $ax_enable_compile_warnings to decide
# which flags to enable.
#
# LICENSE
#
# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
# Copyright (c) 2017, 2018 Reini Urban <rurban@cpan.org>
#
# 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 9
AC_DEFUN([AX_COMPILER_FLAGS_LDFLAGS],[
AX_REQUIRE_DEFINED([AX_APPEND_LINK_FLAGS])
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
# Variable names
m4_define([ax_warn_ldflags_variable],
[m4_normalize(ifelse([$1],,[WARN_LDFLAGS],[$1]))])
# Always pass -Werror=unknown-warning-option to get Clang to fail on bad
# flags, otherwise they are always appended to the warn_ldflags variable,
# and Clang warns on them for every compilation unit.
# If this is passed to GCC, it will explode, so the flag must be enabled
# conditionally.
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
ax_compiler_flags_test="-Werror=unknown-warning-option"
],[
ax_compiler_flags_test=""
])
AX_CHECK_LINK_FLAG([-Wl,--as-needed], [
AX_APPEND_LINK_FLAGS([-Wl,--as-needed],
[AM_LDFLAGS],[$ax_compiler_flags_test])
])
AX_CHECK_LINK_FLAG([-Wl,-z,relro], [
AX_APPEND_LINK_FLAGS([-Wl,-z,relro],
[AM_LDFLAGS],[$ax_compiler_flags_test])
])
AX_CHECK_LINK_FLAG([-Wl,-z,now], [
AX_APPEND_LINK_FLAGS([-Wl,-z,now],
[AM_LDFLAGS],[$ax_compiler_flags_test])
])
AX_CHECK_LINK_FLAG([-Wl,-z,noexecstack], [
AX_APPEND_LINK_FLAGS([-Wl,-z,noexecstack],
[AM_LDFLAGS],[$ax_compiler_flags_test])
])
# textonly, retpolineplt not yet
# macOS and cygwin linker do not have --as-needed
AX_CHECK_LINK_FLAG([-Wl,--no-as-needed], [
ax_compiler_flags_as_needed_option="-Wl,--no-as-needed"
], [
ax_compiler_flags_as_needed_option=""
])
# macOS linker speaks with a different accent
ax_compiler_flags_fatal_warnings_option=""
AX_CHECK_LINK_FLAG([-Wl,--fatal-warnings], [
ax_compiler_flags_fatal_warnings_option="-Wl,--fatal-warnings"
])
AX_CHECK_LINK_FLAG([-Wl,-fatal_warnings], [
ax_compiler_flags_fatal_warnings_option="-Wl,-fatal_warnings"
])
# Base flags
AX_APPEND_LINK_FLAGS([ dnl
$ax_compiler_flags_as_needed_option dnl
$3 dnl
],ax_warn_ldflags_variable,[$ax_compiler_flags_test])
AS_IF([test "$ax_enable_compile_warnings" != "no"],[
# "yes" flags
AX_APPEND_LINK_FLAGS([$4 $5 $6 $7],
ax_warn_ldflags_variable,
[$ax_compiler_flags_test])
])
AS_IF([test "$ax_enable_compile_warnings" = "error"],[
# "error" flags; -Werror has to be appended unconditionally because
# it's not possible to test for
#
# suggest-attribute=format is disabled because it gives too many false
# positives
AX_APPEND_LINK_FLAGS([ dnl
$ax_compiler_flags_fatal_warnings_option dnl
],ax_warn_ldflags_variable,[$ax_compiler_flags_test])
])
# Substitute the variables
AC_SUBST(ax_warn_ldflags_variable)
])dnl AX_COMPILER_FLAGS

80
m4/ax_is_release.m4 Normal file
View File

@ -0,0 +1,80 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_is_release.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_IS_RELEASE(POLICY)
#
# DESCRIPTION
#
# Determine whether the code is being configured as a release, or from
# git. Set the ax_is_release variable to 'yes' or 'no'.
#
# If building a release version, it is recommended that the configure
# script disable compiler errors and debug features, by conditionalising
# them on the ax_is_release variable. If building from git, these
# features should be enabled.
#
# The POLICY parameter specifies how ax_is_release is determined. It can
# take the following values:
#
# * git-directory: ax_is_release will be 'no' if a '.git' directory exists
# * minor-version: ax_is_release will be 'no' if the minor version number
# in $PACKAGE_VERSION is odd; this assumes
# $PACKAGE_VERSION follows the 'major.minor.micro' scheme
# * micro-version: ax_is_release will be 'no' if the micro version number
# in $PACKAGE_VERSION is odd; this assumes
# $PACKAGE_VERSION follows the 'major.minor.micro' scheme
# * dash-version: ax_is_release will be 'no' if there is a dash '-'
# in $PACKAGE_VERSION, for example 1.2-pre3, 1.2.42-a8b9
# or 2.0-dirty (in particular this is suitable for use
# with git-version-gen)
# * always: ax_is_release will always be 'yes'
# * never: ax_is_release will always be 'no'
#
# Other policies may be added in future.
#
# LICENSE
#
# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk>
# Copyright (c) 2016 Collabora Ltd.
#
# 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.
#serial 7
AC_DEFUN([AX_IS_RELEASE],[
AC_BEFORE([AC_INIT],[$0])
m4_case([$1],
[git-directory],[
# $is_release = (.git directory does not exist)
AS_IF([test -d ${srcdir}/.git],[ax_is_release=no],[ax_is_release=yes])
],
[minor-version],[
# $is_release = ($minor_version is even)
minor_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]][[^.]]*.\([[^.]][[^.]]*\).*/\1/'`
AS_IF([test "$(( $minor_version % 2 ))" -ne 0],
[ax_is_release=no],[ax_is_release=yes])
],
[micro-version],[
# $is_release = ($micro_version is even)
micro_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]]*\.[[^.]]*\.\([[^.]]*\).*/\1/'`
AS_IF([test "$(( $micro_version % 2 ))" -ne 0],
[ax_is_release=no],[ax_is_release=yes])
],
[dash-version],[
# $is_release = ($PACKAGE_VERSION has a dash)
AS_CASE([$PACKAGE_VERSION],
[*-*], [ax_is_release=no],
[*], [ax_is_release=yes])
],
[always],[ax_is_release=yes],
[never],[ax_is_release=no],
[
AC_MSG_ERROR([Invalid policy. Valid policies: git-directory, minor-version, micro-version, dash-version, always, never.])
])
])

37
m4/ax_require_defined.m4 Normal file
View File

@ -0,0 +1,37 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_REQUIRE_DEFINED(MACRO)
#
# DESCRIPTION
#
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
# been defined and thus are available for use. This avoids random issues
# where a macro isn't expanded. Instead the configure script emits a
# non-fatal:
#
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
#
# It's like AC_REQUIRE except it doesn't expand the required macro.
#
# Here's an example:
#
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
#
# LICENSE
#
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
#
# 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 2
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
])dnl AX_REQUIRE_DEFINED

28
m4/glibtests.m4 Normal file
View File

@ -0,0 +1,28 @@
dnl GLIB_TESTS
dnl
AC_DEFUN([GLIB_TESTS],
[
AC_ARG_ENABLE(installed-tests,
AS_HELP_STRING([--enable-installed-tests],
[Enable installation of some test cases]),
[case ${enableval} in
yes) ENABLE_INSTALLED_TESTS="1" ;;
no) ENABLE_INSTALLED_TESTS="" ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;;
esac])
AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1")
AC_ARG_ENABLE(always-build-tests,
AS_HELP_STRING([--enable-always-build-tests],
[Enable always building tests during 'make all']),
[case ${enableval} in
yes) ENABLE_ALWAYS_BUILD_TESTS="1" ;;
no) ENABLE_ALWAYS_BUILD_TESTS="" ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;;
esac])
AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1")
if test "$ENABLE_INSTALLED_TESTS" = "1"; then
AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME)
AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME)
fi
])

8372
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])])

343
m4/pkg.m4 Normal file
View File

@ -0,0 +1,343 @@
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
# serial 11 (pkg-config-0.29.1)
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dnl General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
dnl 02111-1307, USA.
dnl
dnl As a special exception to the GNU General Public License, if you
dnl distribute this file as part of a program that contains a
dnl configuration script generated by Autoconf, you may include it under
dnl the same distribution terms that you use for the rest of that
dnl program.
dnl PKG_PREREQ(MIN-VERSION)
dnl -----------------------
dnl Since: 0.29
dnl
dnl Verify that the version of the pkg-config macros are at least
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
dnl installed version of pkg-config, this checks the developer's version
dnl of pkg.m4 when generating configure.
dnl
dnl To ensure that this macro is defined, also add:
dnl m4_ifndef([PKG_PREREQ],
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
dnl
dnl See the "Since" comment for each macro you use to see what version
dnl of the macros you require.
m4_defun([PKG_PREREQ],
[m4_define([PKG_MACROS_VERSION], [0.29.1])
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
dnl ----------------------------------
dnl Since: 0.16
dnl
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
dnl first found in the path. Checks that the version of pkg-config found
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
dnl used since that's the first version where most current features of
dnl pkg-config existed.
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=m4_default([$1], [0.9.0])
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
PKG_CONFIG=""
fi
fi[]dnl
])dnl PKG_PROG_PKG_CONFIG
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------------------------------
dnl Since: 0.18
dnl
dnl Check to see whether a particular set of modules exists. Similar to
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
dnl
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
dnl only at the first occurence in configure.ac, so if the first place
dnl it's called might be skipped (such as if it is within an "if", you
dnl have to call PKG_CHECK_EXISTS manually
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
m4_default([$2], [:])
m4_ifvaln([$3], [else
$3])dnl
fi])
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
dnl ---------------------------------------------
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
dnl pkg_failed based on the result.
m4_define([_PKG_CONFIG],
[if test -n "$$1"; then
pkg_cv_[]$1="$$1"
elif test -n "$PKG_CONFIG"; then
PKG_CHECK_EXISTS([$3],
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes ],
[pkg_failed=yes])
else
pkg_failed=untried
fi[]dnl
])dnl _PKG_CONFIG
dnl _PKG_SHORT_ERRORS_SUPPORTED
dnl ---------------------------
dnl Internal check to see if pkg-config supports short errors.
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi[]dnl
])dnl _PKG_SHORT_ERRORS_SUPPORTED
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl [ACTION-IF-NOT-FOUND])
dnl --------------------------------------------------------------
dnl Since: 0.4.0
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $1])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
AC_MSG_RESULT([no])
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
else
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
m4_default([$4], [AC_MSG_ERROR(
[Package requirements ($2) were not met:
$$1_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
_PKG_TEXT])[]dnl
])
elif test $pkg_failed = untried; then
AC_MSG_RESULT([no])
m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
_PKG_TEXT
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
])
else
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
AC_MSG_RESULT([yes])
$3
fi[]dnl
])dnl PKG_CHECK_MODULES
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl [ACTION-IF-NOT-FOUND])
dnl ---------------------------------------------------------------------
dnl Since: 0.29
dnl
dnl Checks for existence of MODULES and gathers its build flags with
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
dnl and VARIABLE-PREFIX_LIBS from --libs.
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
dnl configure.ac.
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
_save_PKG_CONFIG=$PKG_CONFIG
PKG_CONFIG="$PKG_CONFIG --static"
PKG_CHECK_MODULES($@)
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
])dnl PKG_CHECK_MODULES_STATIC
dnl PKG_INSTALLDIR([DIRECTORY])
dnl -------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable pkgconfigdir as the location where a module
dnl should install pkg-config .pc files. By default the directory is
dnl $libdir/pkgconfig, but the default can be changed by passing
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
dnl parameter.
AC_DEFUN([PKG_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
m4_pushdef([pkg_description],
[pkg-config installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([pkgconfigdir],
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
[with_pkgconfigdir=]pkg_default)
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
])dnl PKG_INSTALLDIR
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
dnl --------------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
dnl module should install arch-independent pkg-config .pc files. By
dnl default the directory is $datadir/pkgconfig, but the default can be
dnl changed by passing DIRECTORY. The user can override through the
dnl --with-noarch-pkgconfigdir parameter.
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
m4_pushdef([pkg_description],
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([noarch-pkgconfigdir],
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
[with_noarch_pkgconfigdir=]pkg_default)
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
])dnl PKG_NOARCH_INSTALLDIR
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------
dnl Since: 0.28
dnl
dnl Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])
AS_VAR_IF([$1], [""], [$5], [$4])dnl
])dnl PKG_CHECK_VAR
dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
dnl [DESCRIPTION], [DEFAULT])
dnl ------------------------------------------
dnl
dnl Prepare a "--with-" configure option using the lowercase
dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
dnl PKG_CHECK_MODULES in a single macro.
AC_DEFUN([PKG_WITH_MODULES],
[
m4_pushdef([with_arg], m4_tolower([$1]))
m4_pushdef([description],
[m4_default([$5], [build with ]with_arg[ support])])
m4_pushdef([def_arg], [m4_default([$6], [auto])])
m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
m4_case(def_arg,
[yes],[m4_pushdef([with_without], [--without-]with_arg)],
[m4_pushdef([with_without],[--with-]with_arg)])
AC_ARG_WITH(with_arg,
AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
[AS_TR_SH([with_]with_arg)=def_arg])
AS_CASE([$AS_TR_SH([with_]with_arg)],
[yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
[auto],[PKG_CHECK_MODULES([$1],[$2],
[m4_n([def_action_if_found]) $3],
[m4_n([def_action_if_not_found]) $4])])
m4_popdef([with_arg])
m4_popdef([description])
m4_popdef([def_arg])
])dnl PKG_WITH_MODULES
dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl [DESCRIPTION], [DEFAULT])
dnl -----------------------------------------------
dnl
dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
dnl check._[VARIABLE-PREFIX] is exported as make variable.
AC_DEFUN([PKG_HAVE_WITH_MODULES],
[
PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
AM_CONDITIONAL([HAVE_][$1],
[test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
])dnl PKG_HAVE_WITH_MODULES
dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl [DESCRIPTION], [DEFAULT])
dnl ------------------------------------------------------
dnl
dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
dnl and preprocessor variable.
AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
[
PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
[AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
])dnl PKG_HAVE_DEFINE_WITH_MODULES

1
src/Makefile.am Normal file
View File

@ -0,0 +1 @@
-include $(top_srcdir)/git.mk

473
src/Makefile.in Normal file
View File

@ -0,0 +1,473 @@
# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2017 Free Software Foundation, Inc.
# This Makefile.in 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.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = src
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
$(top_srcdir)/m4/ax_append_flag.m4 \
$(top_srcdir)/m4/ax_append_link_flags.m4 \
$(top_srcdir)/m4/ax_check_compile_flag.m4 \
$(top_srcdir)/m4/ax_check_enable_debug.m4 \
$(top_srcdir)/m4/ax_check_link_flag.m4 \
$(top_srcdir)/m4/ax_code_coverage.m4 \
$(top_srcdir)/m4/ax_compiler_flags.m4 \
$(top_srcdir)/m4/ax_compiler_flags_cflags.m4 \
$(top_srcdir)/m4/ax_compiler_flags_gir.m4 \
$(top_srcdir)/m4/ax_compiler_flags_ldflags.m4 \
$(top_srcdir)/m4/ax_is_release.m4 \
$(top_srcdir)/m4/ax_require_defined.m4 \
$(top_srcdir)/m4/glibtests.m4 $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
am__DIST_COMMON = $(srcdir)/Makefile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AS = @AS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CACARD_CFLAGS = @CACARD_CFLAGS@
CACARD_LIBS = @CACARD_LIBS@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@
CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@
CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GCOV = @GCOV@
GENHTML = @GENHTML@
GREP = @GREP@
GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
GTHREAD_LIBS = @GTHREAD_LIBS@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LCOV = @LCOV@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PCSC_CFLAGS = @PCSC_CFLAGS@
PCSC_LIBS = @PCSC_LIBS@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
WARN_CFLAGS = @WARN_CFLAGS@
WARN_LDFLAGS = @WARN_LDFLAGS@
WARN_SCANNERFLAGS = @WARN_SCANNERFLAGS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
installed_test_metadir = @installed_test_metadir@
installed_testdir = @installed_testdir@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
all: all-am
.SUFFIXES:
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign src/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
tags TAGS:
ctags CTAGS:
cscope cscopelist:
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile
installdirs:
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libtool mostlyclean-am
distclean: distclean-am
-rm -f Makefile
distclean-am: clean-am distclean-generic
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am:
.MAKE: install-am install-strip
.PHONY: all all-am check check-am clean clean-generic clean-libtool \
cscopelist-am ctags-am distclean distclean-generic \
distclean-libtool distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-ps install-ps-am \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags-am uninstall uninstall-am
.PRECIOUS: Makefile
-include $(top_srcdir)/git.mk
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

1489
src/cac-aca.c Normal file

File diff suppressed because it is too large Load Diff

33
src/cac-aca.h Normal file
View File

@ -0,0 +1,33 @@
/*
* implement the ACA applet for the CAC card.
*
* Adaptation to GSC-IS 2.1:
* https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir6887e2003.pdf
*
* Copyright 2018 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This code is licensed under the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "glib-compat.h"
#include "card_7816t.h"
#include "cac.h"
#include <string.h>
VCardResponse *
cac_aca_get_acr_response(VCard *card, int Le, unsigned char *acrid, int format);
VCardResponse *
cac_aca_get_applet_acr_response(VCard *card, int Le, unsigned int pki_applets,
unsigned char *aid, unsigned int aid_len,
unsigned char *coid, int format);
VCardResponse *
cac_aca_get_amp_response(VCard *card, int Le, int format);
VCardResponse *
cac_aca_get_service_response(VCard *card, int Le, unsigned int pki_applets,
int format);

2395
src/cac.c Normal file

File diff suppressed because it is too large Load Diff

80
src/cac.h Normal file
View File

@ -0,0 +1,80 @@
/*
* defines the entry point for the cac card. Only used by cac.c and
* vcard_emul_type.c
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef CAC_H
#define CAC_H 1
#include "vcard.h"
#include "vreader.h"
#define CAC_GET_PROPERTIES 0x56
#define CAC_GET_ACR 0x4c
#define CAC_READ_BUFFER 0x52 /* CACv2 */
#define CAC_UPDATE_BUFFER 0x58
#define CAC_SIGN_DECRYPT 0x42
#define CAC_GET_CERTIFICATE 0x36 /* CACv1 */
/* read file TAGs for CACv2 */
#define CAC_FILE_TAG 0x01
#define CAC_FILE_VALUE 0x02
/* PKI applet tags */
#define CAC_PKI_TAG_CERTIFICATE 0x70
#define CAC_PKI_TAG_CERTINFO 0x71
#define CAC_PKI_TAG_MSCUID 0x72
#define CAC_PKI_TAG_ERROR_DETECTION_CODE 0xFE
/* ACA applet tags */
#define CAC_ACR_NUM_ENTRIES 0xA1
#define CAC_ACR_ENTRY 0xA0
#define CAC_ACR_NUM_APPLETS 0x81
#define CAC_ACR_APPLET_ACR 0x80
#define CAC_ACR_OBJECT_ACR 0x82
#define CAC_ACR_AMP_NUM_ENTRIES 0x91
#define CAC_ACR_AMP_ENTRY 0x90
#define CAC_ACR_AID 0x92
#define CAC_ACR_SERVICE_NUM_ENTRIES 0x94
#define CAC_ACR_SERVICE_ENTRY 0x93
/* CCC applet tags */
#define CAC_CCC_CARD_IDENTIFIER 0xF0
#define CAC_CCC_CAPABILITY_CONTAINER_VERSION 0xF1
#define CAC_CCC_CAPABILITY_GRAMMAR_VERSION 0xF2
#define CAC_CCC_APPLICATION_CARDURL 0xF3
#define CAC_CCC_PKCS15 0xF4
#define CAC_CCC_REGISTERED_DATA_MODEL_NUMBER 0xF5
#define CAC_CCC_ACCESS_CONTROL_RULE_TABLE 0xF6
#define CAC_CCC_CARD_APDUS 0xF7
#define CAC_CCC_REDIRECTION_TAG 0xFA
#define CAC_CCC_CAPABILITY_TUPLES 0xFB
#define CAC_CCC_STATUS_TUPLES 0xFC
#define CAC_CCC_NEXT_CCC 0xFD
#define CAC_CCC_ERROR_DETECTION_CODE 0xFE
/* Applet properties tags */
#define CAC_PROPERTIES_APPLET_INFORMATION 0x01
#define CAC_PROPERTIES_NUMBER_OBJECTS 0x40
#define CAC_PROPERTIES_OBJECT_ID 0x41
#define CAC_PROPERTIES_BUFFER_PROPERTIES 0x42
#define CAC_PROPERTIES_PKI_PROPERTIES 0x43
#define CAC_PROPERTIES_TV_OBJECT 0x50
#define CAC_PROPERTIES_PKI_OBJECT 0x51
/* Buffer formats */
#define CAC_FORMAT_SIMPLETLV 1
#define CAC_FORMAT_EXTENDED 2
/*
* Initialize the cac card. This is the only public function in this file. All
* the rest are connected through function pointers.
*/
VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params,
unsigned char * const *cert, int cert_len[],
VCardKey *key[] /* adopt the keys*/,
int cert_count);
#endif

501
src/capcsc.c Normal file
View File

@ -0,0 +1,501 @@
/*
* Supply a vreader using the PC/SC interface.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "glib-compat.h"
#include <string.h>
#include <stdio.h>
#include "vcard.h"
#include "card_7816.h"
#include "capcsc.h"
#include "vreader.h"
#include "vevent.h"
#include <PCSC/wintypes.h>
#include <PCSC/winscard.h>
typedef struct _PCSCContext PCSCContext;
typedef struct {
PCSCContext *context;
int index;
char *name;
DWORD protocol;
DWORD state;
SCARDHANDLE card;
BYTE atr[MAX_ATR_SIZE];
DWORD atrlen;
int card_connected;
unsigned long request_count;
} SCardReader;
typedef struct _PCSCContext {
SCARDCONTEXT context;
SCardReader readers[CAPCSC_MAX_READERS];
int reader_count;
int readers_changed;
GThread *thread;
CompatGMutex lock;
} PCSCContext;
static void delete_reader(PCSCContext *pc, int i)
{
SCardReader *r = &pc->readers[i];
g_free(r->name);
r->name = NULL;
if (i < (pc->reader_count - 1)) {
int rem = pc->reader_count - i - 1;
memmove(&pc->readers[i], &pc->readers[i + 1],
sizeof(SCardReader) * rem);
}
pc->reader_count--;
}
static void delete_reader_cb(VReaderEmul *ve)
{
SCardReader *r = (SCardReader *) ve;
g_mutex_lock(&r->context->lock);
delete_reader(r->context, r->index);
g_mutex_unlock(&r->context->lock);
}
static int new_reader(PCSCContext *pc, const char *name, DWORD state)
{
SCardReader *r;
VReader *vreader;
if (pc->reader_count >= CAPCSC_MAX_READERS - 1) {
return 1;
}
r = &pc->readers[pc->reader_count];
memset(r, 0, sizeof(*r));
r->index = pc->reader_count++;
r->context = pc;
r->name = g_strdup(name);
vreader = vreader_new(name, (VReaderEmul *) r, delete_reader_cb);
vreader_add_reader(vreader);
vreader_free(vreader);
return 0;
}
static int find_reader(PCSCContext *pc, const char *name)
{
int i;
for (i = 0; i < pc->reader_count; i++)
if (strcmp(pc->readers[i].name, name) == 0) {
return i;
}
return -1;
}
static int scan_for_readers(PCSCContext *pc)
{
LONG rc;
int i;
char buf[8192];
DWORD buflen = sizeof(buf);
char *p;
int matches[CAPCSC_MAX_READERS] = { 0, };
g_mutex_lock(&pc->lock);
pc->readers_changed = 1;
memset(buf, 0, sizeof(buf));
rc = SCardListReaders(pc->context, NULL, buf, &buflen);
if (rc == SCARD_E_NO_READERS_AVAILABLE) {
rc = 0;
goto exit;
}
if (rc != SCARD_S_SUCCESS) {
fprintf(stderr, "SCardListReaders failed: %s (0x%lX)\n",
pcsc_stringify_error(rc), rc);
goto exit;
}
for (p = buf; p && p < buf + sizeof(buf); p += (strlen(p) + 1)) {
if (strlen(p) > 0) {
i = find_reader(pc, p);
if (i >= 0) {
matches[i]++;
} else {
if (!new_reader(pc, p, SCARD_STATE_UNAWARE)) {
matches[pc->reader_count - 1]++;
}
}
}
}
rc = 0;
exit:
i = pc->reader_count - 1;
g_mutex_unlock(&pc->lock);
for (; i >= 0; i--) {
if (!matches[i]) {
VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
if (reader) {
vreader_free(reader);
vreader_remove_reader(reader);
}
}
}
return rc;
}
static int init_pcsc(PCSCContext *pc)
{
LONG rc;
memset(pc, 0, sizeof(*pc));
rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pc->context);
if (rc != SCARD_S_SUCCESS) {
fprintf(stderr, "SCardEstablishContext: "
"Cannot Connect to Resource Manager %lX\n", rc);
return rc;
}
return 0;
}
static void prepare_reader_states(PCSCContext *pc, SCARD_READERSTATE **states,
DWORD *reader_count)
{
SCARD_READERSTATE *state;
int i;
if (*states) {
g_free(*states);
}
*reader_count = pc->reader_count;
(*reader_count)++;
*states = g_malloc((*reader_count) * sizeof(**states));
memset(*states, 0, sizeof((*reader_count) * sizeof(**states)));
for (i = 0, state = *states; i < pc->reader_count; i++, state++) {
state->szReader = pc->readers[i].name;
state->dwCurrentState = pc->readers[i].state;
}
/* Leave a space to be notified of new readers */
state->szReader = "\\\\?PnP?\\Notification";
state->dwCurrentState = SCARD_STATE_UNAWARE;
}
static int connect_card(SCardReader *r)
{
LONG rc;
r->protocol = -1;
rc = SCardConnect(r->context->context, r->name, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&r->card, &r->protocol);
if (rc != SCARD_S_SUCCESS) {
fprintf(stderr, "Failed to connect to a card reader: %s (0x%lX)\n",
pcsc_stringify_error(rc), rc);
return rc;
}
r->card_connected = 1;
r->request_count = 0;
return 0;
}
static LONG send_receive(SCardReader *r, BYTE *transmit, DWORD transmit_len,
BYTE *receive, DWORD *receive_len)
{
const SCARD_IO_REQUEST *send_header;
SCARD_IO_REQUEST receive_header;
LONG rc;
if (!r->card_connected) {
rc = connect_card(r);
if (rc) {
return rc;
}
}
if (r->protocol == SCARD_PROTOCOL_T0) {
send_header = SCARD_PCI_T0;
} else if (r->protocol == SCARD_PROTOCOL_T1) {
send_header = SCARD_PCI_T1;
} else {
fprintf(stderr, "Unknown protocol %lX\n", r->protocol);
return 1;
}
rc = SCardTransmit(r->card, send_header, transmit, transmit_len,
&receive_header, receive, receive_len);
if (rc != SCARD_S_SUCCESS) {
fprintf(stderr, "Failed to transmit %ld bytes: %s (0x%lX)\n",
transmit_len, pcsc_stringify_error(rc), rc);
return rc;
}
return 0;
}
static VCardStatus apdu_cb(VCard *card, VCardAPDU *apdu,
VCardResponse **response)
{
VCardStatus ret = VCARD_DONE;
SCardReader *r = (SCardReader *) vcard_get_private(card);
BYTE outbuf[4096];
DWORD outlen = sizeof(outbuf);
LONG rc;
rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
if (rc || outlen < 2) {
ret = VCARD_FAIL;
} else {
*response = vcard_response_new_data(outbuf, outlen - 2);
if (*response == NULL) {
return VCARD_FAIL;
}
vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
outbuf[outlen - 1]);
}
return ret;
}
static VCardStatus reset_cb(VCard *card, int channel)
{
SCardReader *r = (SCardReader *) vcard_get_private(card);
LONG rc;
/* vreader_power_on is a bit too free with it's resets.
And a reconnect is expensive; as much as 10-20 seconds.
Hence, we discard any initial reconnect request. */
if (r->request_count++ == 0) {
return VCARD_DONE;
}
rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
SCARD_RESET_CARD, &r->protocol);
if (rc != SCARD_S_SUCCESS) {
fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
pcsc_stringify_error(rc), rc);
return VCARD_FAIL;
}
return VCARD_DONE;
}
static void get_atr_cb(VCard *card, unsigned char *atr, int *atr_len)
{
SCardReader *r = (SCardReader *) vcard_get_private(card);
*atr_len = r->atrlen;
if (atr) {
memcpy(atr, r->atr, r->atrlen);
}
}
static void delete_card_cb(VCardEmul *ve)
{
fprintf(stderr, "TODO, got a delete_card_cb\n");
}
static void insert_card(SCardReader *r, SCARD_READERSTATE *s)
{
VReader *reader;
VCardApplet *applet;
VCard *card;
memcpy(r->atr, s->rgbAtr, MIN(sizeof(r->atr), sizeof(s->rgbAtr)));
r->atrlen = s->cbAtr;
reader = vreader_get_reader_by_name(r->name);
if (!reader) {
return;
}
if (connect_card(r)) {
return;
}
applet =
vcard_new_applet(apdu_cb,
reset_cb,
(const unsigned char *)CAPCSC_APPLET,
strlen(CAPCSC_APPLET));
if (!applet) {
return;
}
card = vcard_new((VCardEmul *) r, delete_card_cb);
if (!card) {
vcard_delete_applet(applet);
vreader_free(reader);
return;
}
vcard_set_type(card, VCARD_DIRECT);
vcard_set_atr_func(card, get_atr_cb);
vcard_add_applet(card, applet);
vreader_insert_card(reader, card);
vreader_free(reader);
}
static void remove_card(SCardReader *r)
{
LONG rc;
VReader *reader;
memset(r->atr, 0, sizeof(r->atr));
r->atrlen = 0;
rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
if (rc != SCARD_S_SUCCESS) {
fprintf(stderr, "Non fatal info:"
"failed to disconnect card reader: %s (0x%lX)\n",
pcsc_stringify_error(rc), rc);
}
r->card_connected = 0;
reader = vreader_get_reader_by_name(r->name);
if (!reader) {
return;
}
vreader_insert_card(reader, NULL);
vreader_free(reader);
}
static void process_reader_change(SCardReader *r, SCARD_READERSTATE *s)
{
if (s->dwEventState & SCARD_STATE_PRESENT) {
insert_card(r, s);
} else if (s->dwEventState & SCARD_STATE_EMPTY) {
remove_card(r);
} else {
fprintf(stderr, "Unexpected card state change from %lx to %lx:\n",
r->state, s->dwEventState);
}
r->state = s->dwEventState & ~SCARD_STATE_CHANGED;
}
/*
* This thread looks for card and reader insertions and puts events on the
* event queue.
*/
static gpointer event_thread(gpointer arg)
{
PCSCContext *pc = (PCSCContext *) arg;
DWORD reader_count = 0;
SCARD_READERSTATE *reader_states = NULL;
LONG rc;
scan_for_readers(pc);
do {
DWORD i;
DWORD timeout = INFINITE;
g_mutex_lock(&pc->lock);
if (pc->readers_changed) {
prepare_reader_states(pc, &reader_states, &reader_count);
timeout = 0;
} else if (reader_count > 1) {
timeout = 0;
}
pc->readers_changed = 0;
g_mutex_unlock(&pc->lock);
rc = SCardGetStatusChange(pc->context, timeout, reader_states,
reader_count);
/* If we have a new reader, or an unknown reader,
rescan and go back and do it again */
if ((rc == SCARD_S_SUCCESS && (reader_states[reader_count - 1].dwEventState & SCARD_STATE_CHANGED))
||
rc == SCARD_E_UNKNOWN_READER) {
scan_for_readers(pc);
continue;
}
if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
rc, pcsc_stringify_error(rc));
continue;
}
g_mutex_lock(&pc->lock);
for (i = 0; i < reader_count; i++) {
if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
process_reader_change(&pc->readers[i], &reader_states[i]);
pc->readers_changed++;
}
}
g_mutex_unlock(&pc->lock);
/* libpcsclite is only thread safe at a high level. If we constantly
hold long calls into SCardGetStatusChange, we'll starve any running
clients. So, if we have an active session, and nothing has changed
on our front, we just idle. */
if (!pc->readers_changed && reader_count > 1) {
g_usleep(CAPCSC_POLL_TIME * 1000);
}
} while (1);
return NULL;
}
/*
* We poll the PC/SC interface, looking for device changes
*/
static int new_event_thread(PCSCContext *pc)
{
pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
return pc->thread == NULL;
}
static PCSCContext context;
int capcsc_init(void)
{
g_mutex_init(&context.lock);
if (init_pcsc(&context)) {
return -1;
}
if (new_event_thread(&context)) {
return -1;
}
return 0;
}

18
src/capcsc.h Normal file
View File

@ -0,0 +1,18 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#ifndef CAPCSC_H
#define CAPCSC_H 1
#define CAPCSC_POLL_TIME 50 /* ms - Time we will poll for */
/* card change when a */
/* reader is connected */
#define CAPCSC_MAX_READERS 16
#define CAPCSC_APPLET "CAPCSC APPLET"
int capcsc_init(void);
#endif

808
src/card_7816.c Normal file
View File

@ -0,0 +1,808 @@
/*
* Implement the 7816 portion of the card spec
*
* This code is licensed under the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "glib-compat.h"
#include <string.h>
#include "vcard.h"
#include "vcard_emul.h"
#include "card_7816.h"
/* Global Platform Card Manager applet AID */
static const unsigned char gp_aid[] = {
0xa0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 };
/* Global Platform Card Manager response on select applet */
static const unsigned char gp_response[] = {
0x6F, 0x19, 0x84, 0x08, 0xA0, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0xA5, 0x0D, 0x9F, 0x6E,
0x06, 0x12, 0x91, 0x51, 0x81, 0x01, 0x00, 0x9F,
0x65, 0x01, 0xFF};
/*
* set the status bytes based on the status word
*/
static void
vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
{
unsigned char sw1, sw2;
response->b_status = status; /* make sure the status and swX representations
* are consistent */
sw1 = (status >> 8) & 0xff;
sw2 = status & 0xff;
response->b_sw1 = sw1;
response->b_sw2 = sw2;
response->b_data[response->b_len] = sw1;
response->b_data[response->b_len+1] = sw2;
}
/*
* set the status bytes in a response buffer
*/
void
vcard_response_set_status_bytes(VCardResponse *response,
unsigned char sw1, unsigned char sw2)
{
response->b_status = sw1 << 8 | sw2;
response->b_sw1 = sw1;
response->b_sw2 = sw2;
response->b_data[response->b_len] = sw1;
response->b_data[response->b_len+1] = sw2;
}
/*
* allocate a VCardResponse structure, plus space for the data buffer, and
* set up everything but the resonse bytes.
*/
VCardResponse *
vcard_response_new_data(const unsigned char *buf, int len)
{
VCardResponse *new_response;
new_response = g_new(VCardResponse, 1);
new_response->b_data = g_malloc(len + 2);
memcpy(new_response->b_data, buf, len);
new_response->b_total_len = len+2;
new_response->b_len = len;
new_response->b_type = VCARD_MALLOC;
return new_response;
}
static VCardResponse *
vcard_init_buffer_response(VCard *card, const unsigned char *buf, int len)
{
VCardResponse *response;
VCardBufferResponse *buffer_response;
buffer_response = vcard_get_buffer_response(card);
if (buffer_response) {
vcard_set_buffer_response(card, NULL);
vcard_buffer_response_delete(buffer_response);
}
buffer_response = vcard_buffer_response_new(buf, len);
if (buffer_response == NULL) {
return NULL;
}
response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
len > 255 ? 0 : len);
if (response == NULL) {
return NULL;
}
vcard_set_buffer_response(card, buffer_response);
return response;
}
/*
* general buffer to hold results from APDU calls
*/
VCardResponse *
vcard_response_new(VCard *card, const unsigned char *buf,
int len, int Le, vcard_7816_status_t status)
{
VCardResponse *new_response;
g_debug("%s: Sending response (len = %d, Le = %d)", __func__, len, Le);
if (len > Le) {
return vcard_init_buffer_response(card, buf, len);
}
new_response = vcard_response_new_data(buf, len);
if (new_response == NULL) {
return NULL;
}
vcard_response_set_status(new_response, status);
return new_response;
}
/*
* general buffer to hold results from APDU calls
*/
VCardResponse *
vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
unsigned char sw1, unsigned char sw2)
{
VCardResponse *new_response;
g_debug("%s: Sending response (len = %d, Le = %d)", __func__, len, Le);
if (len > Le) {
return vcard_init_buffer_response(card, buf, len);
}
new_response = vcard_response_new_data(buf, len);
if (new_response == NULL) {
return NULL;
}
vcard_response_set_status_bytes(new_response, sw1, sw2);
return new_response;
}
/*
* get a new Response buffer that only has a status.
*/
static VCardResponse *
vcard_response_new_status(vcard_7816_status_t status)
{
VCardResponse *new_response;
new_response = g_new(VCardResponse, 1);
new_response->b_data = &new_response->b_sw1;
new_response->b_len = 0;
new_response->b_total_len = 2;
new_response->b_type = VCARD_MALLOC_STRUCT;
vcard_response_set_status(new_response, status);
return new_response;
}
/*
* same as above, but specify the status as separate bytes
*/
VCardResponse *
vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
{
VCardResponse *new_response;
new_response = g_new(VCardResponse, 1);
new_response->b_data = &new_response->b_sw1;
new_response->b_len = 0;
new_response->b_total_len = 2;
new_response->b_type = VCARD_MALLOC_STRUCT;
vcard_response_set_status_bytes(new_response, sw1, sw2);
return new_response;
}
/*
* free the response buffer. The Buffer has a type to handle the buffer
* allocated in other ways than through malloc.
*/
void
vcard_response_delete(VCardResponse *response)
{
if (response == NULL) {
return;
}
switch (response->b_type) {
case VCARD_MALLOC:
/* everything was malloc'ed */
g_free(response->b_data);
g_free(response);
break;
case VCARD_MALLOC_DATA:
/* only the data buffer was malloc'ed */
g_free(response->b_data);
break;
case VCARD_MALLOC_STRUCT:
/* only the structure was malloc'ed */
g_free(response);
break;
case VCARD_STATIC:
break;
default:
g_warn_if_reached();
}
}
/*
* decode the class bit and set our generic type field, channel, and
* secure messaging values.
*/
static vcard_7816_status_t
vcard_apdu_set_class(VCardAPDU *apdu) {
apdu->a_channel = 0;
apdu->a_secure_messaging = 0;
apdu->a_type = apdu->a_cla & 0xf0;
apdu->a_gen_type = VCARD_7816_ISO;
/* parse the class tables 8 & 9 of the 7816-4 Part 4 spec */
switch (apdu->a_type) {
/* we only support the basic types */
case 0x00:
case 0x80:
case 0x90:
case 0xa0:
apdu->a_channel = apdu->a_cla & 3;
apdu->a_secure_messaging = apdu->a_cla & 0xe;
break;
case 0xb0:
case 0xc0:
break;
case 0x10:
case 0x20:
case 0x30:
case 0x40:
case 0x50:
case 0x60:
case 0x70:
/* Reserved for future use */
apdu->a_gen_type = VCARD_7816_RFU;
break;
case 0xd0:
case 0xe0:
case 0xf0:
default:
apdu->a_gen_type =
(apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY;
break;
}
return VCARD7816_STATUS_SUCCESS;
}
/*
* set the Le and Lc fields according to table 5 of the
* 7816-4 part 4 spec
*/
static vcard_7816_status_t
vcard_apdu_set_length(VCardAPDU *apdu)
{
int L, Le;
/* process according to table 5 of the 7816-4 Part 4 spec.
* variable names match the variables in the spec */
L = apdu->a_len-4; /* fixed APDU header */
apdu->a_Lc = 0;
apdu->a_Le = 0;
apdu->a_body = NULL;
switch (L) {
case 0:
/* 1 minimal apdu */
return VCARD7816_STATUS_SUCCESS;
case 1:
/* 2S only return values apdu */
/* zero maps to 256 here */
apdu->a_Le = apdu->a_header->ah_Le ?
apdu->a_header->ah_Le : 256;
return VCARD7816_STATUS_SUCCESS;
default:
/* if the ah_Le byte is zero and we have more than
* 1 byte in the header, then we must be using extended Le and Lc.
* process the extended now. */
if (apdu->a_header->ah_Le == 0) {
if (L < 3) {
/* coding error, need at least 3 bytes */
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
}
/* calculate the first extended value. Could be either Le or Lc */
Le = (apdu->a_header->ah_body[0] << 8)
| apdu->a_header->ah_body[1];
if (L == 3) {
/* 2E extended, return data only */
/* zero maps to 65536 */
apdu->a_Le = Le ? Le : 65536;
return VCARD7816_STATUS_SUCCESS;
}
if (Le == 0) {
/* reserved for future use, probably for next time we need
* to extend the lengths */
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
}
/* we know that the first extended value is Lc now */
apdu->a_Lc = Le;
apdu->a_body = &apdu->a_header->ah_body[2];
if (L == Le+3) {
/* 3E extended, only body parameters */
return VCARD7816_STATUS_SUCCESS;
}
if (L == Le+5) {
/* 4E extended, parameters and return data */
Le = (apdu->a_data[apdu->a_len-2] << 8)
| apdu->a_data[apdu->a_len-1];
apdu->a_Le = Le ? Le : 65536;
return VCARD7816_STATUS_SUCCESS;
}
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
}
/* not extended */
apdu->a_Lc = apdu->a_header->ah_Le;
apdu->a_body = &apdu->a_header->ah_body[0];
if (L == apdu->a_Lc + 1) {
/* 3S only body parameters */
return VCARD7816_STATUS_SUCCESS;
}
if (L == apdu->a_Lc + 2) {
/* 4S parameters and return data */
Le = apdu->a_data[apdu->a_len-1];
apdu->a_Le = Le ? Le : 256;
return VCARD7816_STATUS_SUCCESS;
}
break;
}
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
}
/*
* create a new APDU from a raw set of bytes. This will decode all the
* above fields. users of VCARDAPDU's can then depend on the already decoded
* values.
*/
VCardAPDU *
vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
{
VCardAPDU *new_apdu;
*status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
if (len < 4) {
*status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
return NULL;
}
new_apdu = g_new(VCardAPDU, 1);
new_apdu->a_data = g_memdup(raw_apdu, len);
new_apdu->a_len = len;
*status = vcard_apdu_set_class(new_apdu);
if (*status != VCARD7816_STATUS_SUCCESS) {
vcard_apdu_delete(new_apdu);
return NULL;
}
*status = vcard_apdu_set_length(new_apdu);
if (*status != VCARD7816_STATUS_SUCCESS) {
vcard_apdu_delete(new_apdu);
new_apdu = NULL;
}
return new_apdu;
}
void
vcard_apdu_delete(VCardAPDU *apdu)
{
if (apdu == NULL) {
return;
}
g_free(apdu->a_data);
g_free(apdu);
}
/*
* declare response buffers for all the 7816 defined error codes
*/
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(
VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
VCARD_RESPONSE_NEW_STATIC_STATUS(
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
/*
* return a single response code. This function cannot fail. It will always
* return a response.
*/
VCardResponse *
vcard_make_response(vcard_7816_status_t status)
{
VCardResponse *response;
switch (status) {
/* known 7816 response codes */
case VCARD7816_STATUS_SUCCESS:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_SUCCESS);
case VCARD7816_STATUS_WARNING:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING);
case VCARD7816_STATUS_WARNING_RET_CORUPT:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING_RET_CORUPT);
case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
case VCARD7816_STATUS_WARNING_CHANGE:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING_CHANGE);
case VCARD7816_STATUS_WARNING_FILE_FILLED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_WARNING_FILE_FILLED);
case VCARD7816_STATUS_EXC_ERROR:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_EXC_ERROR);
case VCARD7816_STATUS_EXC_ERROR_CHANGE:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_EXC_ERROR_CHANGE);
case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_WRONG_LENGTH);
case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
case VCARD7816_STATUS_ERROR_DATA_INVALID:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_DATA_INVALID);
case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
case VCARD7816_STATUS_ERROR_DATA_NO_EF:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_DATA_NO_EF);
case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
case VCARD7816_STATUS_ERROR_CLA_INVALID:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_CLA_INVALID);
case VCARD7816_STATUS_ERROR_GENERAL:
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_ERROR_GENERAL);
default:
/* we don't know this status code, create a response buffer to
* hold it */
response = vcard_response_new_status(status);
if (response == NULL) {
/* couldn't allocate the buffer, return memmory error */
return VCARD_RESPONSE_GET_STATIC(
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
}
return response;
}
}
/*
* Add File card support here if you need it.
*/
static VCardStatus
vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response)
{
/* TODO: if we want to support a virtual file system card, we do it here.
* It would probably be a pkcs #15 card type */
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
return VCARD_DONE;
}
/*
* VM card (including java cards)
*/
static VCardStatus
vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response)
{
int bytes_to_copy, next_byte_count, count;
VCardApplet *current_applet;
VCardBufferResponse *buffer_response;
vcard_7816_status_t status;
/* parse the class first */
if (apdu->a_gen_type != VCARD_7816_ISO) {
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
return VCARD_DONE;
}
/* use a switch so that if we need to support secure channel stuff later,
* we know where to put it */
switch (apdu->a_secure_messaging) {
case 0x0: /* no SM */
break;
case 0x4: /* proprietary SM */
case 0x8: /* header not authenticated */
case 0xc: /* header authenticated */
default:
/* for now, don't try to support secure channel stuff in the
* virtual card. */
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
return VCARD_DONE;
}
/* now parse the instruction */
switch (apdu->a_ins) {
case VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
case VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
case VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
case VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
case VCARD7816_INS_ERASE_BINARY: /* applet control op */
case VCARD7816_INS_READ_BINARY: /* applet control op */
case VCARD7816_INS_WRITE_BINARY: /* applet control op */
case VCARD7816_INS_UPDATE_BINARY: /* applet control op */
case VCARD7816_INS_READ_RECORD: /* file op */
case VCARD7816_INS_WRITE_RECORD: /* file op */
case VCARD7816_INS_UPDATE_RECORD: /* file op */
case VCARD7816_INS_APPEND_RECORD: /* file op */
case VCARD7816_INS_ENVELOPE:
case VCARD7816_INS_PUT_DATA:
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
break;
case VCARD7816_INS_SELECT_FILE:
/* GSC-IS: 5.3.3.2 Select Applet APDU: P1 = 0x04 */
if (apdu->a_p1 != 0x04) {
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
break;
}
/* side effect, deselect the current applet if no applet has been found
*/
current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
vcard_select_applet(card, apdu->a_channel, current_applet);
if (current_applet) {
VCardApplet *gp_applet = vcard_find_applet(card,
gp_aid, sizeof(gp_aid));
if (current_applet == gp_applet) {
/* if the new applet is Global Platform Card Manager, we need to
* return a response (from Card Specification v2.3.1):
*
* 6F 19 : FCI Template
* 84 08 : Application / file AID
* A0 00 00 00 03 00 00 00
* A5 0D : Proprietary data
* 9F 6E 06 : Application Producution Life Cycle
* 12 91 51 81 01 00
* 9F 65 01 : Maximum Length of data field in comand message
* FF
*/
*response = vcard_response_new(card, gp_response,
sizeof(gp_response), apdu->a_Le, VCARD7816_STATUS_SUCCESS);
} else {
unsigned char fci_template[] = {
0x6F, 0x0B, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00,
0x79, 0x03, 0x00, 0xA5, 0x00};
/* with GSC-IS 2 applets, we do not need to return anything
* for select applet, but cards generally do, at least this
* FCI template stub:
*
* 6F 0B : FCI Template
* 84 07 : Application / file AID
* A0 00 00 00 79 03 00
* A5 00 : Proprietary data
*/
/* Insert the correct AID in the structure */
g_assert_cmpint(apdu->a_Lc, ==, 7);
memcpy(&fci_template[4], apdu->a_body, apdu->a_Lc);
*response = vcard_response_new(card, fci_template,
sizeof(fci_template), apdu->a_Le, VCARD7816_STATUS_SUCCESS);
}
} else {
/* the real CAC returns (SW1=0x6A, SW2=0x82) */
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
}
break;
case VCARD7816_INS_VERIFY:
if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
} else {
if (apdu->a_Lc == 0) {
/* handle pin count if possible */
count = vcard_emul_get_login_count(card);
if (count < 0) {
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
} else {
if (count > 0xf) {
count = 0xf;
}
*response = vcard_response_new_status_bytes(
VCARD7816_SW1_WARNING_CHANGE,
0xc0 | count);
if (*response == NULL) {
*response = vcard_make_response(
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
}
}
} else {
status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
*response = vcard_make_response(status);
}
}
break;
case VCARD7816_INS_GET_RESPONSE:
buffer_response = vcard_get_buffer_response(card);
if (!buffer_response) {
*response = vcard_make_response(
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
/* handle error */
break;
}
bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
*response = vcard_response_new_bytes(
card, buffer_response->current, bytes_to_copy,
apdu->a_Le,
next_byte_count ?
VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS,
next_byte_count);
buffer_response->current += bytes_to_copy;
buffer_response->len -= bytes_to_copy;
if (*response == NULL || (next_byte_count == 0)) {
vcard_set_buffer_response(card, NULL);
vcard_buffer_response_delete(buffer_response);
}
if (*response == NULL) {
*response =
vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
}
break;
case VCARD7816_INS_GET_DATA:
*response =
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
break;
default:
*response =
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
break;
}
/* response should have been set somewhere */
g_assert(*response != NULL);
return VCARD_DONE;
}
/*
* APDU processing starts here. This routes the card processing stuff to the
* right location.
*/
VCardStatus
vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
{
VCardStatus status;
VCardBufferResponse *buffer_response;
/* first handle any PTS commands, which aren't really APDU's */
if (apdu->a_type == VCARD_7816_PTS) {
/* the PTS responses aren't really responses either */
*response = vcard_response_new_data(apdu->a_data, apdu->a_len);
/* PTS responses have no status bytes */
(*response)->b_total_len = (*response)->b_len;
return VCARD_DONE;
}
buffer_response = vcard_get_buffer_response(card);
if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
/* clear out buffer_response, do not return an error */
vcard_set_buffer_response(card, NULL);
vcard_buffer_response_delete(buffer_response);
}
status = vcard_process_applet_apdu(card, apdu, response);
if (status != VCARD_NEXT) {
return status;
}
switch (vcard_get_type(card)) {
case VCARD_FILE_SYSTEM:
return vcard7816_file_system_process_apdu(card, apdu, response);
case VCARD_VM:
return vcard7816_vm_process_apdu(card, apdu, response);
case VCARD_DIRECT:
/* if we are type direct, then the applet should handle everything */
g_assert(!"VCARD_DIRECT: applet failure");
break;
default:
g_warn_if_reached();
}
*response =
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
return VCARD_DONE;
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */

62
src/card_7816.h Normal file
View File

@ -0,0 +1,62 @@
/*
* Implement the 7816 portion of the card spec
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef CARD_7816_H
#define CARD_7816_H 1
#include "card_7816t.h"
#include "vcardt.h"
/*
* constructors for VCardResponse's
*/
/* response from a return buffer and a status */
VCardResponse *vcard_response_new(VCard *card, const unsigned char *buf, int len,
int Le, vcard_7816_status_t status);
/* response from a return buffer and status bytes */
VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf,
int len, int Le,
unsigned char sw1, unsigned char sw2);
/* response from just status bytes */
VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
unsigned char sw2);
/* response from just status: NOTE this cannot fail, it will always return a
* valid response, if it can't allocate memory, the response will be
* VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */
VCardResponse *vcard_make_response(vcard_7816_status_t status);
/* create a raw response (status has already been encoded */
VCardResponse *vcard_response_new_data(const unsigned char *buf, int len);
void vcard_response_set_status_bytes(VCardResponse *response,
unsigned char sw1, unsigned char sw2);
/*
* destructor for VCardResponse.
* Can be called with a NULL response
*/
void vcard_response_delete(VCardResponse *response);
/*
* constructor for VCardAPDU
*/
VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len,
unsigned short *status);
/*
* destructor for VCardAPDU
* Can be called with a NULL apdu
*/
void vcard_apdu_delete(VCardAPDU *apdu);
/*
* APDU processing starts here. This routes the card processing stuff to the
* right location. Always returns a valid response.
*/
VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response);
#endif

165
src/card_7816t.h Normal file
View File

@ -0,0 +1,165 @@
/*
* Implement the 7816 portion of the card spec
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef CARD_7816T_H
#define CARD_7816T_H 1
typedef unsigned short vcard_7816_status_t;
struct VCardResponseStruct {
unsigned char *b_data;
vcard_7816_status_t b_status;
unsigned char b_sw1;
unsigned char b_sw2;
int b_len;
int b_total_len;
enum VCardResponseBufferType {
VCARD_MALLOC,
VCARD_MALLOC_DATA,
VCARD_MALLOC_STRUCT,
VCARD_STATIC
} b_type;
};
#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \
static const VCardResponse VCardResponse##stat = \
{(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \
((stat) & 0xff), 0, 2, VCARD_STATIC};
#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \
static const VCardResponse VCARDResponse##sw1 = \
{(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \
(sw1), (sw2), 0, 2, VCARD_STATIC};
/* cast away the const, callers need may need to 'free' the
* result, and const implies that they don't */
#define VCARD_RESPONSE_GET_STATIC(name) \
((VCardResponse *)(&VCardResponse##name))
typedef enum {
VCARD_7816_ISO,
VCARD_7816_RFU,
VCARD_7816_PTS,
VCARD_7816_PROPRIETARY
} VCardAPDUType;
/*
* 7816 header. All APDU's have this header.
* They must be laid out in this order.
*/
struct VCardAPDUHeader {
unsigned char ah_cla;
unsigned char ah_ins;
unsigned char ah_p1;
unsigned char ah_p2;
unsigned char ah_Le;
unsigned char ah_body[1]; /* indefinite length */
};
/*
* 7816 APDU structure. The raw bytes are stored in the union and can be
* accessed directly through u.data (which is aliased as a_data).
*
* Names of the fields match the 7816 documentation.
*/
struct VCardAPDUStruct {
int a_len; /* length of the whole buffer, including header */
int a_Lc; /* 7816 Lc (parameter length) value */
int a_Le; /* 7816 Le (expected result length) value */
unsigned char *a_body; /* pointer to the parameter */
int a_channel; /* decoded channel */
int a_secure_messaging; /* decoded secure messaging type */
int a_type; /* decoded type from cla (top nibble of class) */
VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */
union {
struct VCardAPDUHeader *header;
unsigned char *data;
} u;
/* give the subfields a unified look */
#define a_header u.header
#define a_data u.data
#define a_cla a_header->ah_cla /* class */
#define a_ins a_header->ah_ins /* instruction */
#define a_p1 a_header->ah_p1 /* parameter 1 */
#define a_p2 a_header->ah_p2 /* parameter 2 */
};
/* 7816 status codes */
#define VCARD7816_STATUS_SUCCESS 0x9000
#define VCARD7816_STATUS_WARNING 0x6200
#define VCARD7816_STATUS_WARNING_RET_CORUPT 0x6281
#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE 0x6282
#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED 0x6283
#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID 0x6284
#define VCARD7816_STATUS_WARNING_CHANGE 0x6300
#define VCARD7816_STATUS_WARNING_FILE_FILLED 0x6381
#define VCARD7816_STATUS_EXC_ERROR 0x6400
#define VCARD7816_STATUS_EXC_ERROR_CHANGE 0x6500
#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE 0x6581
#define VCARD7816_STATUS_ERROR_WRONG_LENGTH 0x6700
#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED 0x6800
#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED 0x6881
#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED 0x6882
#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED 0x6900
#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981
#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED 0x6982
#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED 0x6983
#define VCARD7816_STATUS_ERROR_DATA_INVALID 0x6984
#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED 0x6985
#define VCARD7816_STATUS_ERROR_DATA_NO_EF 0x6986
#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING 0x6987
#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT 0x6988
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS 0x6a00
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA 0x6a80
#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED 0x6a81
#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND 0x6a82
#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND 0x6a83
#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE 0x6a84
#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT 0x6a85
#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT 0x6a86
#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT 0x6a87
#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND 0x6a88
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2 0x6b00
#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID 0x6d00
#define VCARD7816_STATUS_ERROR_CLA_INVALID 0x6e00
#define VCARD7816_STATUS_ERROR_GENERAL 0x6f00
/* 7816 sw1 codes */
#define VCARD7816_SW1_SUCCESS 0x90
#define VCARD7816_SW1_RESPONSE_BYTES 0x61
#define VCARD7816_SW1_WARNING 0x62
#define VCARD7816_SW1_WARNING_CHANGE 0x63
#define VCARD7816_SW1_EXC_ERROR 0x64
#define VCARD7816_SW1_EXC_ERROR_CHANGE 0x65
#define VCARD7816_SW1_ERROR_WRONG_LENGTH 0x67
#define VCARD7816_SW1_CLA_ERROR 0x68
#define VCARD7816_SW1_COMMAND_ERROR 0x69
#define VCARD7816_SW1_P1_P2_ERROR 0x6a
#define VCARD7816_SW1_LE_ERROR 0x6c
#define VCARD7816_SW1_INS_ERROR 0x6d
#define VCARD7816_SW1_CLA_NOT_SUPPORTED 0x6e
/* 7816 Instructions */
#define VCARD7816_INS_MANAGE_CHANNEL 0x70
#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82
#define VCARD7816_INS_GET_CHALLENGE 0x84
#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88
#define VCARD7816_INS_ERASE_BINARY 0x0e
#define VCARD7816_INS_READ_BINARY 0xb0
#define VCARD7816_INS_WRITE_BINARY 0xd0
#define VCARD7816_INS_UPDATE_BINARY 0xd6
#define VCARD7816_INS_READ_RECORD 0xb2
#define VCARD7816_INS_WRITE_RECORD 0xd2
#define VCARD7816_INS_UPDATE_RECORD 0xdc
#define VCARD7816_INS_APPEND_RECORD 0xe2
#define VCARD7816_INS_ENVELOPE 0xc2
#define VCARD7816_INS_PUT_DATA 0xda
#define VCARD7816_INS_GET_DATA 0xca
#define VCARD7816_INS_SELECT_FILE 0xa4
#define VCARD7816_INS_VERIFY 0x20
#define VCARD7816_INS_GET_RESPONSE 0xc0
#endif

84
src/common.c Normal file
View File

@ -0,0 +1,84 @@
/*
* common.c: Utility functions for libcacard
*
* Copyright (C) 2016 - 2018 Red Hat, Inc.
*
* Authors: Robert Relyea <rrelyea@redhat.com>
* Jakub Jelen <jjelen@redhat.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include "vcard.h"
#include "common.h"
unsigned char *
ushort2lebytes(unsigned char *buf, unsigned short x)
{
if (buf != NULL) {
buf[0] = (unsigned char) (x & 0xff);
buf[1] = (unsigned char) ((x >> 8) & 0xff);
}
return buf;
}
unsigned short
lebytes2ushort(const unsigned char *buf)
{
if (buf == NULL)
return 0U;
return (unsigned short)buf[1] << 8 | (unsigned short)buf[0];
}
#define MAX_STATIC_BYTES 1024
static char hexdump_buffer[5*MAX_STATIC_BYTES + 1];
/*
* Creates printable representation in hexadecimal format of the data
* provided in the buf buffer. A static buffer will be used, which
* can hold up to 1024 bytes (longer will get truncated).
*
* The dumping loop will print 5 visible characters at a time, but since it's
* using sprintf, we also need to account for the '\0' it appends to the end of
* the string on the last iteration, or we'll overflow the buffer we are
* printing to.
*/
char *
hex_dump(const unsigned char *buf, size_t buflen)
{
char *p, *start;
size_t i;
if (buflen <= 0)
return NULL;
start = hexdump_buffer;
buflen = MIN(buflen, MAX_STATIC_BYTES);
p = start;
for (i = 0; i < buflen; i++) {
sprintf(p, "0x%02X ", buf[i]);
p += 5;
}
/* terminate */
*--p = '\x00';
return start;
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */

34
src/common.h Normal file
View File

@ -0,0 +1,34 @@
/*
* common.h: Utility functions for libcacard
*
* Copyright (C) 2016 - 2018 Red Hat, Inc.
*
* Authors: Robert Relyea <rrelyea@redhat.com>
* Jakub Jelen <jjelen@redhat.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _COMMON_H
#define _COMMON_H
#include <stddef.h>
unsigned char *ushort2lebytes(unsigned char *buf, unsigned short x);
unsigned short lebytes2ushort(const unsigned char *buf);
char *hex_dump(const unsigned char *buf, size_t buflen);
#endif

103
src/event.c Normal file
View File

@ -0,0 +1,103 @@
/*
* event queue implementation.
*
* This code is licensed under the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "glib-compat.h"
#include "vcard.h"
#include "vreader.h"
#include "vevent.h"
VEvent *
vevent_new(VEventType type, VReader *reader, VCard *card)
{
VEvent *new_vevent;
new_vevent = g_new(VEvent, 1);
new_vevent->next = NULL;
new_vevent->type = type;
new_vevent->reader = vreader_reference(reader);
new_vevent->card = vcard_reference(card);
return new_vevent;
}
void
vevent_delete(VEvent *vevent)
{
if (vevent == NULL) {
return;
}
vreader_free(vevent->reader);
vcard_free(vevent->card);
g_free(vevent);
}
/*
* VEvent queue management
*/
static VEvent *vevent_queue_head;
static VEvent *vevent_queue_tail;
static CompatGMutex vevent_queue_lock;
static CompatGCond vevent_queue_condition;
void vevent_queue_init(void)
{
vevent_queue_head = vevent_queue_tail = NULL;
}
void
vevent_queue_vevent(VEvent *vevent)
{
vevent->next = NULL;
g_mutex_lock(&vevent_queue_lock);
if (vevent_queue_head) {
assert(vevent_queue_tail);
vevent_queue_tail->next = vevent;
} else {
vevent_queue_head = vevent;
}
vevent_queue_tail = vevent;
g_cond_signal(&vevent_queue_condition);
g_mutex_unlock(&vevent_queue_lock);
}
/* must have lock */
static VEvent *
vevent_dequeue_vevent(void)
{
VEvent *vevent = NULL;
if (vevent_queue_head) {
vevent = vevent_queue_head;
vevent_queue_head = vevent->next;
vevent->next = NULL;
}
return vevent;
}
VEvent *vevent_wait_next_vevent(void)
{
VEvent *vevent;
g_mutex_lock(&vevent_queue_lock);
while ((vevent = vevent_dequeue_vevent()) == NULL) {
g_cond_wait(&vevent_queue_condition, &vevent_queue_lock);
}
g_mutex_unlock(&vevent_queue_lock);
return vevent;
}
VEvent *vevent_get_next_vevent(void)
{
VEvent *vevent;
g_mutex_lock(&vevent_queue_lock);
vevent = vevent_dequeue_vevent();
g_mutex_unlock(&vevent_queue_lock);
return vevent;
}

30
src/eventt.h Normal file
View File

@ -0,0 +1,30 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef EVENTT_H
#define EVENTT_H 1
#include "vreadert.h"
#include "vcardt.h"
typedef struct VEventStruct VEvent;
typedef enum {
VEVENT_READER_INSERT,
VEVENT_READER_REMOVE,
VEVENT_CARD_INSERT,
VEVENT_CARD_REMOVE,
VEVENT_LAST,
} VEventType;
struct VEventStruct {
VEvent *next;
VEventType type;
VReader *reader;
VCard *card;
};
#endif

138
src/glib-compat.h Normal file
View File

@ -0,0 +1,138 @@
/*
* GLIB Compatibility Functions
*
* Copyright IBM, Corp. 2013
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
* Michael Tokarev <mjt@tls.msk.ru>
* Paolo Bonzini <pbonzini@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef GLIB_COMPAT_H
#define GLIB_COMPAT_H
#include <glib.h>
#if !GLIB_CHECK_VERSION(2, 31, 0)
/* before glib-2.31, GMutex and GCond was dynamic-only (there was a separate
* GStaticMutex, but it didn't work with condition variables).
*
* Our implementation uses GOnce to fake a static implementation that does
* not require separate initialization.
* We need to rename the types to avoid passing our CompatGMutex/CompatGCond
* by mistake to a function that expects GMutex/GCond. However, for ease
* of use we keep the GLib function names. GLib uses macros for the
* implementation, we use inline functions instead and undefine the macros.
*/
typedef struct CompatGMutex {
GOnce once;
} CompatGMutex;
typedef struct CompatGCond {
GOnce once;
} CompatGCond;
static inline gpointer do_g_mutex_new(gpointer unused)
{
return (gpointer) g_mutex_new();
}
static inline void g_mutex_init(CompatGMutex *mutex)
{
mutex->once = (GOnce) G_ONCE_INIT;
}
static inline void g_mutex_clear(CompatGMutex *mutex)
{
g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS);
if (mutex->once.retval) {
g_mutex_free((GMutex *) mutex->once.retval);
}
mutex->once = (GOnce) G_ONCE_INIT;
}
static inline void (g_mutex_lock)(CompatGMutex *mutex)
{
g_once(&mutex->once, do_g_mutex_new, NULL);
g_mutex_lock((GMutex *) mutex->once.retval);
}
#undef g_mutex_lock
static inline gboolean (g_mutex_trylock)(CompatGMutex *mutex)
{
g_once(&mutex->once, do_g_mutex_new, NULL);
return g_mutex_trylock((GMutex *) mutex->once.retval);
}
#undef g_mutex_trylock
static inline void (g_mutex_unlock)(CompatGMutex *mutex)
{
g_mutex_unlock((GMutex *) mutex->once.retval);
}
#undef g_mutex_unlock
static inline gpointer do_g_cond_new(gpointer unused)
{
return (gpointer) g_cond_new();
}
static inline void g_cond_init(CompatGCond *cond)
{
cond->once = (GOnce) G_ONCE_INIT;
}
static inline void g_cond_clear(CompatGCond *cond)
{
g_assert(cond->once.status != G_ONCE_STATUS_PROGRESS);
if (cond->once.retval) {
g_cond_free((GCond *) cond->once.retval);
}
cond->once = (GOnce) G_ONCE_INIT;
}
static inline void (g_cond_wait)(CompatGCond *cond, CompatGMutex *mutex)
{
g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS);
g_once(&cond->once, do_g_cond_new, NULL);
g_cond_wait((GCond *) cond->once.retval, (GMutex *) mutex->once.retval);
}
#undef g_cond_wait
static inline void (g_cond_broadcast)(CompatGCond *cond)
{
g_once(&cond->once, do_g_cond_new, NULL);
g_cond_broadcast((GCond *) cond->once.retval);
}
#undef g_cond_broadcast
static inline void (g_cond_signal)(CompatGCond *cond)
{
g_once(&cond->once, do_g_cond_new, NULL);
g_cond_signal((GCond *) cond->once.retval);
}
#undef g_cond_signal
/* before 2.31 there was no g_thread_new() */
static inline GThread *g_thread_new(const char *name,
GThreadFunc func, gpointer data)
{
GThread *thread = g_thread_create(func, data, TRUE, NULL);
if (!thread) {
g_error("creating thread");
}
return thread;
}
#else
#define CompatGMutex GMutex
#define CompatGCond GCond
#endif /* glib 2.31 */
#endif

97
src/gp.c Normal file
View File

@ -0,0 +1,97 @@
/*
* defines the entry point for the Global Plarform Applet emulation. Only used
* by vcard_emul_type.c
*
* Copyright 2018 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "glib-compat.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "gp.h"
#include "vcard.h"
#include "vcard_emul.h"
#include "card_7816.h"
static unsigned char gp_container_aid[] = {
0xa0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00 };
/* Data returned for Get Data Instruction */
static unsigned char gp_get_data[] = {
0x9F, 0x7F, 0x2A, 0x40, 0x70, 0x50, 0x72, 0x12,
0x91, 0x51, 0x81, 0x01, 0x00, 0x70, 0x70, 0x00,
0x00, 0x58, 0xBD, 0x36, 0x0E, 0x40, 0x82, 0x70,
0x90, 0x12, 0x93, 0x70, 0x90, 0x04, 0x44, 0x72,
0x00, 0x00, 0x01, 0x00, 0x40, 0x04, 0x45, 0x84,
0x00, 0x00, 0x2C, 0x19, 0xB5
};
static VCardStatus
gp_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response)
{
VCardStatus ret = VCARD_FAIL;
unsigned int tag;
switch (apdu->a_ins) {
case GP_GET_DATA:
/* GET DATA isntruction for tags:
* 00 66 (not found):
* 9F 7F (len = 2D):
* 9F 7F 2A 40 70 50 72 12 91 51 81 01 00 70 70 00
* 00 58 BD 36 0E 40 82 70 90 12 93 70 90 04 44 72
* 00 00 01 00 40 04 45 84 00 00 2C 19 B5
*/
tag = (apdu->a_p1 & 0xff) << 8 | (apdu->a_p2 & 0xff);
if (tag == 0x9f7f) {
*response = vcard_response_new(card, gp_get_data,
sizeof(gp_get_data), apdu->a_Le, VCARD7816_STATUS_SUCCESS);
ret = VCARD_DONE;
break;
}
*response = vcard_make_response(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
ret = VCARD_DONE;
break;
default:
/* Let the ISO 7816 code to handle other APDUs */
ret = VCARD_NEXT;
break;
}
return ret;
}
/*
* Initialize the cac card. This is the only public function in this file. All
* the rest are connected through function pointers.
*/
VCardStatus
gp_card_init(VReader *reader, VCard *card)
{
VCardApplet *applet;
/* create Card Manager container */
applet = vcard_new_applet(gp_applet_container_process_apdu,
NULL, gp_container_aid,
sizeof(gp_container_aid));
if (applet == NULL) {
goto failure;
}
vcard_add_applet(card, applet);
return VCARD_DONE;
failure:
return VCARD_FAIL;
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */

27
src/gp.h Normal file
View File

@ -0,0 +1,27 @@
/*
* defines the entry point for the Global Plarform Applet emulation. Only used
* by vcard_emul_type.c
*
* Copyright 2018 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef GP_H
#define GP_H 1
#include "vcard.h"
#include "vreader.h"
#define GP_GET_DATA 0xCA
/*
* Initialize the Global Platform Applet. This is the only public function in
* this file. All the rest are connected through function pointers.
*/
VCardStatus
gp_card_init(VReader *reader, VCard *card);
#endif

29
src/libcacard.h Normal file
View File

@ -0,0 +1,29 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef LIBCACARD_H
#define LIBCACARD_H
#include <glib.h>
G_BEGIN_DECLS
#define __LIBCACARD_H_INSIDE__
#include "cac.h"
#include "card_7816.h"
#include "card_7816t.h"
#include "eventt.h"
#include "vcard_emul.h"
#include "vcard_emul_type.h"
#include "vcard.h"
#include "vcardt.h"
#include "vevent.h"
#include "vreader.h"
#include "vreadert.h"
#include "vscard_common.h"
G_END_DECLS
#endif

77
src/libcacard.syms Normal file
View File

@ -0,0 +1,77 @@
cac_card_init
vcard_add_applet
vcard_apdu_delete
vcard_apdu_new
vcard_applet_get_aid
vcard_buffer_response_delete
vcard_buffer_response_new
vcard_delete_applet
vcard_emul_delete_key
vcard_emul_force_card_insert
vcard_emul_force_card_remove
vcard_emul_get_atr
vcard_emul_get_login_count
vcard_emul_init
vcard_emul_login
vcard_emul_options
vcard_emul_replay_insertion_events
vcard_emul_reset
vcard_emul_rsa_op
vcard_emul_type_from_string
vcard_emul_type_select
vcard_emul_usage
vcard_find_applet
vcard_free
vcard_get_atr
vcard_get_buffer_response
vcard_get_current_applet_private
vcard_get_private
vcard_get_type
vcard_init
vcard_make_response
vcard_new
vcard_new_applet
vcard_process_apdu
vcard_process_applet_apdu
vcard_reference
vcard_reset
vcard_response_delete
vcard_response_new
vcard_response_new_bytes
vcard_response_new_data
vcard_response_new_status_bytes
vcard_response_set_status_bytes
vcard_select_applet
vcard_set_applet_private
vcard_set_atr_func
vcard_set_buffer_response
vcard_set_type
vevent_delete
vevent_get_next_vevent
vevent_new
vevent_queue_init
vevent_queue_vevent
vevent_wait_next_vevent
vreader_add_reader
vreader_card_is_present
vreader_free
vreader_get_id
vreader_get_name
vreader_get_private
vreader_get_reader_by_id
vreader_get_reader_by_name
vreader_get_reader_list
vreader_init
vreader_insert_card
vreader_list_delete
vreader_list_get_first
vreader_list_get_next
vreader_list_get_reader
vreader_new
vreader_power_off
vreader_power_on
vreader_queue_card_event
vreader_reference
vreader_remove_reader
vreader_set_id
vreader_xfr_bytes

348
src/simpletlv.c Normal file
View File

@ -0,0 +1,348 @@
/*
* simpletlv.c: Simple TLV encoding and decoding functions
*
* Copyright (C) 2016 - 2018 Red Hat, Inc.
*
* Authors: Robert Relyea <rrelyea@redhat.com>
* Jakub Jelen <jjelen@redhat.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include "simpletlv.h"
#include "common.h"
int
simpletlv_get_length(struct simpletlv_member *tlv, size_t tlv_len,
enum simpletlv_buffer_type buffer_type)
{
size_t i, len = 0;
int child_length;
for (i = 0; i < tlv_len; i++) {
/* This TLV is skipped */
if (tlv[i].type == SIMPLETLV_TYPE_NONE)
continue;
/* We can not unambiguously split the buffers
* for recursive structures
*/
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND
&& buffer_type != SIMPLETLV_BOTH)
return -1;
child_length = tlv[i].length;
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
child_length = simpletlv_get_length(tlv[i].value.child,
tlv[i].length, SIMPLETLV_BOTH);
}
if (buffer_type & SIMPLETLV_TL) {
len += 1/*TAG*/;
if (child_length < 255)
len += 1;
else
len += 3;
}
if (buffer_type & SIMPLETLV_VALUE) {
len += child_length;
}
}
return len;
}
static int
simpletlv_encode_internal(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen,
unsigned char **newptr, int buffer_type)
{
unsigned char *tmp = NULL, *a = NULL, *p, *newp;
size_t tmp_len = 0, p_len, i;
int expect_len = 0, rv;
expect_len = simpletlv_get_length(tlv, tlv_len, buffer_type);
if (expect_len <= 0)
return expect_len;
if (outlen == 0) {
/* allocate a new buffer */
a = g_malloc(expect_len);
tmp = a;
tmp_len = expect_len;
} else if ((int)outlen >= expect_len) {
tmp = *out;
tmp_len = outlen;
} else {
/* we can not fit the data */
return -1;
}
p = tmp;
p_len = tmp_len;
for (i = 0; i < tlv_len; i++) {
size_t child_length = tlv[i].length;
/* This TLV is skipped */
if (tlv[i].type == SIMPLETLV_TYPE_NONE)
continue;
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
child_length = simpletlv_get_length(tlv[i].value.child,
tlv[i].length, SIMPLETLV_BOTH);
}
if (buffer_type & SIMPLETLV_TL) {
rv = simpletlv_put_tag(tlv[i].tag, child_length,
p, p_len, &newp);
if (rv < 0)
goto failure;
p = newp;
}
if (buffer_type & SIMPLETLV_VALUE) {
if (tlv[i].type == SIMPLETLV_TYPE_LEAF) {
memcpy(p, tlv[i].value.value, tlv[i].length);
p += tlv[i].length;
} else {
/* recurse */
rv = simpletlv_encode_internal(tlv[i].value.child,
tlv[i].length, &p, p_len, &newp, buffer_type);
if (rv < 0)
goto failure;
p = newp;
}
}
p_len = tmp_len - (p - tmp);
}
if (newptr)
*newptr = p;
if (out)
*out = tmp;
return tmp_len - p_len;
failure:
g_free(a);
return -1;
}
int
simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen, unsigned char **newptr)
{
return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
SIMPLETLV_BOTH);
}
int
simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen, unsigned char **newptr)
{
return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
SIMPLETLV_TL);
}
int
simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen, unsigned char **newptr)
{
return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
SIMPLETLV_VALUE);
}
/*
* Put a tag/length record to a file in Simple TLV based on the datalen
* content length.
*/
int
simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
size_t outlen, unsigned char **ptr)
{
unsigned char *p = out;
if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
return -1;
/* tag is just number between 0x01 and 0xFE */
if (tag == 0x00 || tag == 0xff)
return -1;
*p++ = tag; /* tag is single byte */
if (datalen < 0xff) {
/* short value up to 255 */
*p++ = (unsigned char)datalen; /* is in the second byte */
} else if (datalen < 0xffff) {
/* longer values up to 65535 */
*p++ = (unsigned char)0xff; /* first byte is 0xff */
*p++ = (unsigned char)datalen & 0xff;
*p++ = (unsigned char)(datalen >> 8) & 0xff; /* LE */
} else {
/* we can't store more than two bytes in Simple TLV */
return -1;
}
if (ptr != NULL)
*ptr = p;
return 0;
}
/* Read the TL file and return appropriate tag and the length of associated
* content.
*/
int
simpletlv_read_tag(unsigned char **buf, size_t buflen, unsigned char *tag_out,
size_t *taglen)
{
size_t len;
unsigned char *p = *buf;
if (buflen < 2) {
*buf = p+buflen;
return -1;
}
*tag_out = *p++;
len = *p++;
if (len == 0xff) {
/* don't crash on bad data */
if (buflen < 4) {
*taglen = 0;
return -1;
}
/* skip two bytes (the size) */
len = lebytes2ushort(p);
p+=2;
}
*taglen = len;
*buf = p;
return 0;
}
/*
* Merges two structures into one, creating a new shallow copy of both
* of the structures.
* Resulting length is the sum of a_len and b_len arguments.
*/
struct simpletlv_member *
simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
const struct simpletlv_member *b, size_t b_len)
{
int offset;
struct simpletlv_member *r;
size_t r_len = a_len + b_len;
r = g_new(struct simpletlv_member, r_len);
/* the ugly way */
offset = a_len * sizeof(struct simpletlv_member);
memcpy(r, a, offset);
memcpy(&r[a_len], b, b_len * sizeof(struct simpletlv_member));
return r;
}
void
simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen)
{
size_t i;
if (tlv == NULL)
return;
for (i = 0; i < tlvlen; i++) {
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
simpletlv_free(tlv[i].value.child, tlv[i].length);
} else {
g_free(tlv[i].value.value);
}
}
g_free(tlv);
}
struct simpletlv_member *
simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen)
{
size_t i = 0, j;
struct simpletlv_member *new = NULL;
new = g_new(struct simpletlv_member, tlvlen);
for (i = 0; i < tlvlen; i++) {
new[i].type = tlv[i].type;
new[i].tag = tlv[i].tag;
new[i].length = tlv[i].length;
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
new[i].value.child = simpletlv_clone(
tlv[i].value.child, tlv[i].length);
if (new[i].value.child == NULL)
goto failure;
} else {
new[i].value.value = g_new(unsigned char, tlv[i].length);
memcpy(new[i].value.value, tlv[i].value.value,
tlv[i].length);
}
}
return new;
failure:
for (j = 0; j < i; i++) {
if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
simpletlv_free(new[i].value.child, new[i].length);
} else {
g_free(new[i].value.value);
}
}
g_free(new);
return NULL;
}
struct simpletlv_member *
simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len)
{
unsigned char *p, *p_end;
unsigned char tag;
size_t vlen;
GArray *tlv = g_array_new(FALSE, FALSE, sizeof(struct simpletlv_member));
p = data;
p_end = p + data_len;
while (p < p_end) {
struct simpletlv_member tlvp;
/* we can return what was parsed successfully */
if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
break;
}
if (vlen > (size_t) (p_end - p)) {
break;
}
tlvp.tag = tag;
tlvp.length = vlen;
tlvp.value.value = g_memdup(p, vlen);
tlvp.type = SIMPLETLV_TYPE_LEAF;
g_array_append_val(tlv, tlvp);
p += vlen;
}
*outtlv_len = tlv->len;
return (struct simpletlv_member *)g_array_free(tlv, FALSE);
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */

143
src/simpletlv.h Normal file
View File

@ -0,0 +1,143 @@
/*
* simpletlv.h: Simple TLV header file
*
* Copyright (C) 2016 Red Hat, Inc.
*
* Authors: Robert Relyea <rrelyea@redhat.com>
* Jakub Jelen <jjelen@redhat.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SIMPLETLV_H
#define _SIMPLETLV_H
enum simpletlv_type {
SIMPLETLV_TYPE_NONE = 0,
SIMPLETLV_TYPE_LEAF = 1,
SIMPLETLV_TYPE_COMPOUND = 2
};
enum simpletlv_buffer_type {
SIMPLETLV_TL = 0x01,
SIMPLETLV_VALUE = 0x02,
SIMPLETLV_BOTH = 0x03
};
struct simpletlv_member {
unsigned char tag;
unsigned int length;
union {
unsigned char *value;
struct simpletlv_member *child;
} value;
enum simpletlv_type type;
};
/*
* Calculate expected length of TLV buffer
* @param ltv array of TLV structures to encode
* @param tlvlen number of members in the array to encode
* @param buffer_type Encode only tags + lengths, values or both
* @return The length of the data in the TLV record,
* -1 on errors
*/
int
simpletlv_get_length(struct simpletlv_member *, size_t,
enum simpletlv_buffer_type);
/*
* Deallocate all parts of dynamically allocated SimpleTLV structure
*/
void
simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen);
/*
* Merges two structures into one, creating a new shallow copy of both
* of the structures.
* Resulting length is the sum of a_len and b_len arguments.
*/
struct simpletlv_member *
simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
const struct simpletlv_member *b, size_t b_len);
/*
* Encode structure into SimpleTLV format, TL together with V
* @param tlv array of TLV structures to encode
* @param tlvlen number of members in the array to encode
* @param out Byte array to write into
* @param outlen The length of output array
* @param ptr The end of TLV record
* @return The length of the encoded data, -1 on errors
*/
int
simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen, unsigned char **ptr);
int
simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen, unsigned char **newptr);
int
simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
unsigned char **out, size_t outlen, unsigned char **newptr);
/*
* Create a tag/length file in Simple TLV based on the val_len content length
* @param tag Tag to store into the TL file
* @param datalen Data length to store into the TL file
* @param out TL byte array to write into
* @param outlen The length of the output array
* @param ptr The end of the TL record written
* @return SC_SUCCESS for correct input
*/
int
simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
size_t outlen, unsigned char **ptr);
/* get the Simple TLV tag and length.
* @param buf Pointer to the TL file
* @param buflen The length of TL file
* @param tag_out The tag from the TL file
* @param taglen The length of the V record
* @return SC_SUCCESS on valid input
*/
int
simpletlv_read_tag(unsigned char **buf, size_t buflen,
unsigned char *tag_out, size_t *taglen);
/* create a deep copy of the SimpleTLV structure
*
* The calling function is responsible for freeing the structure and
* all its children by calling simpletlv_free().
*/
struct simpletlv_member *
simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen);
/* parse the SimpleTLV compound buffer into internal simpletlv structures
*
* The returned structure is NEVER recursive, since thre is no unambiguous
* way how to determine the recursive structures without the knowledge of
* a scheme in advance.
*
* The calling function is responsible for freeing the structure and its
* children by calling simpletlv_free().
*
*/
struct simpletlv_member *
simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len);
#endif

321
src/vcard.c Normal file
View File

@ -0,0 +1,321 @@
/*
* implement the Java card standard.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "glib-compat.h"
#include <string.h>
#include "vcard.h"
#include "vcard_emul.h"
#include "card_7816t.h"
struct VCardAppletStruct {
VCardApplet *next;
VCardProcessAPDU process_apdu;
VCardResetApplet reset_applet;
unsigned char *aid;
int aid_len;
void *applet_private;
VCardAppletPrivateFree applet_private_free;
};
struct VCardStruct {
int reference_count;
VCardApplet *applet_list;
VCardApplet *current_applet[MAX_CHANNEL];
VCardBufferResponse *vcard_buffer_response;
VCardType type;
VCardEmul *vcard_private;
VCardEmulFree vcard_private_free;
VCardGetAtr vcard_get_atr;
};
VCardBufferResponse *
vcard_buffer_response_new(const unsigned char *buffer, int size)
{
VCardBufferResponse *new_buffer;
new_buffer = g_new(VCardBufferResponse, 1);
new_buffer->buffer = (unsigned char *)g_memdup(buffer, size);
new_buffer->buffer_len = size;
new_buffer->current = new_buffer->buffer;
new_buffer->len = size;
return new_buffer;
}
void
vcard_buffer_response_delete(VCardBufferResponse *buffer_response)
{
if (buffer_response == NULL) {
return;
}
g_free(buffer_response->buffer);
g_free(buffer_response);
}
/*
* clean up state after a reset
*/
void
vcard_reset(VCard *card, VCardPower power)
{
int i;
VCardApplet *applet = NULL;
if (card->type == VCARD_DIRECT) {
/* select the last applet */
VCardApplet *current_applet = NULL;
for (current_applet = card->applet_list; current_applet;
current_applet = current_applet->next) {
applet = current_applet;
}
}
for (i = 0; i < MAX_CHANNEL; i++) {
card->current_applet[i] = applet;
}
if (card->vcard_buffer_response) {
vcard_buffer_response_delete(card->vcard_buffer_response);
card->vcard_buffer_response = NULL;
}
vcard_emul_reset(card, power);
if (applet) {
applet->reset_applet(card, 0);
}
}
/* applet utilities */
/*
* applet utilities
*/
/* constructor */
VCardApplet *
vcard_new_applet(VCardProcessAPDU applet_process_function,
VCardResetApplet applet_reset_function,
const unsigned char *aid, int aid_len)
{
VCardApplet *applet;
applet = g_new0(VCardApplet, 1);
applet->process_apdu = applet_process_function;
applet->reset_applet = applet_reset_function;
applet->aid = g_memdup(aid, aid_len);
applet->aid_len = aid_len;
return applet;
}
/* destructor */
void
vcard_delete_applet(VCardApplet *applet)
{
if (applet == NULL) {
return;
}
if (applet->applet_private_free) {
applet->applet_private_free(applet->applet_private);
}
g_free(applet->aid);
g_free(applet);
}
/* accessor */
void
vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private,
VCardAppletPrivateFree private_free)
{
if (applet->applet_private_free) {
applet->applet_private_free(applet->applet_private);
}
applet->applet_private = private;
applet->applet_private_free = private_free;
}
VCard *
vcard_new(VCardEmul *private, VCardEmulFree private_free)
{
VCard *new_card;
new_card = g_new0(VCard, 1);
new_card->type = VCARD_VM;
new_card->vcard_private = private;
new_card->vcard_private_free = private_free;
new_card->reference_count = 1;
return new_card;
}
VCard *
vcard_reference(VCard *vcard)
{
if (vcard == NULL) {
return NULL;
}
vcard->reference_count++;
return vcard;
}
void
vcard_free(VCard *vcard)
{
VCardApplet *current_applet;
VCardApplet *next_applet;
if (vcard == NULL) {
return;
}
vcard->reference_count--;
if (vcard->reference_count != 0) {
return;
}
if (vcard->vcard_private_free) {
(*vcard->vcard_private_free)(vcard->vcard_private);
}
for (current_applet = vcard->applet_list; current_applet;
current_applet = next_applet) {
next_applet = current_applet->next;
vcard_delete_applet(current_applet);
}
vcard_buffer_response_delete(vcard->vcard_buffer_response);
g_free(vcard);
}
void
vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len)
{
if (vcard->vcard_get_atr) {
(*vcard->vcard_get_atr)(vcard, atr, atr_len);
return;
}
vcard_emul_get_atr(vcard, atr, atr_len);
}
void
vcard_set_atr_func(VCard *card, VCardGetAtr get_atr)
{
card->vcard_get_atr = get_atr;
}
VCardStatus
vcard_add_applet(VCard *card, VCardApplet *applet)
{
applet->next = card->applet_list;
card->applet_list = applet;
/* if our card-type is direct, always call the applet */
if (card->type == VCARD_DIRECT) {
int i;
for (i = 0; i < MAX_CHANNEL; i++) {
card->current_applet[i] = applet;
}
}
return VCARD_DONE;
}
/*
* manage applets
*/
VCardApplet *
vcard_find_applet(VCard *card, const unsigned char *aid, int aid_len)
{
VCardApplet *current_applet;
for (current_applet = card->applet_list; current_applet;
current_applet = current_applet->next) {
if (current_applet->aid_len != aid_len) {
continue;
}
if (memcmp(current_applet->aid, aid, aid_len) == 0) {
break;
}
}
return current_applet;
}
unsigned char *
vcard_applet_get_aid(VCardApplet *applet, int *aid_len)
{
if (applet == NULL) {
return NULL;
}
*aid_len = applet->aid_len;
return applet->aid;
}
void
vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
{
g_assert(channel >= 0 && channel < MAX_CHANNEL);
card->current_applet[channel] = applet;
/* reset the applet */
if (applet && applet->reset_applet) {
applet->reset_applet(card, channel);
}
}
VCardAppletPrivate *
vcard_get_current_applet_private(VCard *card, int channel)
{
VCardApplet *applet = card->current_applet[channel];
if (applet == NULL) {
return NULL;
}
return applet->applet_private;
}
VCardStatus
vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response)
{
if (card->current_applet[apdu->a_channel]) {
return card->current_applet[apdu->a_channel]->process_apdu(
card, apdu, response);
}
return VCARD_NEXT;
}
/*
* Accessor functions
*/
/* accessor functions for the response buffer */
VCardBufferResponse *
vcard_get_buffer_response(VCard *card)
{
return card->vcard_buffer_response;
}
void
vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer)
{
card->vcard_buffer_response = buffer;
}
/* accessor functions for the type */
VCardType
vcard_get_type(VCard *card)
{
return card->type;
}
void
vcard_set_type(VCard *card, VCardType type)
{
card->type = type;
}
/* accessor for private data */
VCardEmul *
vcard_get_private(VCard *vcard)
{
return vcard->vcard_private;
}

86
src/vcard.h Normal file
View File

@ -0,0 +1,86 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VCARD_H
#define VCARD_H 1
#include "vcardt.h"
/*
* response buffer constructors and destructors.
*
* response buffers are used when we need to return more data than will fit in
* a normal APDU response (nominally 254 bytes).
*/
VCardBufferResponse *vcard_buffer_response_new(const unsigned char *buffer, int size);
void vcard_buffer_response_delete(VCardBufferResponse *buffer_response);
/*
* clean up state on reset
*/
void vcard_reset(VCard *card, VCardPower power);
/*
* applet utilities
*/
/*
* Constructor for a VCardApplet
*/
VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
VCardResetApplet applet_reset_function,
const unsigned char *aid, int aid_len);
/*
* destructor for a VCardApplet
* Can be called with a NULL applet
*/
void vcard_delete_applet(VCardApplet *applet);
/* accessor - set the card type specific private data */
void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private,
VCardAppletPrivateFree private_free);
/* set type of vcard */
void vcard_set_type(VCard *card, VCardType type);
/*
* utilities interacting with the current applet
*/
/* add a new applet to a card */
VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
/* find the applet on the card with the given aid */
VCardApplet *vcard_find_applet(VCard *card, const unsigned char *aid, int aid_len);
/* set the following applet to be current on the given channel */
void vcard_select_applet(VCard *card, int channel, VCardApplet *applet);
/* get the card type specific private data on the given channel */
VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel);
/* fetch the applet's id */
unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len);
/* process the apdu for the current selected applet/file */
VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
VCardResponse **response);
/*
* VCard utilities
*/
/* constructor */
VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free);
/* get a reference */
VCard *vcard_reference(VCard *);
/* destructor (reference counted) */
void vcard_free(VCard *);
/* get the atr from the card */
void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len);
void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr);
/* accessor functions for the response buffer */
VCardBufferResponse *vcard_get_buffer_response(VCard *card);
void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer);
/* accessor functions for the type */
VCardType vcard_get_type(VCard *card);
/* get the private data */
VCardEmul *vcard_get_private(VCard *card);
#endif

71
src/vcard_emul.h Normal file
View File

@ -0,0 +1,71 @@
/*
* This is the actual card emulator.
*
* These functions can be implemented in different ways on different platforms
* using the underlying system primitives. For Linux it uses NSS, though direct
* to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
* used. On Windows CAPI could be used.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VCARD_EMUL_H
#define VCARD_EMUL_H 1
#include "card_7816t.h"
#include "vcard.h"
#include "vcard_emul_type.h"
/*
* types
*/
typedef enum {
VCARD_EMUL_OK = 0,
VCARD_EMUL_FAIL,
/* return values by vcard_emul_init */
VCARD_EMUL_INIT_ALREADY_INITED,
} VCardEmulError;
/* options are emul specific. call card_emul_parse_args to change a string
* To an options struct */
typedef struct VCardEmulOptionsStruct VCardEmulOptions;
/*
* Login functions
*/
/* return the number of login attempts still possible on the card. if unknown,
* return -1 */
int vcard_emul_get_login_count(VCard *card);
/* login into the card, return the 7816 status word (sw2 || sw1) */
vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin,
int pin_len);
void vcard_emul_logout(VCard *card);
/*
* key functions
*/
/* delete a key */
void vcard_emul_delete_key(VCardKey *key);
int vcard_emul_rsa_bits(VCardKey *key);
/* RSA sign/decrypt with the key, signature happens 'in place' */
vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key,
unsigned char *buffer, int buffer_size);
void vcard_emul_reset(VCard *card, VCardPower power);
void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len);
/* Re-insert of a card that has been removed by force removal */
VCardEmulError vcard_emul_force_card_insert(VReader *vreader);
/* Force a card removal even if the card is not physically removed */
VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
VCardEmulOptions *vcard_emul_options(const char *args);
VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
void vcard_emul_replay_insertion_events(void);
void vcard_emul_usage(void);
unsigned char *vcard_emul_read_object(VCard *card, const char *label,
unsigned int *ret_len);
#endif

1400
src/vcard_emul_nss.c Normal file

File diff suppressed because it is too large Load Diff

61
src/vcard_emul_type.c Normal file
View File

@ -0,0 +1,61 @@
/*
* This file contains utility functions which abstract the different card
* types. The goal is that new card types can easily be added by simply
* changing this file and vcard_emul_type.h. It is currently not a requirement
* to dynamically add new card types.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "config.h"
#include <strings.h>
#include "vcardt.h"
#include "vcard_emul_type.h"
#include "cac.h"
#include "gp.h"
#include "glib-compat.h"
VCardStatus vcard_init(VReader *vreader, VCard *vcard,
VCardEmulType type, const char *params,
unsigned char *const *cert, int cert_len[],
VCardKey *key[], int cert_count)
{
int rv;
switch (type) {
case VCARD_EMUL_NONE:
break;
case VCARD_EMUL_CAC:
rv = cac_card_init(vreader, vcard, params,
cert, cert_len, key, cert_count);
if (rv == VCARD_DONE)
rv = gp_card_init(vreader, vcard);
return rv;
/* add new ones here */
case VCARD_EMUL_PASSTHRU:
default:
g_warn_if_reached();
break;
}
return VCARD_FAIL;
}
VCardEmulType vcard_emul_type_select(VReader *vreader)
{
/* return the default */
return VCARD_EMUL_CAC;
}
VCardEmulType vcard_emul_type_from_string(const char *type_string)
{
if (strcasecmp(type_string, "CAC") == 0) {
return VCARD_EMUL_CAC;
}
#ifdef ENABLE_PCSC
if (strcasecmp(type_string, "PASSTHRU") == 0) {
return VCARD_EMUL_PASSTHRU;
}
#endif
return VCARD_EMUL_NONE;
}

33
src/vcard_emul_type.h Normal file
View File

@ -0,0 +1,33 @@
/*
* This header file abstracts the different card types. The goal is new card
* types can easily be added by simply changing this file and
* vcard_emul_type.c. It is currently not a requirement to dynamically add new
* card types.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VCARD_EMUL_TYPE_H
#define VCARD_EMUL_TYPE_H 1
#include "vcardt.h"
#include "vreadert.h"
/*
* types
*/
typedef enum {
VCARD_EMUL_NONE = 0,
VCARD_EMUL_CAC,
VCARD_EMUL_PASSTHRU
} VCardEmulType;
/* functions used by the rest of the emulator */
VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type,
const char *params, unsigned char * const *cert,
int cert_len[], VCardKey *key[], int cert_count);
VCardEmulType vcard_emul_type_select(VReader *vreader);
VCardEmulType vcard_emul_type_from_string(const char *type_string);
#endif

40
src/vcardt.c Normal file
View File

@ -0,0 +1,40 @@
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "vcardt.h"
#include "vcardt_internal.h"
/* create an ATR with appropriate historical bytes */
#define ATR_TS_DIRECT_CONVENTION 0x3b
#define ATR_TA_PRESENT 0x10
#define ATR_TB_PRESENT 0x20
#define ATR_TC_PRESENT 0x40
#define ATR_TD_PRESENT 0x80
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len)
{
int postfix_len;
const char prefix[] = "VCARD_";
const char default_postfix[] = "DEFAULT";
const int prefix_len = sizeof(prefix) - 1;
int total_len;
unsigned char *atr;
if (postfix == NULL) {
postfix = default_postfix;
}
postfix_len = strlen(postfix);
total_len = 3 + prefix_len + postfix_len;
atr = g_malloc(total_len);
atr[0] = ATR_TS_DIRECT_CONVENTION;
atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len;
atr[2] = 0x00;
memcpy(&atr[3], prefix, prefix_len);
memcpy(&atr[3 + prefix_len], postfix, postfix_len);
if (atr_len) {
*atr_len = total_len;
}
return atr;
}

59
src/vcardt.h Normal file
View File

@ -0,0 +1,59 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VCARDT_H
#define VCARDT_H 1
/*
* these should come from some common spice header file
*/
#include <assert.h>
#ifndef MIN
#define MIN(x, y) ((x) > (y) ? (y) : (x))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
typedef struct VCardStruct VCard;
typedef struct VCardAPDUStruct VCardAPDU;
typedef struct VCardResponseStruct VCardResponse;
typedef struct VCardBufferResponseStruct VCardBufferResponse;
typedef struct VCardAppletStruct VCardApplet;
typedef struct VCardAppletPrivateStruct VCardAppletPrivate;
typedef struct VCardKeyStruct VCardKey; /* opaque */
typedef struct VCardEmulStruct VCardEmul;
#define MAX_CHANNEL 4
typedef enum {
VCARD_DONE,
VCARD_NEXT,
VCARD_FAIL
} VCardStatus;
typedef enum {
VCARD_FILE_SYSTEM,
VCARD_VM,
VCARD_DIRECT
} VCardType;
typedef enum {
VCARD_POWER_ON,
VCARD_POWER_OFF
} VCardPower;
typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
VCardResponse **response);
typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel);
typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
typedef void (*VCardEmulFree) (VCardEmul *);
typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len);
struct VCardBufferResponseStruct {
unsigned char *buffer;
int buffer_len;
unsigned char *current;
int len;
};
#endif

6
src/vcardt_internal.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef VCARDT_INTERNAL_H
#define VCARDT_INTERNAL_H
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len);
#endif

28
src/vevent.h Normal file
View File

@ -0,0 +1,28 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef EVENT_H
#define EVENT_H 1
#include "eventt.h"
#include "vreadert.h"
#include "vcardt.h"
VEvent *vevent_new(VEventType type, VReader *reader, VCard *card);
void vevent_delete(VEvent *);
/*
* VEvent queueing services
*/
void vevent_queue_vevent(VEvent *);
void vevent_queue_init(void);
/*
* VEvent dequeing services
*/
VEvent *vevent_wait_next_vevent(void);
VEvent *vevent_get_next_vevent(void);
#endif

583
src/vreader.c Normal file
View File

@ -0,0 +1,583 @@
/*
* emulate the reader
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include "glib-compat.h"
#include <string.h>
#include "vcard.h"
#include "vcard_emul.h"
#include "card_7816.h"
#include "vreader.h"
#include "vevent.h"
#include "cac.h" /* just for debugging defines */
struct VReaderStruct {
int reference_count;
VCard *card;
char *name;
vreader_id_t id;
CompatGMutex lock;
VReaderEmul *reader_private;
VReaderEmulFree reader_private_free;
};
/*
* Debug helpers
*/
static const char *
apdu_ins_to_string(int ins)
{
switch (ins) {
case VCARD7816_INS_MANAGE_CHANNEL:
return "manage channel";
case VCARD7816_INS_EXTERNAL_AUTHENTICATE:
return "external authenticate";
case VCARD7816_INS_GET_CHALLENGE:
return "get challenge";
case VCARD7816_INS_INTERNAL_AUTHENTICATE:
return "internal authenticate";
case VCARD7816_INS_ERASE_BINARY:
return "erase binary";
case VCARD7816_INS_READ_BINARY:
return "read binary";
case VCARD7816_INS_WRITE_BINARY:
return "write binary";
case VCARD7816_INS_UPDATE_BINARY:
return "update binary";
case VCARD7816_INS_READ_RECORD:
return "read record";
case VCARD7816_INS_WRITE_RECORD:
return "write record";
case VCARD7816_INS_UPDATE_RECORD:
return "update record";
case VCARD7816_INS_APPEND_RECORD:
return "append record";
case VCARD7816_INS_ENVELOPE:
return "envelope";
case VCARD7816_INS_PUT_DATA:
return "put data";
case VCARD7816_INS_GET_DATA:
return "get data";
case VCARD7816_INS_SELECT_FILE:
return "select file";
case VCARD7816_INS_VERIFY:
return "verify";
case VCARD7816_INS_GET_RESPONSE:
return "get response";
case CAC_GET_PROPERTIES:
return "get properties";
case CAC_GET_ACR:
return "get acr";
case CAC_READ_BUFFER:
return "read buffer";
case CAC_UPDATE_BUFFER:
return "update buffer";
case CAC_SIGN_DECRYPT:
return "sign decrypt";
case CAC_GET_CERTIFICATE:
return "get certificate";
default:
g_return_val_if_reached("unknown");
}
}
/* manage locking */
static inline void
vreader_lock(VReader *reader)
{
g_mutex_lock(&reader->lock);
}
static inline void
vreader_unlock(VReader *reader)
{
g_mutex_unlock(&reader->lock);
}
/*
* vreader constructor
*/
VReader *
vreader_new(const char *name, VReaderEmul *private,
VReaderEmulFree private_free)
{
VReader *reader;
reader = g_new(VReader, 1);
g_mutex_init(&reader->lock);
reader->reference_count = 1;
reader->name = g_strdup(name);
reader->card = NULL;
reader->id = (vreader_id_t)-1;
reader->reader_private = private;
reader->reader_private_free = private_free;
return reader;
}
/* get a reference */
VReader*
vreader_reference(VReader *reader)
{
if (reader == NULL) {
return NULL;
}
vreader_lock(reader);
reader->reference_count++;
vreader_unlock(reader);
return reader;
}
/* free a reference */
void
vreader_free(VReader *reader)
{
if (reader == NULL) {
return;
}
vreader_lock(reader);
if (reader->reference_count-- > 1) {
vreader_unlock(reader);
return;
}
vreader_unlock(reader);
g_mutex_clear(&reader->lock);
if (reader->card) {
vcard_free(reader->card);
}
g_free(reader->name);
if (reader->reader_private_free) {
reader->reader_private_free(reader->reader_private);
}
g_free(reader);
}
static VCard *
vreader_get_card(VReader *reader)
{
VCard *card;
vreader_lock(reader);
card = vcard_reference(reader->card);
vreader_unlock(reader);
return card;
}
VReaderStatus
vreader_card_is_present(VReader *reader)
{
VCard *card = vreader_get_card(reader);
if (card == NULL) {
return VREADER_NO_CARD;
}
vcard_free(card);
return VREADER_OK;
}
vreader_id_t
vreader_get_id(VReader *reader)
{
if (reader == NULL) {
return (vreader_id_t)-1;
}
return reader->id;
}
VReaderStatus
vreader_set_id(VReader *reader, vreader_id_t id)
{
if (reader == NULL) {
return VREADER_NO_CARD;
}
reader->id = id;
return VREADER_OK;
}
const char *
vreader_get_name(VReader *reader)
{
if (reader == NULL) {
return NULL;
}
return reader->name;
}
VReaderEmul *
vreader_get_private(VReader *reader)
{
return reader->reader_private;
}
static VReaderStatus
vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
{
VCard *card = vreader_get_card(reader);
if (card == NULL) {
return VREADER_NO_CARD;
}
/*
* clean up our state
*/
vcard_reset(card, power);
if (atr) {
vcard_get_atr(card, atr, len);
}
vcard_free(card); /* free our reference */
return VREADER_OK;
}
VReaderStatus
vreader_power_on(VReader *reader, unsigned char *atr, int *len)
{
return vreader_reset(reader, VCARD_POWER_ON, atr, len);
}
VReaderStatus
vreader_power_off(VReader *reader)
{
return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
}
VReaderStatus
vreader_xfr_bytes(VReader *reader,
unsigned char *send_buf, int send_buf_len,
unsigned char *receive_buf, int *receive_buf_len)
{
VCardAPDU *apdu;
VCardResponse *response = NULL;
VCardStatus card_status;
VReaderStatus ret;
unsigned short status;
VCard *card = vreader_get_card(reader);
int size;
if (card == NULL) {
return VREADER_NO_CARD;
}
apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
if (apdu == NULL) {
response = vcard_make_response(status);
card_status = VCARD_DONE;
} else {
g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s",
__func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2,
apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins));
card_status = vcard_process_apdu(card, apdu, &response);
if (response) {
g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)",
__func__, response->b_status, response->b_sw1,
response->b_sw2, response->b_len, response->b_total_len);
}
}
if (card_status == VCARD_FAIL) {
*receive_buf_len = 0;
ret = VREADER_NO_CARD;
goto exit;
}
assert(card_status == VCARD_DONE && response);
size = MIN(*receive_buf_len, response->b_total_len);
memcpy(receive_buf, response->b_data, size);
*receive_buf_len = size;
ret = VREADER_OK;
exit:
vcard_response_delete(response);
vcard_apdu_delete(apdu);
vcard_free(card); /* free our reference */
return ret;
}
struct VReaderListStruct {
VReaderListEntry *head;
VReaderListEntry *tail;
};
struct VReaderListEntryStruct {
VReaderListEntry *next;
VReaderListEntry *prev;
VReader *reader;
};
static VReaderListEntry *
vreader_list_entry_new(VReader *reader)
{
VReaderListEntry *new_reader_list_entry;
new_reader_list_entry = g_new0(VReaderListEntry, 1);
new_reader_list_entry->reader = vreader_reference(reader);
return new_reader_list_entry;
}
static void
vreader_list_entry_delete(VReaderListEntry *entry)
{
if (entry == NULL) {
return;
}
vreader_free(entry->reader);
g_free(entry);
}
static VReaderList *
vreader_list_new(void)
{
VReaderList *new_reader_list;
new_reader_list = g_new0(VReaderList, 1);
return new_reader_list;
}
void
vreader_list_delete(VReaderList *list)
{
VReaderListEntry *current_entry;
VReaderListEntry *next_entry;
for (current_entry = vreader_list_get_first(list); current_entry;
current_entry = next_entry) {
next_entry = vreader_list_get_next(current_entry);
vreader_list_entry_delete(current_entry);
}
g_free(list);
}
VReaderListEntry *
vreader_list_get_first(VReaderList *list)
{
return list ? list->head : NULL;
}
VReaderListEntry *
vreader_list_get_next(VReaderListEntry *current)
{
return current ? current->next : NULL;
}
VReader *
vreader_list_get_reader(VReaderListEntry *entry)
{
return entry ? vreader_reference(entry->reader) : NULL;
}
static void
vreader_queue(VReaderList *list, VReaderListEntry *entry)
{
if (entry == NULL) {
return;
}
entry->next = NULL;
entry->prev = list->tail;
if (list->head) {
list->tail->next = entry;
} else {
list->head = entry;
}
list->tail = entry;
}
static void
vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
{
if (entry == NULL) {
return;
}
if (entry->next == NULL) {
list->tail = entry->prev;
} else if (entry->prev == NULL) {
list->head = entry->next;
} else {
entry->prev->next = entry->next;
entry->next->prev = entry->prev;
}
if ((list->tail == NULL) || (list->head == NULL)) {
list->head = list->tail = NULL;
}
entry->next = entry->prev = NULL;
}
static VReaderList *vreader_list;
static CompatGMutex vreader_list_mutex;
static void
vreader_list_init(void)
{
vreader_list = vreader_list_new();
}
static void
vreader_list_lock(void)
{
g_mutex_lock(&vreader_list_mutex);
}
static void
vreader_list_unlock(void)
{
g_mutex_unlock(&vreader_list_mutex);
}
static VReaderList *
vreader_copy_list(VReaderList *list)
{
VReaderList *new_list;
VReaderListEntry *current_entry;
new_list = vreader_list_new();
if (new_list == NULL) {
return NULL;
}
for (current_entry = vreader_list_get_first(list); current_entry;
current_entry = vreader_list_get_next(current_entry)) {
VReader *reader = vreader_list_get_reader(current_entry);
VReaderListEntry *new_entry = vreader_list_entry_new(reader);
vreader_free(reader);
vreader_queue(new_list, new_entry);
}
return new_list;
}
VReaderList *
vreader_get_reader_list(void)
{
VReaderList *new_reader_list;
vreader_list_lock();
new_reader_list = vreader_copy_list(vreader_list);
vreader_list_unlock();
return new_reader_list;
}
VReader *
vreader_get_reader_by_id(vreader_id_t id)
{
VReader *reader = NULL;
VReaderListEntry *current_entry;
if (id == (vreader_id_t) -1) {
return NULL;
}
vreader_list_lock();
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
current_entry = vreader_list_get_next(current_entry)) {
VReader *creader = vreader_list_get_reader(current_entry);
if (creader->id == id) {
reader = creader;
break;
}
vreader_free(creader);
}
vreader_list_unlock();
return reader;
}
VReader *
vreader_get_reader_by_name(const char *name)
{
VReader *reader = NULL;
VReaderListEntry *current_entry;
vreader_list_lock();
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
current_entry = vreader_list_get_next(current_entry)) {
VReader *creader = vreader_list_get_reader(current_entry);
if (strcmp(creader->name, name) == 0) {
reader = creader;
break;
}
vreader_free(creader);
}
vreader_list_unlock();
return reader;
}
/* called from card_emul to initialize the readers */
VReaderStatus
vreader_add_reader(VReader *reader)
{
VReaderListEntry *reader_entry;
reader_entry = vreader_list_entry_new(reader);
if (reader_entry == NULL) {
return VREADER_OUT_OF_MEMORY;
}
vreader_list_lock();
vreader_queue(vreader_list, reader_entry);
vreader_list_unlock();
vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
return VREADER_OK;
}
VReaderStatus
vreader_remove_reader(VReader *reader)
{
VReaderListEntry *current_entry;
vreader_list_lock();
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
current_entry = vreader_list_get_next(current_entry)) {
if (current_entry->reader == reader) {
break;
}
}
vreader_dequeue(vreader_list, current_entry);
vreader_list_unlock();
vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
vreader_list_entry_delete(current_entry);
return VREADER_OK;
}
/*
* Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
* state. Separated from vreader_insert_card to allow replaying events
* for a given state.
*/
void
vreader_queue_card_event(VReader *reader)
{
vevent_queue_vevent(vevent_new(
reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
reader->card));
}
/*
* insert/remove a new card. for removal, card == NULL
*/
VReaderStatus
vreader_insert_card(VReader *reader, VCard *card)
{
vreader_lock(reader);
if (reader->card) {
/* decrement reference count */
vcard_free(reader->card);
reader->card = NULL;
}
reader->card = vcard_reference(card);
vreader_unlock(reader);
vreader_queue_card_event(reader);
return VREADER_OK;
}
/*
* initialize all the static reader structures
*/
void
vreader_init(void)
{
vreader_list_init();
}

55
src/vreader.h Normal file
View File

@ -0,0 +1,55 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VREADER_H
#define VREADER_H 1
#include "eventt.h"
#include "vreadert.h"
#include "vcardt.h"
/*
* calls for reader front end
*/
VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len);
VReaderStatus vreader_power_off(VReader *reader);
VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf,
int send_buf_len, unsigned char *receive_buf,
int *receive_buf_len);
/* constructor */
VReader *vreader_new(const char *readerName, VReaderEmul *emul_private,
VReaderEmulFree private_free);
/* get a new reference to a reader */
VReader *vreader_reference(VReader *reader);
/* "destructor" (readers are reference counted) */
void vreader_free(VReader *reader);
/* accessors */
VReaderEmul *vreader_get_private(VReader *);
VReaderStatus vreader_card_is_present(VReader *reader);
void vreader_queue_card_event(VReader *reader);
const char *vreader_get_name(VReader *reader);
vreader_id_t vreader_get_id(VReader *reader);
VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id);
/* list operations */
VReaderList *vreader_get_reader_list(void);
void vreader_list_delete(VReaderList *list);
VReader *vreader_list_get_reader(VReaderListEntry *entry);
VReaderListEntry *vreader_list_get_first(VReaderList *list);
VReaderListEntry *vreader_list_get_next(VReaderListEntry *list);
VReader *vreader_get_reader_by_id(vreader_id_t id);
VReader *vreader_get_reader_by_name(const char *name);
/*
* list tools for vcard_emul
*/
void vreader_init(void);
VReaderStatus vreader_add_reader(VReader *reader);
VReaderStatus vreader_remove_reader(VReader *reader);
VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
#endif

24
src/vreadert.h Normal file
View File

@ -0,0 +1,24 @@
/*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VREADERT_H
#define VREADERT_H 1
typedef enum {
VREADER_OK = 0,
VREADER_NO_CARD,
VREADER_OUT_OF_MEMORY
} VReaderStatus;
typedef unsigned int vreader_id_t;
typedef struct VReaderStruct VReader;
typedef struct VReaderListStruct VReaderList;
typedef struct VReaderListEntryStruct VReaderListEntry;
typedef struct VReaderEmulStruct VReaderEmul;
typedef void (*VReaderEmulFree)(VReaderEmul *);
#endif

186
src/vscard_common.h Normal file
View File

@ -0,0 +1,186 @@
/* Virtual Smart Card protocol definition
*
* This protocol is between a host using virtual smart card readers,
* and a client providing the smart cards, perhaps by emulating them or by
* access to real cards.
*
* Definitions for this protocol:
* Host - user of the card
* Client - owner of the card
*
* The current implementation passes the raw APDU's from 7816 and additionally
* contains messages to setup and teardown readers, handle insertion and
* removal of cards, negotiate the protocol via capabilities and provide
* for error responses.
*
* Copyright (c) 2011 Red Hat.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef VSCARD_COMMON_H
#define VSCARD_COMMON_H
#include <stdint.h>
#include <glib.h>
#define VERSION_MAJOR_BITS 11
#define VERSION_MIDDLE_BITS 11
#define VERSION_MINOR_BITS 10
#define MAKE_VERSION(major, middle, minor) \
((major << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
| (middle << VERSION_MINOR_BITS) \
| (minor))
/*
* IMPORTANT NOTE on VERSION
*
* The version below MUST be changed whenever a change in this file is made.
*
* The last digit, the minor, is for bug fix changes only.
*
* The middle digit is for backward / forward compatible changes, updates
* to the existing messages, addition of fields.
*
* The major digit is for a breaking change of protocol, presumably
* something that cannot be accommodated with the existing protocol.
*/
#define VSCARD_VERSION MAKE_VERSION(0, 0, 2)
typedef enum VSCMsgType {
VSC_Init = 1,
VSC_Error,
VSC_ReaderAdd,
VSC_ReaderRemove,
VSC_ATR,
VSC_CardRemove,
VSC_APDU,
VSC_Flush,
VSC_FlushComplete
} VSCMsgType;
typedef enum VSCErrorCode {
VSC_SUCCESS = 0,
VSC_GENERAL_ERROR = 1,
VSC_CANNOT_ADD_MORE_READERS,
VSC_CARD_ALREAY_INSERTED,
} VSCErrorCode;
#define VSCARD_UNDEFINED_READER_ID 0xffffffff
#define VSCARD_MINIMAL_READER_ID 0
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define VSCARD_MAGIC_CONST(f) \
((uint32_t)(((f)[0])|((f)[1]<<8)|((f)[2]<<16)|((f)[3]<<24)))
#else
#define VSCARD_MAGIC_CONST(f) \
((uint32_t)(((f)[3])|((f)[2]<<8)|((f)[1]<<16)|((f)[0]<<24)))
#endif
#define VSCARD_MAGIC VSCARD_MAGIC_CONST("VSCD")
/*
* Header
* Each message starts with the header.
* type - message type
* reader_id - used by messages that are reader specific
* length - length of payload (not including header, i.e. zero for
* messages containing empty payloads)
*/
typedef struct VSCMsgHeader {
uint32_t type;
uint32_t reader_id;
uint32_t length;
uint8_t data[0];
} VSCMsgHeader;
/*
* VSCMsgInit Client <-> Host
* Client sends it on connection, with its own capabilities.
* Host replies with VSCMsgInit filling in its capabilities.
*
* It is not meant to be used for negotiation, i.e. sending more then
* once from any side, but could be used for that in the future.
*/
typedef struct VSCMsgInit {
uint32_t magic;
uint32_t version;
uint32_t capabilities[1]; /* receiver must check length,
array may grow in the future*/
} VSCMsgInit;
/*
* VSCMsgError Client <-> Host
* This message is a response to any of:
* Reader Add
* Reader Remove
* Card Remove
* If the operation was successful then VSC_SUCCESS
* is returned, other wise a specific error code.
*/
typedef struct VSCMsgError {
uint32_t code;
} VSCMsgError;
/*
* VSCMsgReaderAdd Client -> Host
* Host replies with allocated reader id in VSCMsgError with code==SUCCESS.
*
* name - name of the reader on client side, UTF-8 encoded. Only used
* for client presentation (may be translated to the device presented to the
* guest), protocol wise only reader_id is important.
*/
typedef struct VSCMsgReaderAdd {
uint8_t name[0];
} VSCMsgReaderAdd;
/*
* VSCMsgReaderRemove Client -> Host
* The client's reader has been removed.
*/
typedef struct VSCMsgReaderRemove {
} VSCMsgReaderRemove;
/*
* VSCMsgATR Client -> Host
* Answer to reset. Sent for card insertion or card reset. The reset/insertion
* happens on the client side, they do not require any action from the host.
*/
typedef struct VSCMsgATR {
uint8_t atr[0];
} VSCMsgATR;
/*
* VSCMsgCardRemove Client -> Host
* The client card has been removed.
*/
typedef struct VSCMsgCardRemove {
} VSCMsgCardRemove;
/*
* VSCMsgAPDU Client <-> Host
* Main reason of existence. Transfer a single APDU in either direction.
*/
typedef struct VSCMsgAPDU {
uint8_t data[0];
} VSCMsgAPDU;
/*
* VSCMsgFlush Host -> Client
* Request client to send a FlushComplete message when it is done
* servicing all outstanding APDUs
*/
typedef struct VSCMsgFlush {
} VSCMsgFlush;
/*
* VSCMsgFlush Client -> Host
* Client response to Flush after all APDUs have been processed and
* responses sent.
*/
typedef struct VSCMsgFlushComplete {
} VSCMsgFlushComplete;
#endif /* VSCARD_COMMON_H */

934
src/vscclient.c Normal file
View File

@ -0,0 +1,934 @@
/*
* Tester for VSCARD protocol, client side.
*
* Can be used with ccid-card-passthru.
*
* Copyright (c) 2011 Red Hat.
* Written by Alon Levy.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#define closesocket(x) close(x)
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#include <getopt.h>
#endif
#if defined(ENABLE_PCSC)
# ifdef __APPLE__
# include <PCSC/winscard.h>
# include <PCSC/wintypes.h>
# else
# include <winscard.h>
# endif
#endif
#include "glib-compat.h"
#include "vscard_common.h"
#include "vreader.h"
#include "vcard_emul.h"
#include "vevent.h"
static int verbose;
static int with_pcsc;
static void
print_byte_array(
uint8_t *arrBytes,
int nSize
) {
int i;
for (i = 0; i < nSize; i++) {
printf("%02X ", arrBytes[i]);
}
printf("\n");
}
static void
print_usage(void) {
printf("vscclient OPTIONS <host> <port>\n");
printf(" -e <emul_args> - Emulator arguments, see below\n");
printf(" -c <certname> - Software emulation certificates\n");
printf(" -d <level> - Debug level\n");
printf(" -p - Use real smartcard to compare with emulator\n");
vcard_emul_usage();
}
#if defined(ENABLE_PCSC)
static SCARD_IO_REQUEST scard_pci;
static SCARDHANDLE scard;
static SCARDCONTEXT scard_ctxt;
static gboolean pcsc_transmit(BYTE *cmd, LONG cmdlen, BYTE *recv, int *recvlen)
{
LONG rv;
rv = SCardTransmit(scard, &scard_pci, cmd, cmdlen,
NULL, recv, (DWORD*)recvlen);
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
return 0;
}
static gboolean pcsc_init(void)
{
LONG rv;
DWORD nreaders, protocol;
LPTSTR readers;
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard_ctxt);
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
#ifdef SCARD_AUTOALLOCATE
nreaders = SCARD_AUTOALLOCATE;
rv = SCardListReaders(scard_ctxt, NULL, (LPTSTR)&readers, &nreaders);
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
#else
rv = SCardListReaders(scard_ctxt, NULL, NULL, &nreaders);
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
readers = g_new0(char, nreaders);
rv = SCardListReaders(scard_ctxt, NULL, readers, &nreaders);
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
#endif
printf("reader name: %s\n", readers);
rv = SCardConnect(scard_ctxt, readers, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scard, &protocol);
g_return_val_if_fail(rv == SCARD_S_SUCCESS, FALSE);
switch(protocol) {
case SCARD_PROTOCOL_T0:
scard_pci = *SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
scard_pci = *SCARD_PCI_T1;
break;
default:
g_return_val_if_reached(FALSE);
}
#ifdef SCARD_AUTOALLOCATE
rv = SCardFreeMemory(scard_ctxt, readers);
g_warn_if_fail(rv == SCARD_S_SUCCESS);
#else
g_free(readers);
#endif
return TRUE;
}
static void pcsc_deinit(void)
{
LONG rv;
rv = SCardDisconnect(scard, SCARD_LEAVE_CARD);
g_warn_if_fail(rv == SCARD_S_SUCCESS);
scard = 0;
rv = SCardReleaseContext(scard_ctxt);
g_warn_if_fail(rv == SCARD_S_SUCCESS);
scard_ctxt = 0;
}
#endif
static GIOChannel *channel_socket;
static GByteArray *socket_to_send;
static CompatGMutex socket_to_send_lock;
static guint socket_tag;
static void
update_socket_watch(void);
static gboolean
do_socket_send(GIOChannel *source,
GIOCondition condition,
gpointer data)
{
gsize bw;
GError *err = NULL;
g_return_val_if_fail(socket_to_send->len != 0, FALSE);
g_return_val_if_fail(condition & G_IO_OUT, FALSE);
g_io_channel_write_chars(channel_socket,
(gchar *)socket_to_send->data, socket_to_send->len, &bw, &err);
if (err != NULL) {
g_error("Error while sending socket %s", err->message);
return FALSE;
}
g_byte_array_remove_range(socket_to_send, 0, bw);
if (socket_to_send->len == 0) {
update_socket_watch();
return FALSE;
}
return TRUE;
}
static gboolean
socket_prepare_sending(gpointer user_data)
{
update_socket_watch();
return FALSE;
}
static int
send_msg(
VSCMsgType type,
uint32_t reader_id,
const void *msg,
unsigned int length
) {
VSCMsgHeader mhHeader;
g_mutex_lock(&socket_to_send_lock);
if (verbose > 10) {
printf("sending type=%d id=%u, len =%u (0x%x)\n",
type, reader_id, length, length);
}
mhHeader.type = htonl(type);
mhHeader.reader_id = 0;
mhHeader.length = htonl(length);
g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader));
g_byte_array_append(socket_to_send, (guint8 *)msg, length);
g_idle_add(socket_prepare_sending, NULL);
g_mutex_unlock(&socket_to_send_lock);
return 0;
}
static VReader *pending_reader;
static CompatGMutex pending_reader_lock;
static CompatGCond pending_reader_condition;
#define MAX_ATR_LEN 40
static gpointer
event_thread(gpointer arg)
{
unsigned char atr[MAX_ATR_LEN];
int atr_len;
VEvent *event;
unsigned int reader_id;
while (1) {
const char *reader_name;
event = vevent_wait_next_vevent();
if (event == NULL) {
break;
}
reader_id = vreader_get_id(event->reader);
if (reader_id == VSCARD_UNDEFINED_READER_ID &&
event->type != VEVENT_READER_INSERT) {
/* ignore events from readers qemu has rejected */
/* if qemu is still deciding on this reader, wait to see if need to
* forward this event */
g_mutex_lock(&pending_reader_lock);
if (!pending_reader || (pending_reader != event->reader)) {
/* wasn't for a pending reader, this reader has already been
* rejected by qemu */
g_mutex_unlock(&pending_reader_lock);
vevent_delete(event);
continue;
}
/* this reader hasn't been told its status from qemu yet, wait for
* that status */
while (pending_reader != NULL) {
g_cond_wait(&pending_reader_condition, &pending_reader_lock);
}
g_mutex_unlock(&pending_reader_lock);
/* now recheck the id */
reader_id = vreader_get_id(event->reader);
if (reader_id == VSCARD_UNDEFINED_READER_ID) {
/* this reader was rejected */
vevent_delete(event);
continue;
}
/* reader was accepted, now forward the event */
}
switch (event->type) {
case VEVENT_READER_INSERT:
/* tell qemu to insert a new CCID reader */
/* wait until qemu has responded to our first reader insert
* before we send a second. That way we won't confuse the responses
* */
g_mutex_lock(&pending_reader_lock);
while (pending_reader != NULL) {
g_cond_wait(&pending_reader_condition, &pending_reader_lock);
}
pending_reader = vreader_reference(event->reader);
g_mutex_unlock(&pending_reader_lock);
reader_name = vreader_get_name(event->reader);
if (verbose > 10) {
printf(" READER INSERT: %s\n", reader_name);
}
send_msg(VSC_ReaderAdd,
reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */
NULL, 0 /* TODO reader_name, strlen(reader_name) */);
break;
case VEVENT_READER_REMOVE:
/* future, tell qemu that an old CCID reader has been removed */
if (verbose > 10) {
printf(" READER REMOVE: %u\n", reader_id);
}
send_msg(VSC_ReaderRemove, reader_id, NULL, 0);
break;
case VEVENT_CARD_INSERT:
/* get the ATR (intended as a response to a power on from the
* reader */
atr_len = MAX_ATR_LEN;
vreader_power_on(event->reader, atr, &atr_len);
/* ATR call functions as a Card Insert event */
if (verbose > 10) {
printf(" CARD INSERT %u: ", reader_id);
print_byte_array(atr, atr_len);
}
send_msg(VSC_ATR, reader_id, atr, atr_len);
break;
case VEVENT_CARD_REMOVE:
/* Card removed */
if (verbose > 10) {
printf(" CARD REMOVE %u:\n", reader_id);
}
send_msg(VSC_CardRemove, reader_id, NULL, 0);
break;
case VEVENT_LAST:
default:
break;
}
vevent_delete(event);
}
return NULL;
}
static unsigned int
get_id_from_string(char *string, unsigned int default_id)
{
unsigned int id = atoi(string);
/* don't accidentally swith to zero because no numbers have been supplied */
if ((id == 0) && *string != '0') {
return default_id;
}
return id;
}
static int
on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
{
uint32_t *capabilities = (incoming->capabilities);
int num_capabilities =
1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
int i;
incoming->version = ntohl(incoming->version);
if (incoming->version != VSCARD_VERSION) {
if (verbose > 0) {
printf("warning: host has version %d, we have %d\n",
verbose, VSCARD_VERSION);
}
}
if (incoming->magic != VSCARD_MAGIC) {
printf("unexpected magic: got %d, expected %d\n",
incoming->magic, VSCARD_MAGIC);
return -1;
}
for (i = 0 ; i < num_capabilities; ++i) {
capabilities[i] = ntohl(capabilities[i]);
}
/* Future: check capabilities */
/* remove whatever reader might be left in qemu,
* in case of an unclean previous exit. */
send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
/* launch the event_thread. This will trigger reader adds for all the
* existing readers */
g_thread_new("vsc/event", event_thread, NULL);
return 0;
}
enum {
STATE_HEADER,
STATE_MESSAGE,
};
#define APDUBufSize 270
static gboolean
do_socket_read(GIOChannel *source,
GIOCondition condition,
gpointer data)
{
int rv;
int dwSendLength;
int dwRecvLength;
uint8_t pbRecvBuffer[APDUBufSize];
static uint8_t pbSendBuffer[APDUBufSize];
VReaderStatus reader_status;
VReader *reader = NULL;
static VSCMsgHeader mhHeader;
VSCMsgError error_msg;
GError *err = NULL;
VSCMsgInit init;
static gchar *buf;
static gsize br, to_read;
static int state = STATE_HEADER;
if (state == STATE_HEADER && to_read == 0) {
buf = (gchar *)&mhHeader;
to_read = sizeof(mhHeader);
}
if (to_read > 0) {
g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
if (err != NULL) {
g_error("error while reading: %s", err->message);
}
buf += br;
to_read -= br;
if (to_read != 0) {
return TRUE;
}
}
if (state == STATE_HEADER) {
mhHeader.type = ntohl(mhHeader.type);
mhHeader.reader_id = ntohl(mhHeader.reader_id);
mhHeader.length = ntohl(mhHeader.length);
if (verbose) {
printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
mhHeader.type, mhHeader.reader_id, mhHeader.length,
mhHeader.length);
}
switch (mhHeader.type) {
case VSC_APDU:
case VSC_Flush:
case VSC_Error:
case VSC_Init:
buf = (gchar *)pbSendBuffer;
to_read = mhHeader.length;
state = STATE_MESSAGE;
return TRUE;
default:
fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
return FALSE;
}
}
if (state == STATE_MESSAGE) {
char *reply = NULL;
#if defined(ENABLE_PCSC)
int reply_size;
#endif
switch (mhHeader.type) {
case VSC_APDU:
if (verbose) {
static int n = 0;
printf("\n\n >>> %d recv APDU: \n", n++);
print_byte_array(pbSendBuffer, mhHeader.length);
}
/* Transmit received APDU */
dwSendLength = mhHeader.length;
dwRecvLength = sizeof(pbRecvBuffer);
reader = vreader_get_reader_by_id(mhHeader.reader_id);
reader_status = vreader_xfr_bytes(reader,
pbSendBuffer, dwSendLength,
pbRecvBuffer, &dwRecvLength);
if (verbose) {
printf("libcacard response: ");
print_byte_array(pbRecvBuffer, dwRecvLength);
}
#if defined(ENABLE_PCSC)
if (with_pcsc) {
reply_size = dwRecvLength;
reply = g_memdup(pbRecvBuffer, reply_size);
dwSendLength = mhHeader.length;
dwRecvLength = sizeof(pbRecvBuffer);
if (!pcsc_transmit(pbSendBuffer, dwSendLength,
pbRecvBuffer, &dwRecvLength))
reader_status = VREADER_OK;
else
reader_status = VREADER_NO_CARD;
}
#endif
if (reader_status == VREADER_OK) {
mhHeader.length = dwRecvLength;
#if defined(ENABLE_PCSC)
if (with_pcsc && verbose) {
int diff = (unsigned int) reply_size != mhHeader.length ||
memcmp(pbRecvBuffer, reply, reply_size);
printf("HW response:%s ", diff ? "\x1B[31m!!!\x1B[0m" : "");
print_byte_array(pbRecvBuffer, mhHeader.length);
}
#endif
send_msg(VSC_APDU, mhHeader.reader_id,
pbRecvBuffer, dwRecvLength);
} else {
rv = reader_status; /* warning: not meaningful */
send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
}
g_free(reply);
vreader_free(reader);
reader = NULL; /* we've freed it, don't use it by accident
again */
break;
case VSC_Flush:
/* TODO: actually flush */
send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
break;
case VSC_Error:
memcpy(&error_msg, pbSendBuffer, sizeof(VSCMsgError));
if (error_msg.code == VSC_SUCCESS) {
g_mutex_lock(&pending_reader_lock);
if (pending_reader) {
vreader_set_id(pending_reader, mhHeader.reader_id);
vreader_free(pending_reader);
pending_reader = NULL;
g_cond_signal(&pending_reader_condition);
}
g_mutex_unlock(&pending_reader_lock);
break;
}
printf("warning: qemu refused to add reader\n");
if (error_msg.code == VSC_CANNOT_ADD_MORE_READERS) {
/* clear pending reader, qemu can't handle any more */
g_mutex_lock(&pending_reader_lock);
if (pending_reader) {
pending_reader = NULL;
/* make sure the event loop doesn't hang */
g_cond_signal(&pending_reader_condition);
}
g_mutex_unlock(&pending_reader_lock);
}
break;
case VSC_Init:
memcpy(&init, pbSendBuffer, sizeof(VSCMsgInit));
if (on_host_init(&mhHeader, &init) < 0) {
return FALSE;
}
break;
default:
g_assert_not_reached();
return FALSE;
}
state = STATE_HEADER;
}
return TRUE;
}
static gboolean
do_socket(GIOChannel *source,
GIOCondition condition,
gpointer data)
{
/* not sure if two watches work well with a single win32 sources */
if (condition & G_IO_OUT) {
if (!do_socket_send(source, condition, data)) {
return FALSE;
}
}
if (condition & G_IO_IN) {
if (!do_socket_read(source, condition, data)) {
return FALSE;
}
}
return TRUE;
}
static void
update_socket_watch(void)
{
gboolean out = socket_to_send->len > 0;
if (socket_tag != 0) {
g_source_remove(socket_tag);
}
socket_tag = g_io_add_watch(channel_socket,
G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL);
}
static gboolean
do_command(GIOChannel *source,
GIOCondition condition,
gpointer data)
{
char *string;
VCardEmulError error;
static unsigned int default_reader_id;
unsigned int reader_id;
VReader *reader = NULL;
GError *err = NULL;
g_assert(condition & G_IO_IN);
reader_id = default_reader_id;
g_io_channel_read_line(source, &string, NULL, NULL, &err);
if (err != NULL) {
g_error("Error while reading command: %s", err->message);
}
if (string != NULL) {
if (strncmp(string, "exit", 4) == 0) {
/* remove all the readers */
VReaderList *list = vreader_get_reader_list();
VReaderListEntry *reader_entry;
printf("Active Readers:\n");
for (reader_entry = vreader_list_get_first(list); reader_entry;
reader_entry = vreader_list_get_next(reader_entry)) {
VReader *r = vreader_list_get_reader(reader_entry);
vreader_id_t id;
id = vreader_get_id(r);
if (id == (vreader_id_t)-1) {
continue;
}
/* be nice and signal card removal first (qemu probably should
* do this itself) */
if (vreader_card_is_present(r) == VREADER_OK) {
send_msg(VSC_CardRemove, id, NULL, 0);
}
send_msg(VSC_ReaderRemove, id, NULL, 0);
}
exit(0);
} else if (strncmp(string, "insert", 6) == 0) {
if (string[6] == ' ') {
reader_id = get_id_from_string(&string[7], reader_id);
}
reader = vreader_get_reader_by_id(reader_id);
if (reader != NULL) {
error = vcard_emul_force_card_insert(reader);
printf("insert %s, returned %d\n",
vreader_get_name(reader), error);
} else {
printf("no reader by id %u found\n", reader_id);
}
} else if (strncmp(string, "remove", 6) == 0) {
if (string[6] == ' ') {
reader_id = get_id_from_string(&string[7], reader_id);
}
reader = vreader_get_reader_by_id(reader_id);
if (reader != NULL) {
error = vcard_emul_force_card_remove(reader);
printf("remove %s, returned %d\n",
vreader_get_name(reader), error);
} else {
printf("no reader by id %u found\n", reader_id);
}
} else if (strncmp(string, "select", 6) == 0) {
if (string[6] == ' ') {
reader_id = get_id_from_string(&string[7],
VSCARD_UNDEFINED_READER_ID);
}
if (reader_id != VSCARD_UNDEFINED_READER_ID) {
reader = vreader_get_reader_by_id(reader_id);
}
if (reader) {
printf("Selecting reader %u, %s\n", reader_id,
vreader_get_name(reader));
default_reader_id = reader_id;
} else {
printf("Reader with id %u not found\n", reader_id);
}
} else if (strncmp(string, "debug", 5) == 0) {
if (string[5] == ' ') {
verbose = get_id_from_string(&string[6], 0);
}
printf("debug level = %d\n", verbose);
} else if (strncmp(string, "list", 4) == 0) {
VReaderList *list = vreader_get_reader_list();
VReaderListEntry *reader_entry;
printf("Active Readers:\n");
for (reader_entry = vreader_list_get_first(list); reader_entry;
reader_entry = vreader_list_get_next(reader_entry)) {
VReader *r = vreader_list_get_reader(reader_entry);
vreader_id_t id;
id = vreader_get_id(r);
if (id == (vreader_id_t)-1) {
continue;
}
printf("%3u %s %s\n", id,
vreader_card_is_present(r) == VREADER_OK ?
"CARD_PRESENT" : " ",
vreader_get_name(r));
}
printf("Inactive Readers:\n");
for (reader_entry = vreader_list_get_first(list); reader_entry;
reader_entry = vreader_list_get_next(reader_entry)) {
VReader *r = vreader_list_get_reader(reader_entry);
vreader_id_t id;
id = vreader_get_id(reader);
if (id != (vreader_id_t)-1) {
continue;
}
printf("INA %s %s\n",
vreader_card_is_present(r) == VREADER_OK ?
"CARD_PRESENT" : " ",
vreader_get_name(r));
}
vreader_list_delete(list);
} else if (*string != 0) {
printf("valid commands:\n");
printf("insert [reader_id]\n");
printf("remove [reader_id]\n");
printf("select reader_id\n");
printf("list\n");
printf("debug [level]\n");
printf("exit\n");
}
}
vreader_free(reader);
printf("> ");
fflush(stdout);
return TRUE;
}
/* just for ease of parsing command line arguments. */
#define MAX_CERTS 100
static int
connect_to_qemu(
const char *host,
const char *port
) {
struct addrinfo hints;
struct addrinfo *server = NULL;
int ret, sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
/* Error */
fprintf(stderr, "Error opening socket!\n");
return -1;
}
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
ret = getaddrinfo(host, port, &hints, &server);
if (ret != 0) {
/* Error */
fprintf(stderr, "getaddrinfo failed\n");
goto cleanup_socket;
}
if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
/* Error */
fprintf(stderr, "Could not connect\n");
goto cleanup_socket;
}
if (verbose) {
printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
}
freeaddrinfo(server);
return sock;
cleanup_socket:
if (server) {
freeaddrinfo(server);
}
closesocket(sock);
return -1;
}
int
main(
int argc,
char *argv[]
) {
GMainLoop *loop;
GIOChannel *channel_stdin;
char *qemu_host;
char *qemu_port;
VSCMsgInit init;
VCardEmulOptions *command_line_options = NULL;
char *cert_names[MAX_CERTS];
char *emul_args = NULL;
int cert_count = 0;
int c, sock;
#ifdef _WIN32
WSADATA Data;
if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) {
c = WSAGetLastError();
fprintf(stderr, "WSAStartup: %d\n", c);
return 1;
}
#endif
#if !GLIB_CHECK_VERSION(2, 31, 0)
if (!g_thread_supported()) {
g_thread_init(NULL);
}
#endif
while ((c = getopt(argc, argv, "c:e:d:p")) != -1) {
if (c == '?') {
break;
}
switch (c) {
case 'c':
assert(optarg != NULL);
if (cert_count >= MAX_CERTS) {
printf("too many certificates (max = %d)\n", MAX_CERTS);
exit(5);
}
cert_names[cert_count++] = optarg;
break;
case 'e':
assert(optarg != NULL);
emul_args = optarg;
break;
case 'd':
assert(optarg != NULL);
verbose = get_id_from_string(optarg, 1);
break;
case 'p':
with_pcsc = 1;
break;
default:
g_warn_if_reached();
}
}
if (argc - optind != 2) {
print_usage();
exit(4);
}
if (cert_count > 0) {
char *new_args;
int len, i;
/* if we've given some -c options, we clearly we want do so some
* software emulation. add that emulation now. this is NSS Emulator
* specific */
if (emul_args == NULL) {
emul_args = (char *)"db=\"/etc/pki/nssdb\"";
}
#define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
/* 2 == close paren & null */
len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
for (i = 0; i < cert_count; i++) {
len += strlen(cert_names[i])+1; /* 1 == comma */
}
new_args = g_malloc(len);
strcpy(new_args, emul_args);
strcat(new_args, SOFT_STRING);
for (i = 0; i < cert_count; i++) {
strcat(new_args, cert_names[i]);
strcat(new_args, ",");
}
strcat(new_args, ")");
emul_args = new_args;
}
if (emul_args) {
command_line_options = vcard_emul_options(emul_args);
}
qemu_host = g_strdup(argv[argc - 2]);
qemu_port = g_strdup(argv[argc - 1]);
sock = connect_to_qemu(qemu_host, qemu_port);
if (sock == -1) {
fprintf(stderr, "error opening socket, exiting.\n");
exit(5);
}
socket_to_send = g_byte_array_new();
vcard_emul_init(command_line_options);
loop = g_main_loop_new(NULL, TRUE);
printf("> ");
fflush(stdout);
#ifdef _WIN32
channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO);
#else
channel_stdin = g_io_channel_unix_new(STDIN_FILENO);
#endif
g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL);
#ifdef _WIN32
channel_socket = g_io_channel_win32_new_socket(sock);
#else
channel_socket = g_io_channel_unix_new(sock);
#endif
g_io_channel_set_encoding(channel_socket, NULL, NULL);
/* we buffer ourself for thread safety reasons */
g_io_channel_set_buffered(channel_socket, FALSE);
if (with_pcsc) {
#if defined(ENABLE_PCSC)
if (!pcsc_init())
return 1;
#else
printf("No PCSC support\n");
return 1;
#endif
}
/* Send init message, Host responds (and then we send reader attachments) */
init = (VSCMsgInit) {
.version = htonl(VSCARD_VERSION),
.magic = VSCARD_MAGIC,
.capabilities = {0}
};
send_msg(VSC_Init, 0, &init, sizeof(init));
g_main_loop_run(loop);
g_main_loop_unref(loop);
g_io_channel_unref(channel_stdin);
g_io_channel_unref(channel_socket);
g_byte_array_free(socket_to_send, TRUE);
closesocket(sock);
#if defined(ENABLE_PCSC)
pcsc_deinit();
#endif
return 0;
}

6
tests/cert.cfg Normal file
View File

@ -0,0 +1,6 @@
organization = "OpenSC"
expiration_days = 365
email = "none@example.org"
signing_key
encryption_key

821
tests/common.c Normal file
View File

@ -0,0 +1,821 @@
/*
* Shared test functions for libCACard
*
* Copyright 2018 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This code is licensed under the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include <glib.h>
#include <string.h>
#include "common.h"
#include "simpletlv.h"
int key_bits = 0;
int hw_tests = 0;
static void select_coid(VReader *reader, unsigned char *coid,
int expect_success)
{
VReaderStatus status;
int dwRecvLength = APDUBufSize;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t selfile[] = {
0x00, 0xa4, 0x02, 0x00, 0x02, 0x00, 0x00
};
size_t selfile_len = sizeof(selfile);
memcpy(&selfile[5], coid, 2);
g_debug("%s: Select OID 0x%02x 0x%02x", __func__, coid[0], coid[1]);
g_assert_nonnull(reader);
status = vreader_xfr_bytes(reader,
selfile, selfile_len,
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
if (expect_success) {
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
} else {
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_P1_P2_ERROR);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x82);
}
}
void select_coid_good(VReader *reader, unsigned char *coid)
{
select_coid(reader, coid, 1);
}
void select_coid_bad(VReader *reader, unsigned char *coid)
{
select_coid(reader, coid, 0);
}
int select_aid_response(VReader *reader, unsigned char *aid,
unsigned int aid_len, int response_len)
{
VReaderStatus status;
int dwRecvLength = APDUBufSize;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t selfile[] = {
0x00, 0xa4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
};
size_t selfile_len = sizeof(selfile);
g_assert_cmpint(aid_len, ==, 7);
memcpy(&selfile[5], aid, aid_len);
g_debug("%s: Select applet with AID 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x",
__func__, aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]);
g_assert_nonnull(reader);
status = vreader_xfr_bytes(reader,
selfile, selfile_len,
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
if (response_len > 0) {
/* we expect specific amount of response bytes */
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, response_len);
} else {
/* the default response length is 13 (FCI stub) */
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x0d);
}
return pbRecvBuffer[dwRecvLength-2];
}
void select_aid(VReader *reader, unsigned char *aid, unsigned int aid_len)
{
(void) select_aid_response(reader, aid, aid_len, 0);
}
void get_properties_coid(VReader *reader, const unsigned char coid[2],
int object_type)
{
int dwRecvLength = APDUBufSize;
VReaderStatus status;
uint8_t pbRecvBuffer[APDUBufSize], *p, *p_end, *p2, *p2_end;
uint8_t get_properties[] = {
/* Get properties [Le] */
0x80, 0x56, 0x01, 0x00, 0x00
};
uint8_t get_properties_tag[] = {
/* Get properties [tag list] [Le] */
0x80, 0x56, 0x02, 0x00, 0x02, 0x01, 0x01, 0x00
};
int verified_pki_properties = 0;
int num_objects = 0, num_objects_expected = -1;
int have_applet_information = 0;
status = vreader_xfr_bytes(reader,
get_properties, sizeof(get_properties),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
/* for too long Le, the cards return LE_ERROR with correct length to ask */
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_LE_ERROR);
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], >, 0);
/* Update the APDU to match Le field from response and resend */
get_properties[4] = pbRecvBuffer[1];
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
get_properties, sizeof(get_properties),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, >, 2);
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
/* try to parse the response, if it makes sense */
p = pbRecvBuffer;
p_end = p + dwRecvLength - 2;
while (p < p_end) {
uint8_t tag;
size_t vlen;
if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
g_debug("The generated SimpleTLV can not be parsed");
g_assert_not_reached();
}
g_debug("Tag: 0x%02x, Len: %" G_GSIZE_FORMAT, tag, vlen);
g_assert_cmpint(vlen, <=, p_end - p);
switch (tag) {
case 0x01: /* Applet Information */
g_assert_cmpint(vlen, ==, 5);
g_assert_cmphex(*p, ==, 0x10); /* Applet family */
have_applet_information = 1;
break;
case 0x40: /* Number of objects */
g_assert_cmpint(vlen, ==, 1);
if (num_objects_expected != -1) {
g_debug("Received multiple number-of-objects tags");
g_assert_not_reached();
}
num_objects_expected = *p;
break;
case 0x50: /* TV Object */
case 0x51: /* PKI Object */
/* recursive SimpleTLV structure */
p2 = p;
p2_end = p + vlen;
while (p2 < p2_end) {
uint8_t tag2;
size_t vlen2;
if (simpletlv_read_tag(&p2, p2_end - p2, &tag2, &vlen2) < 0) {
g_debug("The generated SimpleTLV can not be parsed");
g_assert_not_reached();
}
g_assert_cmpint(vlen2, <=, p2_end - p2);
g_debug(" Tag: 0x%02x, Len: %" G_GSIZE_FORMAT, tag2, vlen2);
switch (tag2) {
case 0x41: /* Object ID */
g_assert_cmpmem(p2, vlen2, coid, 2);
break;
case 0x42: /* Buffer properties */
g_assert_cmpint(vlen2, ==, 5);
if (object_type != TEST_EMPTY_BUFFER)
g_assert_cmpint(p2[0], ==, 0x00);
break;
case 0x43: /* PKI properties */
g_assert_cmphex(p2[0], ==, 0x06);
if (hw_tests) {
/* Assuming CAC card with 1024 b RSA keys */
key_bits = 1024;
} else {
/* Assuming 2048 b RSA keys */
key_bits = 2048;
}
g_assert_cmphex(p2[1], ==, (key_bits / 8 / 8));
g_assert_cmphex(p2[2], ==, 0x01);
g_assert_cmphex(p2[3], ==, 0x01);
verified_pki_properties = 1;
break;
case 0x26:
g_assert_cmpint(vlen2, ==, 1);
g_assert_cmphex(p2[0], ==, 0x01);
break;
default:
g_debug("Unknown tag in object: 0x%02x", tag2);
g_assert_not_reached();
}
p2 += vlen2;
}
/* one more object processed */
num_objects++;
break;
case 0x39:
g_assert_cmpint(vlen, ==, 1);
g_assert_cmphex(p[0], ==, 0x00);
break;
case 0x3A:
g_assert_cmpint(vlen, ==, 7);
break;
default:
g_debug("Unknown tag in properties buffer: 0x%02x", tag);
g_assert_not_reached();
}
p += vlen;
}
if (num_objects_expected != -1) {
g_assert_cmpint(num_objects, ==, num_objects_expected);
}
if (object_type == TEST_EMPTY_BUFFER) {
g_assert_cmpint(num_objects_expected, ==, 1);
}
if (object_type == TEST_EMPTY) {
g_assert_cmpint(num_objects_expected, ==, 0);
}
if (object_type == TEST_PKI) {
g_assert_cmpint(verified_pki_properties, ==, 1);
}
g_assert_cmpint(have_applet_information, ==, 1);
/* Try to list only some properties */
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
get_properties_tag, sizeof(get_properties_tag),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR);
g_assert_cmpint(pbRecvBuffer[1], ==, 0x0e); /* Two applet information buffers */
/* Update the APDU to match Le field from response and resend */
get_properties_tag[7] = pbRecvBuffer[1];
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
get_properties_tag, sizeof(get_properties_tag),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 16); /* Two applet information buffers + status */
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
/* Test the undocumented P1 = 0x40 */
dwRecvLength = APDUBufSize;
get_properties[2] = 0x40;
get_properties[4] = 0x00;
status = vreader_xfr_bytes(reader,
get_properties, sizeof(get_properties),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
/* for too long Le, the cards return LE_ERROR with correct length to ask */
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_LE_ERROR);
g_assert_cmpint(pbRecvBuffer[1], !=, 0x00);
/* Update the APDU to match Le field from response and resend */
get_properties[4] = pbRecvBuffer[1];
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
get_properties, sizeof(get_properties),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, >, 2);
g_assert_cmpint(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmpint(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
}
void get_properties(VReader *reader, int object_type)
{
unsigned char coid[2];
switch (object_type) {
case TEST_PKI:
// XXX only the first PKI for now
coid[0] = 0x01;
coid[1] = 0x00;
get_properties_coid(reader, coid, object_type);
break;
case TEST_CCC:
coid[0] = 0xDB;
coid[1] = 0x00;
get_properties_coid(reader, coid, object_type);
break;
case TEST_ACA:
coid[0] = 0x03;
coid[1] = 0x00;
get_properties_coid(reader, coid, object_type);
break;
default:
g_debug("Got unknown object type");
g_assert_not_reached();
}
}
void read_buffer(VReader *reader, uint8_t type, int object_type)
{
int dwRecvLength = APDUBufSize, dwLength, dwReadLength, offset;
VReaderStatus status;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t *data;
uint8_t read_buffer[] = {
/*Read Buffer OFFSET TYPE LENGTH a_Le */
0x80, 0x52, 0x00, 0x00, 0x02, 0x01, 0x02, 0x02
};
int card_urls = 0;
dwRecvLength = 4;
read_buffer[5] = type;
status = vreader_xfr_bytes(reader,
read_buffer, sizeof(read_buffer),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 4);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
dwLength = (pbRecvBuffer[0] & 0xff) | ((pbRecvBuffer[1] << 8) & 0xff);
if (dwLength == 0)
return;
data = g_malloc(dwLength);
offset = 0x02;
do {
dwReadLength = MIN(255, dwLength);
dwRecvLength = dwReadLength+2;
read_buffer[2] = (unsigned char) ((offset >> 8) & 0xff);
read_buffer[3] = (unsigned char) (offset & 0xff);
read_buffer[6] = (unsigned char) (dwReadLength);
read_buffer[7] = (unsigned char) (dwReadLength);
status = vreader_xfr_bytes(reader,
read_buffer, sizeof(read_buffer),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, dwReadLength + 2);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
memcpy(data + offset - 2, pbRecvBuffer, dwReadLength);
offset += dwLength;
dwLength -= dwReadLength;
} while (dwLength != 0);
/* Try to parse the TAG buffer, if it makes sense */
if (type == CAC_FILE_TAG) {
uint8_t *p = data;
uint8_t *p_end = p + offset - 2;
while (p < p_end) {
uint8_t tag;
size_t vlen;
if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
g_debug("The generated SimpleTLV can not be parsed");
g_assert_not_reached();
}
g_debug("Tag: 0x%02x, Len: %" G_GSIZE_FORMAT, tag, vlen);
switch (tag) {
case 0xF3: /* CardURL from CCC */
if (object_type == TEST_CCC) {
card_urls++;
} else {
g_debug("CardURLs found outside of CCC buffer");
g_assert_not_reached();
}
break;
default:
break;
}
}
if (object_type == TEST_CCC)
g_assert_cmpint(card_urls, ==, 11 + 3);
}
g_free(data);
}
void select_applet(VReader *reader, int type)
{
uint8_t selfile_ccc[] = {
/* Select CCC Applet */
0xa0, 0x00, 0x00, 0x01, 0x16, 0xDB, 0x00
};
uint8_t selfile_aca[] = {
/* Select ACA Applet */
0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00
};
uint8_t selfile_pki[] = {
/* Select first PKI Applet */
0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00
};
uint8_t selfile_passthrough[] = {
/* Select Person Instance (passthrough) */
0xa0, 0x00, 0x00, 0x00, 0x79, 0x02, 0x00
};
uint8_t selfile_empty[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF0
};
uint8_t *aid = NULL;
size_t aid_len = 0;
switch (type) {
case TEST_PKI:
aid = selfile_pki;
aid_len = sizeof(selfile_pki);
break;
case TEST_CCC:
aid = selfile_ccc;
aid_len = sizeof(selfile_ccc);
break;
case TEST_ACA:
aid = selfile_aca;
aid_len = sizeof(selfile_aca);
break;
case TEST_PASSTHROUGH:
aid = selfile_passthrough;
aid_len = sizeof(selfile_passthrough);
break;
case TEST_EMPTY:
aid = selfile_empty;
aid_len = sizeof(selfile_empty);
break;
default:
g_assert_not_reached();
}
g_assert_nonnull(aid);
select_aid(reader, aid, aid_len);
}
void do_sign(VReader *reader, int parts)
{
VReaderStatus status;
int dwRecvLength = APDUBufSize;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t sign[] = {
/* VERIFY [p1,p2=0 ] [Lc ] [2048b keys: 256 bytes of PKCS#1.5 padded data] */
0x80, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20,
0x28, 0x6d, 0x61, 0x78, 0x20, 0x31, 0x30, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a
};
int sign_len = sizeof(sign);
uint8_t getresp[] = {
/* Get Response (max we can get) */
0x00, 0xc0, 0x00, 0x00, 0x00
};
g_assert_nonnull(reader);
/* Adjust the buffers to match the key lengths, if already retrieved */
if (key_bits && key_bits < 2048) {
sign[4] = key_bits/8; /* less than 2048b will fit the length into one byte */
sign[5] = 0x00;
sign[6] = 0x01;
memcpy(&sign[7], &sign[sign_len-key_bits/8+2], key_bits/8-2);
sign_len = 5 + key_bits/8;
}
/* The driver supports signatures while data are passed in more separate APDUs */
if (parts) {
int split = 0x47;
/* we have not sent the whole buffer */
sign[2] = 0x80;
sign[4] = split;
sign[5] = 0x00;
sign[6] = 0x01;
sign[7] = 0xFF;
sign[8] = 0xFF;
sign_len = 5 + split;
status = vreader_xfr_bytes(reader,
sign, sign_len,
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
/* the next message will send the rest of the buffer */
sign[2] = 0x00;
if (key_bits)
sign[4] = key_bits/8 - split;
else
sign[4] = 256 - split;
memmove(&sign[5], &sign[5+2+split], sign[4]);
sign_len = 5 + sign[4];
}
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
sign, sign_len,
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_RESPONSE_BYTES);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, (unsigned char) (key_bits/8));
/* fetch the actual response */
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
getresp, sizeof(getresp),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, key_bits/8+2);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
}
void test_empty_applets(void)
{
uint8_t applet_02fb[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xFB
};
uint8_t applet_1201[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x12, 0x01
};
uint8_t applet_1202[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x12, 0x02
};
uint8_t applet_02f0[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF0
};
uint8_t applet_02f1[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF1
};
uint8_t applet_02f2[] = {
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0xF2
};
uint8_t coid[2] = {0x02, 0xFB};
VReader *reader = vreader_get_reader_by_id(0);
/* Skip the HW tests without physical card */
if (isHWTests() && vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* select the empty applet A00000007902FB, which should be empty buffer */
select_aid(reader, applet_02fb, sizeof(applet_02fb));
/* get properties */
get_properties_coid(reader, coid, TEST_EMPTY_BUFFER);
/* get the TAG buffer length */
read_buffer(reader, CAC_FILE_TAG, TEST_EMPTY_BUFFER);
/* get the VALUE buffer length */
read_buffer(reader, CAC_FILE_VALUE, TEST_EMPTY_BUFFER);
/* select the empty applet A0000000791201, which should be empty buffer */
select_aid(reader, applet_1201, sizeof(applet_1201));
coid[0] = 0x12;
coid[1] = 0x01;
/* get properties */
get_properties_coid(reader, coid, TEST_EMPTY_BUFFER);
/* get the TAG buffer length */
read_buffer(reader, CAC_FILE_TAG, TEST_EMPTY_BUFFER);
/* get the VALUE buffer length */
read_buffer(reader, CAC_FILE_VALUE, TEST_EMPTY_BUFFER);
/* select the empty applet A0000000791202, which should be empty buffer */
select_aid(reader, applet_1202, sizeof(applet_1202));
coid[0] = 0x12;
coid[1] = 0x02;
/* get properties */
get_properties_coid(reader, coid, TEST_EMPTY_BUFFER);
/* get the TAG buffer length */
read_buffer(reader, CAC_FILE_TAG, TEST_EMPTY_BUFFER);
/* get the VALUE buffer length */
read_buffer(reader, CAC_FILE_VALUE, TEST_EMPTY_BUFFER);
/* select the empty applet A00000007902F0, which should be empty */
select_aid(reader, applet_02f0, sizeof(applet_02f0));
/* get properties */
get_properties_coid(reader, NULL, TEST_EMPTY);
/* select the empty applet A00000007902F1, which should be empty */
select_aid(reader, applet_02f1, sizeof(applet_02f1));
/* get properties */
get_properties_coid(reader, NULL, TEST_EMPTY);
/* select the empty applet A00000007902F2, which should be empty */
select_aid(reader, applet_02f2, sizeof(applet_02f2));
/* get properties */
get_properties_coid(reader, NULL, TEST_EMPTY);
vreader_free(reader); /* get by id ref */
}
/*
* Check that access method without provided buffer returns valid
* SW and allow us to get the response with the following APDU
*
* opensc-tool -s 00A4040007A000000116DB00 -s 80520000020102 -s 00C0000002 \
* -s 00520002020134 -s 00C0000034
*/
void test_get_response(void)
{
VReader *reader = vreader_get_reader_by_id(0);
int dwRecvLength = APDUBufSize, dwLength;
VReaderStatus status;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t getresp[] = {
/* Get Response (max we can get) */
0x00, 0xc0, 0x00, 0x00, 0x00
};
uint8_t read_buffer[] = {
/*Read Buffer OFFSET TYPE LENGTH */
0x00, 0x52, 0x00, 0x00, 0x02, 0x01, 0x02 /* no L_e */
};
/* Skip the HW tests without physical card */
if (isHWTests() && vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* select CCC */
select_applet(reader, TEST_CCC);
/* read buffer without response buffer. Ignore the response. */
dwRecvLength = 2;
status = vreader_xfr_bytes(reader,
read_buffer, sizeof(read_buffer),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_RESPONSE_BYTES);
g_assert_cmpint(pbRecvBuffer[1], ==, 0x02);
/* read buffer without response buffer */
dwRecvLength = 2;
status = vreader_xfr_bytes(reader,
read_buffer, sizeof(read_buffer),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_RESPONSE_BYTES);
g_assert_cmpint(pbRecvBuffer[1], ==, 0x02);
/* fetch the actual response */
dwRecvLength = 4;
getresp[4] = 0x02;
status = vreader_xfr_bytes(reader,
getresp, sizeof(getresp),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 4);
g_assert_cmphex(pbRecvBuffer[2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[3], ==, 0x00);
/* the same with offset */
dwLength = (pbRecvBuffer[0] & 0xff) | ((pbRecvBuffer[1] << 8) & 0xff);
dwRecvLength = dwLength + 2;
read_buffer[3] = 0x02; // offset
read_buffer[6] = dwLength;
status = vreader_xfr_bytes(reader,
read_buffer, sizeof(read_buffer),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmpint(pbRecvBuffer[0], ==, VCARD7816_SW1_RESPONSE_BYTES);
g_assert_cmpint(pbRecvBuffer[1], ==, dwLength);
/* fetch the actual response */
dwRecvLength = dwLength + 2;
getresp[4] = dwLength;
status = vreader_xfr_bytes(reader,
getresp, sizeof(getresp),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, dwLength + 2);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
/* If we ask again, when there is no pending response */
dwRecvLength = dwLength + 2;
getresp[4] = dwLength;
status = vreader_xfr_bytes(reader,
getresp, sizeof(getresp),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmpint(dwRecvLength, ==, 2);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_P1_P2_ERROR);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x88);
vreader_free(reader); /* get by id ref */
}
void check_login_count(void)
{
VReader *reader = vreader_get_reader_by_id(0);
VReaderStatus status;
int dwRecvLength = APDUBufSize;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t login[] = {
/* VERIFY [p1,p2=0 ] [Lc] */
0x00, 0x20, 0x00, 0x00, 0x00
};
g_assert_nonnull(reader);
/* Skip the HW tests without physical card */
if (isHWTests() && vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* Get login count */
status = vreader_xfr_bytes(reader,
login, sizeof(login),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
/* NSS does not know how to do this yet */
g_assert_cmphex(pbRecvBuffer[0], ==, VCARD7816_SW1_P1_P2_ERROR);
g_assert_cmphex(pbRecvBuffer[1], ==, 0x88);
/* P1 = 0x01 is invalid */
login[2] = 0x01;
status = vreader_xfr_bytes(reader,
login, sizeof(login),
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmphex(pbRecvBuffer[0], ==, VCARD7816_SW1_P1_P2_ERROR);
g_assert_cmphex(pbRecvBuffer[1], ==, 0x00);
vreader_free(reader);
}
int
isHWTests(void)
{
return hw_tests;
}
void
setHWTests(int new_value)
{
hw_tests = new_value;
}
int
getBits(void)
{
return key_bits;
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */

54
tests/common.h Normal file
View File

@ -0,0 +1,54 @@
/*
* Shared test functions for libCACard
*
* Copyright 2018 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This code is licensed under the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef _TESTS_COMMON
#define _TESTS_COMMON
#include "libcacard.h"
#define APDUBufSize 270
enum {
TEST_PKI = 1,
TEST_CCC = 2,
TEST_ACA = 3,
TEST_GENERIC = 4,
TEST_EMPTY_BUFFER = 5,
TEST_EMPTY = 6,
TEST_PASSTHROUGH = 7,
};
void select_coid_good(VReader *reader, unsigned char *coid);
void select_coid_bad(VReader *reader, unsigned char *coid);
int select_aid_response(VReader *reader, unsigned char *aid,
unsigned int aid_len, int response);
void select_aid(VReader *reader, unsigned char *aid, unsigned int aid_len);
void select_applet(VReader *reader, int type);
void get_properties_coid(VReader *reader, const unsigned char coid[2], int object_type);
void get_properties(VReader *reader, int object_type);
void read_buffer(VReader *reader, uint8_t type, int object_type);
void do_sign(VReader *reader, int parts);
void test_empty_applets(void);
void test_get_response(void);
void check_login_count(void);
int isHWTests(void);
void setHWTests(int);
int getBits(void);
#endif /* _TESTS_COMMON */

BIN
tests/db/cert8.db Normal file

Binary file not shown.

BIN
tests/db/key3.db Normal file

Binary file not shown.

BIN
tests/db/secmod.db Normal file

Binary file not shown.

371
tests/hwtests.c Normal file
View File

@ -0,0 +1,371 @@
/*
* Test mirroring of CAC smart card
*
* Copyright 2018 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This code is licensed under the GNU LGPL, version 2.1 or later.
* See the COPYING file in the top-level directory.
*/
#include <glib.h>
#include <string.h>
#include "libcacard.h"
#include "simpletlv.h"
#include "common.h"
#define ARGS "db=\"sql:%s\" use_hw=removable"
#define LOGIN_PIN "77777777"
static GMainLoop *loop;
static GThread *thread;
static guint nreaders;
static GMutex mutex;
static GCond cond;
static gpointer
events_thread(gpointer arg)
{
unsigned int reader_id;
VEvent *event;
while (1) {
event = vevent_wait_next_vevent();
if (event == NULL || event->type == VEVENT_LAST) {
vevent_delete(event);
break;
}
reader_id = vreader_get_id(event->reader);
if (reader_id == VSCARD_UNDEFINED_READER_ID) {
g_mutex_lock(&mutex);
vreader_set_id(event->reader, nreaders++);
g_cond_signal(&cond);
g_mutex_unlock(&mutex);
reader_id = vreader_get_id(event->reader);
}
switch (event->type) {
case VEVENT_READER_INSERT:
case VEVENT_READER_REMOVE:
case VEVENT_CARD_INSERT:
case VEVENT_CARD_REMOVE:
break;
case VEVENT_LAST:
default:
g_warn_if_reached();
break;
}
vevent_delete(event);
}
return NULL;
}
static void libcacard_init(void)
{
VCardEmulOptions *command_line_options = NULL;
gchar *dbdir = g_test_build_filename(G_TEST_BUILT, "hwdb", NULL);
gchar *args = g_strdup_printf(ARGS, dbdir);
VCardEmulError ret;
thread = g_thread_new("test/events", events_thread, NULL);
command_line_options = vcard_emul_options(args);
ret = vcard_emul_init(command_line_options);
g_assert_cmpint(ret, ==, VCARD_EMUL_OK);
/* We test with real hardware */
setHWTests(1);
/* Do not assume any specific reader name here */
g_mutex_lock(&mutex);
while (nreaders < 2)
g_cond_wait(&cond, &mutex);
g_mutex_unlock(&mutex);
g_free(args);
g_free(dbdir);
}
static void test_list(void)
{
VReaderList *list = vreader_get_reader_list();
VReaderListEntry *reader_entry;
int cards = 0;
for (reader_entry = vreader_list_get_first(list); reader_entry;
reader_entry = vreader_list_get_next(reader_entry)) {
VReader *r = vreader_list_get_reader(reader_entry);
vreader_id_t id;
id = vreader_get_id(r);
g_debug("%s: VReader name = %s, card = %d, %u", __func__, vreader_get_name(r), vreader_card_is_present(r), id);
g_assert_cmpint(id, !=, VSCARD_UNDEFINED_READER_ID);
if (vreader_card_is_present(r) == VREADER_OK) {
cards++;
}
vreader_free(r);
}
vreader_list_delete(list);
if (cards == 0) {
g_test_skip("No physical card found");
return;
}
g_assert_cmpint(cards, ==, 1);
}
static void do_login(VReader *reader)
{
VReaderStatus status;
int dwRecvLength = APDUBufSize;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t login[] = {
/* VERIFY [p1,p2=0 ] [Lc] [pin 77777777 ] */
0x00, 0x20, 0x00, 0x00, 0x08,
//0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int login_len, pin_len;
g_assert_nonnull(reader);
/* Set the pin from constant */
pin_len = strlen(LOGIN_PIN);
login[4] = pin_len;
memcpy(&login[5], LOGIN_PIN, pin_len);
login_len = 5 + pin_len;
status = vreader_xfr_bytes(reader,
login, login_len,
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
g_assert_cmphex(pbRecvBuffer[0], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[1], ==, 0x00);
}
static void test_passthrough_applets(void)
{
uint8_t applet_person[] = {
/*Read Buffer OFFSET TYPE LENGTH */
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0x00
};
uint8_t applet_personnel[] = {
/*Read Buffer OFFSET TYPE LENGTH */
0xA0, 0x00, 0x00, 0x00, 0x79, 0x02, 0x01
};
uint8_t person_coid[2] = {0x02, 0x00};
uint8_t personnel_coid[2] = {0x02, 0x01};
VReader *reader = vreader_get_reader_by_id(0);
/* Skip the HW tests without physical card */
if (vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* select the Person Instance applet A0000000790200 */
select_aid(reader, applet_person, sizeof(applet_person));
/* get properties */
get_properties_coid(reader, person_coid, TEST_GENERIC);
/* These objects requires a PIN to read the value buffer */
do_login(reader);
/* get the TAG buffer length */
read_buffer(reader, CAC_FILE_TAG, TEST_GENERIC);
/* get the VALUE buffer length */
read_buffer(reader, CAC_FILE_VALUE, TEST_GENERIC);
/* select the Personnel applet A0000000790201 */
select_aid(reader, applet_personnel, sizeof(applet_personnel));
/* get properties */
get_properties_coid(reader, personnel_coid, TEST_GENERIC);
/* get the TAG buffer */
read_buffer(reader, CAC_FILE_TAG, TEST_GENERIC);
/* get the VALUE buffer */
read_buffer(reader, CAC_FILE_VALUE, TEST_GENERIC);
vreader_free(reader); /* get by id ref */
}
static void test_login(void)
{
VReader *reader = vreader_get_reader_by_id(0);
/* Skip the HW tests without physical card */
if (vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* select the ACA */
select_applet(reader, TEST_ACA);
do_login(reader);
vreader_free(reader); /* get by id ref */
}
static void test_sign(void)
{
VReader *reader = vreader_get_reader_by_id(0);
/* Skip the HW tests without physical card */
if (vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* select the ACA */
select_applet(reader, TEST_ACA);
do_login(reader);
/* select the PKI */
select_applet(reader, TEST_PKI);
/* get properties to figure out the key length */
get_properties(reader, TEST_PKI);
do_sign(reader, 0);
/* test also multipart signatures */
do_sign(reader, 1);
vreader_free(reader); /* get by id ref */
}
/* Try to pass bad formatted PKCS#1.5 data and make sure the libcacard does not
* crash while handling them
*/
static void test_sign_bad_data_x509(void)
{
VReader *reader = vreader_get_reader_by_id(0);
VReaderStatus status;
int dwRecvLength = APDUBufSize;
uint8_t pbRecvBuffer[APDUBufSize];
uint8_t sign[] = {
/* VERIFY [p1,p2=0 ] [Lc ] [2048b keys: 256 bytes of non PKCS#1.5 data] */
0x80, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
/* ^--- the second byte of data should be 0x01 for signatures */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20,
0x28, 0x6d, 0x61, 0x78, 0x20, 0x31, 0x30, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a,
0x00 /* <-- [Le] */
};
int sign_len = sizeof(sign);
int key_bits = getBits();
g_assert_nonnull(reader);
/* Skip the HW tests without physical card */
if (vreader_card_is_present(reader) != VREADER_OK) {
vreader_free(reader);
g_test_skip("No physical card found");
return;
}
/* run the actual test */
key_bits = getBits();
/* Adjust the buffers to match the key lengths, if already retrieved */
if (key_bits && key_bits < 2048) {
int payload_len = key_bits/8; /* RSA signature has the same length as the key */
sign[4] = payload_len; /* less than 2048b will fit the length into one byte */
sign[5] = 0x00; /* PKCS#1.5 padding first byte */
/*sign[6] = 0x01; <- this should be 0x01 for PKCS#1.5 signatures */
memmove(&sign[6], &sign[sign_len - payload_len], payload_len - 1);
sign_len = 5 /* [APDU header] */ + payload_len + 1 /* [Le] */;
sign[sign_len-1] = 0x00; /* [Le] */
}
dwRecvLength = APDUBufSize;
status = vreader_xfr_bytes(reader,
sign, sign_len,
pbRecvBuffer, &dwRecvLength);
g_assert_cmpint(status, ==, VREADER_OK);
/* We expect one of the following results:
* * VCARD7816_STATUS_ERROR_DATA_INVALID: Invalid data
* * VCARD7816_STATUS_SUCCESS: Properly signed data
*
* we should not crash as with 2.5.3
*/
if (pbRecvBuffer[dwRecvLength-2] == VCARD7816_SW1_SUCCESS) {
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_SUCCESS);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x00);
} else {
g_assert_cmphex(pbRecvBuffer[dwRecvLength-2], ==, VCARD7816_SW1_COMMAND_ERROR);
g_assert_cmphex(pbRecvBuffer[dwRecvLength-1], ==, 0x84);
}
/* no need to fetch the actual response */
vreader_free(reader); /* get by id ref */
}
static void libcacard_finalize(void)
{
VReader *reader = vreader_get_reader_by_id(0);
/* This probably supposed to be a event that terminates the loop */
vevent_queue_vevent(vevent_new(VEVENT_LAST, reader, NULL));
/* join */
g_thread_join(thread);
/* Clean up */
vreader_free(reader);
vreader_free(reader);
}
int main(int argc, char *argv[])
{
int ret;
g_test_init(&argc, &argv, NULL);
loop = g_main_loop_new(NULL, TRUE);
libcacard_init();
g_test_add_func("/hw-tests/list", test_list);
g_test_add_func("/hw-tests/passthrough-applet", test_passthrough_applets);
g_test_add_func("/hw-tests/check-login-count", check_login_count);
g_test_add_func("/hw-tests/login", test_login);
g_test_add_func("/hw-tests/sign", test_sign);
g_test_add_func("/hw-tests/sign-bad-data", test_sign_bad_data_x509);
g_test_add_func("/hw-tests/empty-applets", test_empty_applets);
g_test_add_func("/hw-tests/get-response", test_get_response);
ret = g_test_run();
libcacard_finalize();
return ret;
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */

1146
tests/libcacard.c Normal file

File diff suppressed because it is too large Load Diff

93
tests/setup-softhsm2.sh Executable file
View File

@ -0,0 +1,93 @@
#/bin/bash
SRCDIR=`dirname "$0"`
NSSDB=hwdb
CONF=softhsm2.conf
SOPIN="12345678"
PIN="77777777"
export GNUTLS_PIN=$PIN
P11LIB=/usr/lib64/pkcs11/libsofthsm2.so
generate_cert() {
TYPE="$1"
ID="$2"
LABEL="$3"
# Generate key pair
pkcs11-tool --keypairgen --key-type="$TYPE" --login --pin=$PIN \
--module="$P11LIB" --label="$LABEL" --id=$ID
if [[ "$?" -ne "0" ]]; then
echo "Couldn't generate $TYPE key pair"
return 1
fi
# check type value for the PKCS#11 URI (RHEL7 is using old "object-type")
TYPE_KEY="type"
p11tool --list-all --provider="$P11LIB" --login | grep "object-type" && \
TYPE_KEY="object-type"
# Generate certificate
certtool --generate-self-signed --outfile="$TYPE.cert" --template=$SRCDIR/cert.cfg \
--provider="$P11LIB" --load-privkey "pkcs11:object=$LABEL;$TYPE_KEY=private" \
--load-pubkey "pkcs11:object=$LABEL;$TYPE_KEY=public"
# convert to DER:
openssl x509 -inform PEM -outform DER -in "$TYPE.cert" -out "$TYPE.cert.der"
# Write certificate
pkcs11-tool --write-object "$TYPE.cert.der" --type=cert --id=$ID \
--label="$LABEL" --module="$P11LIB"
rm "$TYPE.cert" "$TYPE.cert.der"
p11tool --login --provider="$P11LIB" --list-all
}
# Check requirements
if [ ! -f $(which pkcs11-tool) ]; then
echo "ERROR: Need 'opensc' package to run tests"
exit 1
fi
if [ ! -f $(which p11tool) -o ! -f $(which certtool) ]; then
echo "ERROR: Need 'gnutls-utils' package to run tests"
exit 1
fi
if [ ! -f $(which modutil) ]; then
echo "ERROR: Need 'nss-tools' package to run tests"
exit 1
fi
if [ ! -f $(which openssl) ]; then
echo "ERROR: Need 'openssl' package to run tests"
exit 1
fi
if [ ! -f $(which softhsm2-util) ]; then
echo "ERROR: Need 'softhsm' package to run tests"
exit 1
fi
export SOFTHSM2_CONF="$CONF"
# SoftHSM configuration file
if [ ! -f "$CONF" ]; then
echo "directories.tokendir = `pwd`/tokens/" > $CONF
echo "slots.removable = true" >> $CONF
fi
# SoftHSM configuration directory
if [ ! -d "tokens" ]; then
mkdir "tokens"
# Init token
softhsm2-util --init-token --slot 0 --label "SC test" --so-pin="$SOPIN" --pin="$PIN"
# Generate 1024b RSA Key pair
generate_cert "RSA:1024" "01" "RSA_auth"
generate_cert "RSA:1024" "02" "RSA_sign"
fi
# NSS DB
if [ ! -d "$NSSDB" ]; then
mkdir "$NSSDB"
modutil -create -dbdir "sql:$NSSDB" -force
modutil -add "SoftHSM PKCS#11" -dbdir "sql:$NSSDB" -libfile "$P11LIB" -force
fi

375
tests/simpletlv.c Normal file
View File

@ -0,0 +1,375 @@
#include <glib.h>
#include <string.h>
#include "simpletlv.h"
static GMainLoop *loop;
/* Test that length estimations are sane */
static void test_length_simple(void)
{
size_t length = 0;
unsigned char simple_value[] = "\x10\x11";
unsigned char long_value[256] = "Long data value";
static struct simpletlv_member simple[1] = {
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
};
simple[0].value.value = simple_value;
/* Simple short value to TLV */
length = simpletlv_get_length(simple, 1, SIMPLETLV_BOTH);
g_assert_cmpint(length, ==, 4);
/* Simple short value to TL */
length = simpletlv_get_length(simple, 1, SIMPLETLV_TL);
g_assert_cmpint(length, ==, 2);
/* Simple short value to V */
length = simpletlv_get_length(simple, 1, SIMPLETLV_VALUE);
g_assert_cmpint(length, ==, 2);
/* Prepare long value */
simple[0].value.value = long_value;
simple[0].length = 256;
/* Simple long value to TLV */
length = simpletlv_get_length(simple, 1, SIMPLETLV_BOTH);
g_assert_cmpint(length, ==, 260);
/* Simple long value to TL */
length = simpletlv_get_length(simple, 1, SIMPLETLV_TL);
g_assert_cmpint(length, ==, 4);
/* Simple long value to V */
length = simpletlv_get_length(simple, 1, SIMPLETLV_VALUE);
g_assert_cmpint(length, ==, 256);
}
static void test_length_nested(void)
{
int length = 0;
unsigned char simple_value[] = "\x12\x14";
static struct simpletlv_member simple[1] = {
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
};
static struct simpletlv_member nested[1] = {
{0x72, 1, {/*.child = simple*/}, SIMPLETLV_TYPE_COMPOUND}
};
simple[0].value.value = simple_value;
nested[0].value.child = simple;
/* Simple short value to TLV */
length = simpletlv_get_length(nested, 1, SIMPLETLV_BOTH);
g_assert_cmpint(length, ==, 6);
/* Nested structures do not support splitting TL and V buffers ?? */
/* Simple short value to TL */
length = simpletlv_get_length(nested, 1, SIMPLETLV_TL);
g_assert_cmpint(length, ==, -1);
/* Simple short value to V */
length = simpletlv_get_length(nested, 1, SIMPLETLV_VALUE);
g_assert_cmpint(length, ==, -1);
}
static void test_length_skipped(void)
{
size_t length = 0;
unsigned char simple_value[] = "\x12\x14";
unsigned char simple_value2[] = "\x16\x18";
static struct simpletlv_member simple[2] = {
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF},
{0x30, 2, {/*.value = simple_value2*/}, SIMPLETLV_TYPE_NONE}
};
simple[0].value.value = simple_value;
simple[1].value.value = simple_value2;
/* Simple short value to TLV */
length = simpletlv_get_length(simple, 2, SIMPLETLV_BOTH);
g_assert_cmpint(length, ==, 4);
/* Simple short value to TL */
length = simpletlv_get_length(simple, 2, SIMPLETLV_TL);
g_assert_cmpint(length, ==, 2);
/* Simple short value to V */
length = simpletlv_get_length(simple, 2, SIMPLETLV_VALUE);
g_assert_cmpint(length, ==, 2);
}
/* Test that we can encode arbitrary data into Simple TLV */
static void test_encode_simple(void)
{
unsigned char *result = NULL;
int result_len = 0;
unsigned char simple_value[] = "\x10\x11";
unsigned char simple_encoded[] = "\x25\x02\x10\x11";
unsigned char long_value[256] = "Long data value";
unsigned char long_encoded[261] = "\x25\xFF\x00\x01Long data value";
static struct simpletlv_member simple[1] = {
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
};
simple[0].value.value = simple_value;
/* Encode simple short TLV with automatic allocation */
result = NULL;
result_len = simpletlv_encode(simple, 1, &result, 0, NULL);
g_assert_cmpmem(result, result_len, simple_encoded, 4);
g_free(result);
/* Encode simple short TLV with pre-allocated buffer (long enough) */
result = g_malloc(10);
result_len = simpletlv_encode(simple, 1, &result, 10, NULL);
g_assert_cmpmem(result, result_len, simple_encoded, 4);
g_free(result);
/* Encode simple short TLV with pre-allocated buffer (too short) */
result = g_malloc(2);
result_len = simpletlv_encode(simple, 1, &result, 2, NULL);
g_assert_cmpint(result_len, ==, -1);
g_free(result);
/* Encode only TL part */
result = NULL;
result_len = simpletlv_encode_tl(simple, 1, &result, 0, NULL);
g_assert_cmpmem(result, result_len, "\x25\x02", 2);
g_free(result);
/* Encode only VALUE part (equals to the value itself) */
result = NULL;
result_len = simpletlv_encode_val(simple, 1, &result, 0, NULL);
g_assert_cmpmem(result, result_len, simple_value, 2);
g_free(result);
/* Prepare long value */
memset(long_value+15, '\x00', 256-15);
memset(long_encoded+19, '\x00', 256-15);
simple[0].value.value = long_value;
simple[0].length = 256;
/* Encode simple long TLV with automatic allocation */
result = NULL;
result_len = simpletlv_encode(simple, 1, &result, 0, NULL);
g_assert_cmpmem(result, result_len, long_encoded, 260);
g_free(result);
/* Encode simple long TLV with pre-allocated buffer (long enough) */
result = g_malloc(300);
result_len = simpletlv_encode(simple, 1, &result, 300, NULL);
g_assert_cmpmem(result, result_len, long_encoded, 260);
g_free(result);
}
/* Test that we can encode nested data into Simple TLV */
static void test_encode_nested(void)
{
unsigned char *result = NULL;
int result_len = 0;
unsigned char simple_value[] = "\x12\x14";
unsigned char encoded[] = "\x72\x04\x25\x02\x12\x14";
static struct simpletlv_member simple[1] = {
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF}
};
static struct simpletlv_member nested[1] = {
{0x72, 1, {/*.child = simple*/}, SIMPLETLV_TYPE_COMPOUND}
};
simple[0].value.value = simple_value;
nested[0].value.child = simple;
/* Encode nested TLV with automatic allocation */
result = NULL;
result_len = simpletlv_encode(nested, 1, &result, 0, NULL);
g_assert_cmpmem(result, result_len, encoded, 6);
g_free(result);
/* Encode nested TLV with pre-allocated buffer (long enough) */
result = g_malloc(10);
result_len = simpletlv_encode(nested, 1, &result, 10, NULL);
g_assert_cmpmem(result, result_len, encoded, 6);
g_free(result);
/* Encode simple short TLV with pre-allocated buffer (too short) */
result = g_malloc(4);
result_len = simpletlv_encode(nested, 1, &result, 4, NULL);
g_assert_cmpint(result_len, ==, -1);
g_free(result);
/* Encode only TL part */
result = NULL;
result_len = simpletlv_encode_tl(nested, 1, &result, 0, NULL);
g_assert_cmpint(result_len, ==, -1);
/* Encode only VALUE part (equals to the value itself) */
result = NULL;
result_len = simpletlv_encode_val(nested, 1, &result, 0, NULL);
g_assert_cmpint(result_len, ==, -1);
}
static void test_encode_skipped(void)
{
unsigned char *result = NULL;
size_t result_len = 0;
unsigned char simple_value[] = "\x12\x14";
unsigned char simple_value2[] = "\x16\x18";
static struct simpletlv_member simple[2] = {
{0x25, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF},
{0x30, 2, {/*.value = simple_value2*/}, SIMPLETLV_TYPE_NONE}
};
unsigned char encoded[] = "\x25\x02\x12\x14";
simple[0].value.value = simple_value;
simple[1].value.value = simple_value2;
/* Simple short value to TLV */
result = NULL;
result_len = simpletlv_encode(simple, 2, &result, 0, NULL);
g_assert_cmpmem(result, result_len, encoded, 4);
g_free(result);
/* Simple short value to TL */
result = NULL;
result_len = simpletlv_encode_tl(simple, 2, &result, 0, NULL);
g_assert_cmpmem(result, result_len, "\x25\x02", 2);
g_free(result);
/* Simple short value to V */
result = NULL;
result_len = simpletlv_encode_val(simple, 2, &result, 0, NULL);
g_assert_cmpmem(result, result_len, "\x12\x14", 2);
g_free(result);
}
static void test_clone_simple(void)
{
unsigned char *result = NULL;
size_t result_len = 0;
unsigned char simple_value[] = "\x14\x18";
unsigned char simple_value2[] = "\x64\x24\x44";
static struct simpletlv_member simple[2] = {
{0x13, 2, {/*.value = simple_value*/}, SIMPLETLV_TYPE_LEAF},
{0xDD, 3, {/*.value = simple_value2*/}, SIMPLETLV_TYPE_LEAF}
};
unsigned char encoded[] = "\x13\x02\x14\x18\xDD\x03\x64\x24\x44";
struct simpletlv_member *clone;
simple[0].value.value = simple_value;
simple[1].value.value = simple_value2;
clone = simpletlv_clone(simple, 2);
g_assert_nonnull(clone);
result = NULL;
result_len = simpletlv_encode(clone, 2, &result, 0, NULL);
g_assert_cmpmem(result, result_len, encoded, 9);
g_free(result);
simpletlv_free(clone, 2);
}
static void test_parse_simple(void)
{
unsigned char data[] = "\x13\x02\x14\x18\xDD\x03\x64\x24\x44";
size_t data_len = 9, tlv_len = 0;
struct simpletlv_member *tlv;
tlv = simpletlv_parse(data, data_len, &tlv_len);
g_assert_cmpint(tlv_len, ==, 2);
g_assert_cmpint(tlv[0].tag, ==, 0x13);
g_assert_cmpint(tlv[0].length, ==, 0x02);
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
g_assert_cmpint(tlv[1].tag, ==, 0xDD);
g_assert_cmpint(tlv[1].length, ==, 0x03);
g_assert_cmpint(tlv[1].type, ==, SIMPLETLV_TYPE_LEAF);
g_assert_cmpmem(tlv[1].value.value, tlv[1].length, "\x64\x24\x44", 3);
simpletlv_free(tlv, tlv_len);
}
/* If there is some gargabe in the end of buffer, we would like to
* return at least what was properly parsed, rahter than to fail
* hard.
* Also makes sure we do not leak memory or crash on bad data.
*
* This is an issue for OpenSC at this moment, which fails to encode
* last TLV into the compound buffer for data objects.
*/
static void test_parse_last_bad(void)
{
size_t data_len = 9;
unsigned char data[] = "\x13\x02\x14\x18\x28\x13\x64\x24\x44";
/* this length is oveflow -^ */
size_t data2_len = 7;
unsigned char data2[] = "\x13\x02\x14\x18\x28\xFF\xFF";
/* this length is missing last byte -^ */
size_t data3_len = 5;
unsigned char data3[] = "\x13\x02\x14\x18\x12";
/* this length is missed completely -^ */
size_t tlv_len = 0;
struct simpletlv_member *tlv;
/* Test the overflow length in the last member */
tlv = simpletlv_parse(data, data_len, &tlv_len);
g_assert_cmpint(tlv_len, ==, 1);
g_assert_cmpint(tlv[0].tag, ==, 0x13);
g_assert_cmpint(tlv[0].length, ==, 0x02);
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
simpletlv_free(tlv, tlv_len);
/* Test the truncated length item in last member */
tlv = simpletlv_parse(data2, data2_len, &tlv_len);
g_assert_cmpint(tlv_len, ==, 1);
g_assert_cmpint(tlv[0].tag, ==, 0x13);
g_assert_cmpint(tlv[0].length, ==, 0x02);
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
simpletlv_free(tlv, tlv_len);
/* Test the missing length item in last member */
tlv = simpletlv_parse(data3, data3_len, &tlv_len);
g_assert_cmpint(tlv_len, ==, 1);
g_assert_cmpint(tlv[0].tag, ==, 0x13);
g_assert_cmpint(tlv[0].length, ==, 0x02);
g_assert_cmpint(tlv[0].type, ==, SIMPLETLV_TYPE_LEAF);
g_assert_cmpmem(tlv[0].value.value, tlv[0].length, "\x14\x18", 2);
simpletlv_free(tlv, tlv_len);
}
int main(int argc, char *argv[])
{
int ret;
g_test_init(&argc, &argv, NULL);
loop = g_main_loop_new(NULL, TRUE);
g_test_add_func("/simpletlv/length/simple", test_length_simple);
g_test_add_func("/simpletlv/length/nested", test_length_nested);
g_test_add_func("/simpletlv/length/skipped", test_length_skipped);
g_test_add_func("/simpletlv/encode/simple", test_encode_simple);
g_test_add_func("/simpletlv/encode/nested", test_encode_nested);
g_test_add_func("/simpletlv/encode/skipped", test_encode_skipped);
g_test_add_func("/simpletlv/parse/simple", test_parse_simple);
g_test_add_func("/simpletlv/parse/last_bad", test_parse_last_bad);
g_test_add_func("/simpletlv/clone/simple", test_clone_simple);
ret = g_test_run();
g_main_loop_unref(loop);
return ret;
}
/* vim: set ts=4 sw=4 tw=0 noet expandtab: */