pax_global_header 0000666 0000000 0000000 00000000064 14161030125 0014503 g ustar 00root root 0000000 0000000 52 comment=a7f717a4b486e49da06efd6065dcb5612aa48e7d
liboauth2-1.4.4/ 0000775 0000000 0000000 00000000000 14161030125 0013402 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/.autotools 0000664 0000000 0000000 00000006053 14161030125 0015440 0 ustar 00root root 0000000 0000000
liboauth2-1.4.4/.clang-format 0000664 0000000 0000000 00000000264 14161030125 0015757 0 ustar 00root root 0000000 0000000 BasedOnStyle: LLVM
IndentWidth: 8
UseTab: Always
BreakBeforeBraces: Linux
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
AllowShortFunctionsOnASingleLine: None
liboauth2-1.4.4/.cproject 0000664 0000000 0000000 00000062354 14161030125 0015226 0 ustar 00root root 0000000 0000000
make
all
true
true
false
make
clean
true
true
false
make
clang-format
true
true
true
make
check
true
true
true
liboauth2-1.4.4/.dockerignore 0000664 0000000 0000000 00000001256 14161030125 0016062 0 ustar 00root root 0000000 0000000 Makefile.in
aclocal.m4
ar-lib
compile
config.guess
config.sub
configure
depcomp
install-sh
ltmain.sh
missing
Makefile
config.log
config.status
libtool
test-driver
build/
liboauth2-*-coverage/
liboauth2-*-coverage.info
liboauth2.pc
.libs/**/*
.libs
.settings/**/*
.settings
src/.deps/**/*
src/.deps
src/.libs/**/*
src/.libs
src/.dirstamp
src/cache/.deps/**/*
src/cache/.deps
src/cache/.libs/**/*
src/cache/.libs
src/cache/.dirstamp
src/cfg/.deps/**/*
src/cfg/.deps
src/cfg/.libs/**/*
src/cfg/.libs
src/cfg/.dirstamp
src/server/.deps/**/*
src/server/.deps
src/server/.libs/**/*
src/server/.libs
src/server/.dirstamp
*.log
*.trs
check_liboauth2
*.la
liboauth2_apache.pc
liboauth2_nginx.pc
liboauth2-1.4.4/.github/ 0000775 0000000 0000000 00000000000 14161030125 0014742 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/.github/workflows/ 0000775 0000000 0000000 00000000000 14161030125 0016777 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/.github/workflows/archs.yml 0000664 0000000 0000000 00000003411 14161030125 0020621 0 ustar 00root root 0000000 0000000 name: Archs
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
name: ${{ matrix.arch }}
strategy:
matrix:
include:
#test/check_cache.c:74:F:core:test_cache_redis:0: Assertion 'value == ((void *)0)' failed: value == 0x4006a290, ((void *)0) == 0
#test/check_nginx.c:102:E:core:test_request_context:0: (after this point) Test timeout expired
#test/check_nginx.c:114:E:core:test_nginx_http_response_set:0: (after this point) Received signal 11 (Segmentation fault)
# - arch: armv7
# distro: ubuntu20.04
- arch: aarch64
distro: ubuntu20.04
- arch: ppc64le
distro: ubuntu20.04
- arch: s390x
distro: ubuntu20.04
steps:
- uses: actions/checkout@v2.4.0
- uses: uraimo/run-on-arch-action@v2.1.1
name: Build
id: build
with:
arch: ${{ matrix.arch }}
distro: ${{ matrix.distro }}
install: |
apt-get update -y
apt-get install -y apache2-dev libcjose-dev libssl-dev check pkg-config wget
apt-get install -y libjansson-dev libcurl4-openssl-dev libhiredis-dev libmemcached-dev redis-server memcached
apt-get install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libgd-dev libxml2 libxml2-dev uuid-dev
cd /tmp
wget --no-check-certificate https://nginx.org/download/nginx-1.18.0.tar.gz
tar zxvf nginx-1.18.0.tar.gz
ln -s nginx-1.18.0 nginx
cd /tmp/nginx && ./configure --with-debug
run: |
./autogen.sh
./configure --with-nginx=/tmp/nginx
service memcached start && service redis-server start && make check || (cat test-suite.log && exit -1)
liboauth2-1.4.4/.github/workflows/build.yml 0000664 0000000 0000000 00000002641 14161030125 0020624 0 ustar 00root root 0000000 0000000 name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
services:
redis:
image: redis
# options: >-
# --health-cmd "redis-cli ping"
# --health-interval 10s
# --health-timeout 5s
# --health-retries 5
ports:
- 6379:6379
memcached:
image: memcached
ports:
- 11211:11211
steps:
- uses: actions/checkout@v2
- name: Dependencies
env:
NGINX_VERSION: 1.18.0
run: |
sudo apt-get update -y
sudo apt-get install -y apache2-dev libcjose-dev libssl-dev check pkg-config
sudo apt-get install -y libjansson-dev libcurl4-openssl-dev libhiredis-dev libmemcached-dev
sudo apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libgd-dev libxml2 libxml2-dev uuid-dev
cd /tmp
wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar zxvf nginx-${NGINX_VERSION}.tar.gz
ln -s nginx-${NGINX_VERSION} nginx
cd /tmp/nginx && ./configure --with-debug
- name: Configure
run: |
./autogen.sh
./configure --with-nginx=/tmp/nginx
- name: Make
run: make
- name: Test
run: make check || (cat test-suite.log && exit -1)
- name: Distcheck
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--with-nginx=/tmp/nginx" DESTDIR="/tmp/liboauth2"
liboauth2-1.4.4/.github/workflows/codeql-analysis.yml 0000664 0000000 0000000 00000001626 14161030125 0022617 0 ustar 00root root 0000000 0000000 name: "CodeQL"
on: [push, pull_request]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y apache2-dev libcjose-dev libssl-dev check pkg-config
sudo apt-get install -y libjansson-dev libcurl4-openssl-dev libhiredis-dev libmemcached-dev
- name: CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Configure
run: |
./autogen.sh
./configure
- name: Make
run: make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1 liboauth2-1.4.4/.gitignore 0000664 0000000 0000000 00000000645 14161030125 0015377 0 ustar 00root root 0000000 0000000 /Makefile.in
/aclocal.m4
/ar-lib
/compile
/config.guess
/config.sub
/configure
/depcomp
/install-sh
/ltmain.sh
/missing
/Makefile
/config.log
/config.status
/libtool
/test-driver
/build/
/liboauth2-*-coverage/
/liboauth2-*-coverage.info
/liboauth2.pc
/.libs/
/*.log
/*.trs
/check_liboauth2
/*.la
/liboauth2_apache.pc
/liboauth2_nginx.pc
/ChangeLog.org
/.settings/
/autom4te.cache/
/config.guess~
/config.sub~
/configure~
liboauth2-1.4.4/.project 0000664 0000000 0000000 00000001452 14161030125 0015053 0 ustar 00root root 0000000 0000000
liboauth2
org.eclipse.cdt.managedbuilder.core.genmakebuilder
clean,full,incremental,
org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
full,incremental,
org.eclipse.cdt.core.cnature
org.eclipse.cdt.core.ccnature
org.eclipse.cdt.managedbuilder.core.managedBuildNature
org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
liboauth2-1.4.4/AUTHORS 0000664 0000000 0000000 00000000675 14161030125 0014462 0 ustar 00root root 0000000 0000000 The primary author of liboauth2 is:
Hans Zandbelt
Thanks to the following people for contributing to liboauth2 by
reporting bugs, providing fixes, suggesting useful features or other:
Nicolas Mora
Phil Brown
Alexander Bokovoy
Niebardzo
Mikael Broadfoot
liboauth2-1.4.4/ChangeLog 0000664 0000000 0000000 00000007414 14161030125 0015162 0 ustar 00root root 0000000 0000000 12/23/2021
- allow deprecated declarations to build with OpenSSL 3.0; see #31
- release 1.4.4
12/22/2021
- hash the cache encryption key to a string instead of bytes
- Makefile.am improvements:
- move OpenSSL libs go generic libraries so cache files compile with the right flags
- use ${srcdir} to conform to distcheck
- add Github Actions CI; remove Travis
10/12/2021
- make outgoing_proxy an endpoint property
- accommodate for NULL key in oauth2_cache_get and oauth2_cache_set
- release 1.4.3.2
10/11/2021
- add outgoing_proxy option to verify context
- correct remote_user debug printout
- release 1.4.3.1
06/21/2021
- printout remote username claim when not found, for debugging purposes
06/10/2021
- use encrypted JWTs for storing encrypted cache contents and avoid using static AAD/IV
closes #26; thanks @niebardzo
- avoid memory leaks on JWT validation errors
- release 1.4.3
06/07/2021
- correct iat slack validation defaults, see https://github.com/zmartzone/mod_oauth2/discussions/20
thanks @DrakezulsMinimalism
- release 1.4.2.1
05/28/2021
- add Travis and LGTM
05/25/2021
- set memory alignment of shm cache structs to 64 bytes; see #21 and #24
- release 1.4.2
04/19/2021
- apache: use include directory from APXS; thanks @abbra
- pass missing argument to oauth2_error in _oauth2_dpop_jti_validate; thanks @abbra
02/02/2021
- avoid creating files for anonymous shared memory segments; see #18
- release 1.4.1
01/30/2021
- fix Apache cleanup routines; see zmartzone/liboauth2#18 and zmartzone/mod_oauth2#7
01/26/2021
- add support for RFC 8705 OAuth 2.0 Mutual-TLS Certificate-Bound Access Tokens
https://tools.ietf.org/html/rfc8705; thanks @vdzhuvinov
12/23/2020
- use per-process semaphore locking to prevent multi-process issue; see #18
- release 1.4.0.1
12/21/2020
- release 1.4.0
12/03/2020
- add oauth2_cfg_openidc_set_options for configurable state cookie handling
12/02/2020
- cleanup OIDC expired/superfluous state cookies; closes zmartzone/ngx_openidc_module#6
11/13/2020
- add support for PKCE
11/12/2020
- separate OpenID client configs and named providers
- fix parsing in oauth2_cfg_set_flag_slot
- add configurable state and session cookie paths
11/11/2020
- fix session cache handler cloning
- support configurable cookie path for session cookie
11/09/2020
- refactored caching; use named caches consistently
11/08/2020
- use endpoint more consistently
- harmonize naming of endpoint, endpoint auth and ropc
11/07/2020
- don't use automake config.h; closes #10; thanks @babelouest
10/07/2020
- add support for DPOP bound access tokens
- bump to 1.4.0-dev
02/27/2020
- lock access to cache globals
- log corrections and improvements
02/26/2020
- resolve some TODOs; valgrind
- bump to 1.3.0
02/25/2020
- change to named sessions
02/21/2020
- add serialized id_token to session
- externalize oauth2_jose_jwt_verify and allow verification context to be NULL
- bump to 1.2.5
02/13/2020
- add userinfo endpoint request and claims
- bump to 1.2.4
- change to named cache configurations
02/10/2020
- implement session expiry checks
- bump to 1.2.3
02/05/2020
- add missing ROPC config functions
- bump to 1.2.2
02/04/2020
- add generic endpoint config struct and ROPC client capability
- bump to 1.2.1 and bump copyright year
01/31/2020
- sane session cfg defaults
09/12/2019
- change http request header function naming
- more openidc handling
- bump to 1.2.0
09/02/2019
- fix type (auth->client_secret_jwt.aud = NULL); closes #3; thanks @pengjiaoyang
08/19/2019
- add first outline of openidc and sessions
07/03/2019
- return status code from HTTP callouts
- bump to version 1.1.1
07/01/2019
- encapsulate oauth2_log_sink_t
- bump to version 1.1.0
05/20/2019
- add Apache Require claim authorization functions
- bump to version 1.0.1
03/22/2019
- initial import of version 1.0.0
liboauth2-1.4.4/LICENSE 0000664 0000000 0000000 00000103333 14161030125 0014412 0 ustar 00root root 0000000 0000000 GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, 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
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero 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
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
.
liboauth2-1.4.4/Makefile.am 0000664 0000000 0000000 00000013544 14161030125 0015445 0 ustar 00root root 0000000 0000000 ACLOCAL_AMFLAGS=-I m4
EXTRA_DIST = autogen.sh ChangeLog README.md LICENSE
#
# generic
#
AM_CPPFLAGS = -Wall -Werror -Wno-error=deprecated-declarations -I${srcdir}/include -I${srcdir}/src @JANSSON_CFLAGS@ @OPENSSL_CFLAGS@
AM_CPPFLAGS += $(CODE_COVERAGE_CPPFLAGS) $(CODE_COVERAGE_CFLAGS)
AM_LDFLAGS = --coverage
LDADD = @JANSSON_LIBS@ @OPENSSL_LIBS@
LDADD += $(CODE_COVERAGE_LIBS)
#
# headers
#
includesubdir = $(includedir)/oauth2
includesub_HEADERS = \
include/oauth2/cache.h \
include/oauth2/http.h \
include/oauth2/ipc.h \
include/oauth2/jose.h \
include/oauth2/log.h \
include/oauth2/mem.h \
include/oauth2/proto.h \
include/oauth2/oauth2.h \
include/oauth2/openidc.h \
include/oauth2/session.h \
include/oauth2/cfg.h \
include/oauth2/util.h \
include/oauth2/version.h
#
# liboauth
#
lib_LTLIBRARIES = liboauth2.la
liboauth2_la_pkgconfigdir = $(libdir)/pkgconfig
liboauth2_la_pkgconfig_DATA = liboauth2.pc
liboauth2_la_CFLAGS = @CURL_CFLAGS@ @CJOSE_CFLAGS@
liboauth2_la_LIBADD = @CURL_LIBS@ @CJOSE_LIBS@
liboauth2_la_SOURCES = \
src/version.c \
src/mem.c \
src/log.c \
src/util_int.h \
src/util.c \
src/cfg_int.h \
src/cfg/cfg.c \
src/cfg/auth.c \
src/cfg/source.c \
src/cfg/cache_cfg.c \
src/cfg/verify.c \
src/cfg/target.c \
src/cfg/proto_cfg.c \
src/cfg/openidc_cfg.c \
src/cfg/session_cfg.c \
src/ipc.c \
src/cache_int.h \
src/cache/shm.c \
src/cache/file.c \
src/jose_int.h \
src/jose.c \
src/http.c \
src/proto.c \
src/oauth2_int.h \
src/oauth2.c \
src/dpop.c \
src/session.c \
src/openidc_int.h \
src/openidc/provider.c \
src/openidc/resolver.c \
src/openidc/client.c \
src/openidc/state.c \
src/openidc/openidc.c
#
# cache
#
noinst_LTLIBRARIES = liboauth2_cache.la
liboauth2_la_LIBADD += liboauth2_cache.la
liboauth2_cache_la_SOURCES = src/cache.c
liboauth2_cache_la_CPPFLAGS = $(AM_CPPFLAGS)
if HAVE_LIBMEMCACHE
liboauth2_cache_la_CPPFLAGS += -DHAVE_LIBMEMCACHE
liboauth2_la_LIBADD += liboauth2_memcache.la
noinst_LTLIBRARIES += liboauth2_memcache.la
liboauth2_memcache_la_SOURCES = src/cache/memcache.c
liboauth2_memcache_la_CFLAGS = @MEMCACHE_CFLAGS@
liboauth2_memcache_la_LIBADD = @MEMCACHE_LIBS@
endif
if HAVE_LIBHIREDIS
liboauth2_cache_la_CPPFLAGS += -DHAVE_LIBHIREDIS
liboauth2_la_LIBADD += liboauth2_redis.la
noinst_LTLIBRARIES += liboauth2_redis.la
liboauth2_redis_la_SOURCES = src/cache/redis.c
liboauth2_redis_la_CFLAGS = @HIREDIS_CFLAGS@
liboauth2_redis_la_LIBADD = @HIREDIS_LIBS@
endif
#
# liboauth2_apache
#
if HAVE_APACHE
lib_LTLIBRARIES += liboauth2_apache.la
liboauth2_apache_la_pkgconfigdir = $(libdir)/pkgconfig
liboauth2_apache_la_pkgconfig_DATA = liboauth2_apache.pc
liboauth2_apache_la_CPPFLAGS = $(AM_CPPFLAGS) -DHAVE_APACHE
liboauth2_apache_la_CFLAGS = @APACHE_CFLAGS@
liboauth2_apache_la_LIBADD = liboauth2.la @APR_LIBS@
includesub_HEADERS += \
include/oauth2/apache.h
liboauth2_apache_la_SOURCES = \
src/server/apache.c
endif
#
# liboauth2_nginx
#
if HAVE_NGINX
lib_LTLIBRARIES += liboauth2_nginx.la
liboauth2_nginx_la_pkgconfigdir = $(libdir)/pkgconfig
liboauth2_nginx_la_pkgconfig_DATA = liboauth2_nginx.pc
liboauth2_nginx_la_CPPFLAGS = $(AM_CPPFLAGS) -DHAVE_NGINX
liboauth2_nginx_la_CFLAGS = @NGINX_CFLAGS@
liboauth2_nginx_la_LIBADD = liboauth2.la @NGINX_LIBS@
includesub_HEADERS += \
include/oauth2/nginx.h
liboauth2_nginx_la_SOURCES = \
src/server/nginx.c
endif
#
# check
#
if HAVE_CHECK
TESTS = check_liboauth2
check_PROGRAMS = $(TESTS)
check_liboauth2_CPPFLAGS = $(liboauth2_cache_la_CPPFLAGS)
check_liboauth2_CFLAGS = @OPENSSL_CFLAGS@ @CURL_CFLAGS@ @CJOSE_CFLAGS@ @CHECK_CFLAGS@
check_liboauth2_LDADD = liboauth2.la
if HAVE_APACHE
check_liboauth2_CPPFLAGS += $(liboauth2_apache_la_CPPFLAGS)
check_liboauth2_CFLAGS += $(liboauth2_apache_la_CFLAGS)
check_liboauth2_LDADD += liboauth2_apache.la ${liboauth2_apache_la_LIBADD}
endif
if HAVE_NGINX
check_liboauth2_CPPFLAGS += $(liboauth2_nginx_la_CPPFLAGS)
check_liboauth2_CFLAGS += $(liboauth2_nginx_la_CFLAGS)
check_liboauth2_LDADD += liboauth2_nginx.la
endif
check_liboauth2_LDADD += @OPENSSL_LIBS@ @CURL_LIBS@ @CJOSE_LIBS@ @CHECK_LIBS@
check_liboauth2_SOURCES = \
test/check_liboauth2.h \
test/check_liboauth2.c \
test/check_version.c \
test/check_mem.c \
test/check_log.c \
test/check_cfg.c \
test/check_util.c \
test/check_ipc.c \
test/check_cache.c \
test/check_jose.c \
test/check_http.c \
test/check_proto.c \
test/check_oauth2.c \
test/check_openidc.c \
test/server_stubs.c \
test/provider.json \
test/client.json
if HAVE_APACHE
check_liboauth2_SOURCES += \
test/check_apache.c
endif
if HAVE_NGINX
check_liboauth2_SOURCES += \
test/check_nginx.c
endif
endif
#@CODE_COVERAGE_RULES@
clang-format:
clang-format -style=file -i `find . -name *.[ch]`
test: check
TAG=liboauth2/test
docker: clean
docker build -f test/Dockerfile . -t $(TAG)
docker-check: docker
docker run -it --rm $(TAG):latest /bin/bash -c "./start.sh && make check"
docker-coverage: clean
docker build --build-arg CONFIGURE_ARGS="--enable-code-coverage" -f test/Dockerfile . -t $(TAG)
docker run -it --rm $(TAG):latest /bin/bash -c "./start.sh && make check-code-coverage"
docker-valgrind: docker
docker run -it --rm -e CK_FORK=no $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/valgrind --leak-check=full --show-leak-kinds=all .libs/check_liboauth2"
docker-valgrind-%: docker
docker run -it --rm -e CK_FORK=no -e CK_RUN_SUITE=${subst docker-valgrind-,,$@} $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/valgrind --leak-check=full --show-leak-kinds=all .libs/check_liboauth2"
docker-gdb: docker
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it --rm -e CK_FORK=no $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/gdb .libs/check_liboauth2"
docker-gdb-%: docker
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it --rm -e CK_FORK=no -e CK_RUN_SUITE=${subst docker-gdb-,,$@} $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/gdb .libs/check_liboauth2"
liboauth2-1.4.4/README.md 0000664 0000000 0000000 00000011145 14161030125 0014663 0 ustar 00root root 0000000 0000000 [](https://github.com/zmartzone/liboauth2/actions/workflows/build.yml)
[](https://github.com/zmartzone/liboauth2/actions/workflows/archs.yml)
[](https://github.com/zmartzone/liboauth2/actions/workflows/codeql-analysis.yml)
[](https://lgtm.com/projects/g/zmartzone/liboauth2/context:cpp)
[](https://lgtm.com/projects/g/zmartzone/liboauth2/alerts/)
# liboauth2
Generic library to build C-based OAuth 2.x and OpenID Connect servers and clients e.g. web-server plugins.
## Overview
- extends [cjose](https://github.com/cisco/cjose) into OAuth 2.x and OpenID Connect specific claims, secrets, and hashes
- adds OAuth 2.x and OpenID Connect protocols by abstracting HTTP requests and responses from web server implementation specifics
- reusable code across other OAuth 2.x and REST related protocols
e.g. token exchange with endpoint authentication, source token retrieval, target pass settings etc.
- generic code with plugins for Apache, NGINX, and possibly more (e.g. Envoy, HA Proxy, IIS)
- configurable cache backend/size/options per cache element type
- cookie-based session management (i.e. enforce inactivity timeout, expiry)
## Features
- [OpenID Connect 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
- OAuth 2.0 Resource Owner Password Credentials ([RFC 6749](https://tools.ietf.org/html/rfc6749#section-4.3))
- OAuth 2.0 Token Introspection ([RFC 7662](https://tools.ietf.org/html/rfc7662))
- JWT bearer token validation using JWK, JWKS URI, shared symmetric key, X.509 cert, and RSA public key ([RFC 6750](https://tools.ietf.org/html/rfc6750))
- OAuth 2.0 Authorization Server Metadata ([RFC 8414](https://tools.ietf.org/html/rfc8414))
- Proof Key for Code Exchange (PCKE) by OAuth Public Clients ([RFC 7636](https://tools.ietf.org/html/rfc7636))
- OAuth 2.0 Mutual-TLS (MTLS) Certificate-Bound Access Tokens ([RFC 8705](https://tools.ietf.org/html/rfc8705))
- OAuth 2.0 Demonstration of Proof-of-Possession (DPoP) at the Application Layer ([Internet-Draft](https://tools.ietf.org/html/draft-ietf-oauth-dpop))
- Amazon ALB [EC key URL based `x-amzn-oidc-data` JWT verification](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html)
- endpoint authentication methods: `client_secret_basic`, `client_secret_post`, [`client_secret_jwt`, `private_key_jwt`](https://tools.ietf.org/html/rfc7523), [TLS client certificate](https://tools.ietf.org/id/draft-ietf-oauth-mtls), and HTTP basic authentication
- configurable cache backends: shared memory, file-based, memcache, and Redis
- retrieving a token from a header, a query parameter, a post parameter, or a cookie
- setting a token as a header, a query parameter, a post parameter, or a cookie
- Apache and NGINX bindings
## Dependencies
liboauth2 depends on the following libraries:
- [`openssl`](https://www.openssl.org/) for SSL and crypto support
- [`libcurl`](https://curl.haxx.se/libcurl/) for HTTP client support
- [`jansson`](http://www.digip.org/jansson/) for JSON parsing
- [`cjose`](https://github.com/cisco/cjose) for JSON Object Signing and Encryption (JOSE) support
- (optional) [`libmemcached`](https://libmemcached.org) for memcache cache backend support
- (optional) [`libhiredis`](https://github.com/redis/hiredis) for Redis cache backend support
- (optional) [`Apache 2.x`](https://httpd.apache.org/) for Apache 2.x bindings support
- (optional) [`NGINX`](https://nginx.org) for NGINX bindings support
- (optional, build time only) [`check`](https://libcheck.github.io/check/) for unit test support
## Support
### Community Support
See [Frequently Asked Questions](https://github.com/zmartzone/liboauth2/wiki) on the Wiki.
Ask questions in the [Discussions](https://github.com/zmartzone/liboauth2/discussions) tracker.
### Commercial Support
For commercial support contracts, professional services, training, and use-case specific support, contact [ZmartZone IAM](https://www.zmartzone.eu) at:
[sales@zmartzone.eu](mailto:sales@zmartzone.eu)
Disclaimer
----------
*This software is open sourced by ZmartZone IAM. For commercial support
you can contact [ZmartZone IAM](https://www.zmartzone.eu) as described above in the [Support](#support) section.*
liboauth2-1.4.4/autogen.sh 0000775 0000000 0000000 00000000076 14161030125 0015406 0 ustar 00root root 0000000 0000000 #!/bin/sh
autoreconf --force --install
rm -rf autom4te.cache/
liboauth2-1.4.4/configure.ac 0000664 0000000 0000000 00000006674 14161030125 0015705 0 ustar 00root root 0000000 0000000 AC_INIT([liboauth2],[1.4.4],[hans.zandbelt@zmartzone.eu])
AM_INIT_AUTOMAKE([foreign no-define subdir-objects])
AC_CONFIG_MACRO_DIR([m4])
AM_PROG_AR
LT_INIT([dlopen])
AC_PROG_CC
AX_CODE_COVERAGE
PKG_CHECK_MODULES(OPENSSL, openssl)
AC_SUBST(OPENSSL_CFLAGS)
AC_SUBST(OPENSSL_LIBS)
PKG_CHECK_MODULES(CURL, libcurl)
AC_SUBST(CURL_CFLAGS)
AC_SUBST(CURL_LIBS)
PKG_CHECK_MODULES(JANSSON, jansson)
AC_SUBST(JANSSON_CFLAGS)
AC_SUBST(JANSSON_LIBS)
PKG_CHECK_MODULES(CJOSE, cjose)
AC_SUBST(CJOSE_CFLAGS)
AC_SUBST(CJOSE_LIBS)
AC_ARG_WITH([memcache], AS_HELP_STRING([--with-memcache], [build with Memcache cache support [default=autodetect]]),)
if test "x$with_memcache" != "xno"; then
PKG_CHECK_MODULES([MEMCACHE], [libmemcached >= 0.8.0], [have_memcache="yes"], [have_memcache="no"])
fi
AM_CONDITIONAL(HAVE_LIBMEMCACHE, [test x"$have_memcache" = "xyes"])
AC_SUBST(MEMCACHE_CFLAGS)
AC_SUBST(MEMCACHE_LIBS)
if test x"$have_memcache" = "xyes"; then
MEMCACHE_PC=', libmemcached >= 0.8.0'
fi
AC_SUBST(MEMCACHE_PC)
AC_ARG_WITH([redis], AS_HELP_STRING([--with-redis], [build with Redis cache support [default=autodetect]]),)
if test "x$with_redis" != "xno"; then
PKG_CHECK_MODULES([HIREDIS], [hiredis], [have_redis="yes"], [have_redis="no"])
fi
AM_CONDITIONAL(HAVE_LIBHIREDIS, [test x"$have_redis" = "xyes"])
AC_SUBST(HIREDIS_CFLAGS)
AC_SUBST(HIREDIS_LIBS)
if test x"$have_redis" = "xyes"; then
HIREDIS_PC=', hiredis'
fi
AC_SUBST(HIREDIS_PC)
AC_ARG_WITH([apache], AS_HELP_STRING([--with-apache], [build with Apache support [default=autodetect]]),)
AC_ARG_WITH([apxs],
[AS_HELP_STRING([--with-apxs=PATH/NAME],[path to the apxs binary for Apache [[apxs]]])],
[AC_SUBST(APXS, $with_apxs)],
[AC_PATH_PROGS(APXS, [apxs2 apxs])])
if test "x$with_apache" != "xno"; then
PKG_CHECK_MODULES([APR], [apr-1, apr-util-1], [have_apache="yes"], [have_apache="no"])
AS_IF([test "x${APXS}" != "x" -a -x "${APXS}"],
[AC_MSG_NOTICE([apxs found at $APXS])],
[AC_MSG_FAILURE(["apxs not found. Use --with-apxs"])])
APACHE_CFLAGS="`${APXS} -q CFLAGS` `${APXS} -q EXTRA_CPPFLAGS` -I`${APXS} -q INCLUDEDIR` ${APR_CFLAGS}"
fi
AM_CONDITIONAL(HAVE_APACHE, [test x"$have_apache" = "xyes"])
AC_SUBST(APR_LIBS)
AC_SUBST(APACHE_CFLAGS)
AC_ARG_WITH([nginx], AS_HELP_STRING([--with-nginx=DIR], [build with NGINX support [default=no]]), [have_nginx="yes"], [have_nginx="no"])
if test x"$have_nginx" = "xyes" ; then
if test ! -d "$withval"; then
AC_MSG_ERROR([Could not find NGINX. Please specify the path to the NGINX sources using the --with-nginx=/full/path/to/nginx- option.])
else
NGINX_CFLAGS="-I $withval/src/core -I $withval/src/event -I $withval/src/event/modules -I $withval/src/os/unix -I $withval/objs -I $withval/src/http -I $withval/src/http/v2 -I $withval/src/http/modules"
NGINX_LIBS=
CPPFLAGS_SAVE="$CPPFLAGS"
CPPFLAGS="$NGINX_CFLAGS $CPPFLAGS"
AC_CHECK_HEADER([nginx.h], [], [have_nginx="no" NGINX_CFLAGS= NGINX_LIBS=])
CPPFLAGS="$CPPFLAGS_SAVE"
fi
fi
AM_CONDITIONAL(HAVE_NGINX, [test x"$have_nginx" = "xyes"])
AC_SUBST(NGINX_CFLAGS)
AC_SUBST(NGINX_LIBS)
PKG_CHECK_MODULES([CHECK],
[check >= 0.9.4],
[have_check="yes"],
[ AC_MSG_WARN([Check not found; cannot run unit tests!]);
[have_check="no"]
])
AM_CONDITIONAL(HAVE_CHECK, [test x"$have_check" = "xyes"])
AC_SUBST(CHECK_CFLAGS)
AC_SUBST(CHECK_LIBS)
AC_CONFIG_FILES([
include/oauth2/version.h
liboauth2.pc
liboauth2_apache.pc
liboauth2_nginx.pc
Makefile
])
AC_OUTPUT
liboauth2-1.4.4/include/ 0000775 0000000 0000000 00000000000 14161030125 0015025 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/include/oauth2/ 0000775 0000000 0000000 00000000000 14161030125 0016227 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/include/oauth2/.gitignore 0000664 0000000 0000000 00000000072 14161030125 0020216 0 ustar 00root root 0000000 0000000 /version.h
/stamp-h1
/config.h.in~
/config.h.in
/config.h
liboauth2-1.4.4/include/oauth2/apache.h 0000664 0000000 0000000 00000036230 14161030125 0017625 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_APACHE_H_
#define _OAUTH2_APACHE_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include
#include
#include
// avoid errors about ap_auto_config overriding these, so undefine first
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include
#include
#include
#include
extern oauth2_cfg_server_callback_funcs_t oauth2_apache_server_callback_funcs;
/*
* logging
*/
extern oauth2_uint_t log_level_log2apache[];
extern oauth2_uint_t log_level_apache2oauth2[];
#ifndef APLOG_USE_MODULE
#define APLOG_USE_MODULE(foo) \
extern module AP_MODULE_DECLARE_DATA foo##_module; \
AP_MAYBE_UNUSED(static int *const aplog_module_index) = \
&(foo##_module.module_index)
#endif
#define OAUTH2_APACHE_LOG(foo) \
\
APLOG_USE_MODULE(foo); \
\
static void foo##_log_server( \
oauth2_log_sink_t *sink, const char *filename, unsigned long line, \
const char *function, oauth2_log_level_t level, const char *msg) \
{ \
ap_log_error( \
filename, line, \
aplog_module_index ? *aplog_module_index \
: APLOG_NO_MODULE, \
log_level_log2apache[level], 0, \
(const server_rec *)oauth2_log_sink_ctx_get(sink), \
"%s: %s", function, msg); \
} \
\
static void foo##_log_request( \
oauth2_log_sink_t *sink, const char *filename, unsigned long line, \
const char *function, oauth2_log_level_t level, const char *msg) \
{ \
ap_log_rerror( \
filename, line, \
aplog_module_index ? *aplog_module_index \
: APLOG_NO_MODULE, \
log_level_log2apache[level], 0, \
(const request_rec *)oauth2_log_sink_ctx_get(sink), \
"%s: %s", function, msg); \
}
/*
* parent/child cleanup
*/
apr_status_t oauth2_apache_child_cleanup(void *data, module *m,
const char *package_name_version);
#define OAUTH2_APACHE_CHILD_CLEANUP(foo) \
static apr_status_t foo##_child_cleanup(void *data) \
{ \
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, \
(const server_rec *)data, "%s: %s", __FUNCTION__, \
"enter"); \
return oauth2_apache_child_cleanup( \
data, &foo##_module, OAUTH2_PACKAGE_NAME_VERSION); \
}
apr_status_t oauth2_apache_parent_cleanup(void *data, module *m,
const char *package_name_version);
#define OAUTH2_APACHE_PARENT_CLEANUP(foo) \
static apr_status_t foo##_parent_cleanup(void *data) \
{ \
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, \
(const server_rec *)data, "%s: %s", __FUNCTION__, \
"enter"); \
return oauth2_apache_parent_cleanup( \
data, &foo##_module, OAUTH2_PACKAGE_NAME_VERSION); \
}
/*
* post config
*/
int oauth2_apache_post_config(apr_pool_t *pool, apr_pool_t *p1, apr_pool_t *p2,
server_rec *s, module *m,
const char *package_name_version,
apr_status_t (*parent_cleanup)(void *),
apr_status_t (*child_cleanup)(void *));
#define OAUTH2_APACHE_POST_CONFIG(foo) foo##_post_config
#define OAUTH2_APACHE_POST_CONFIG_IMPL(foo) \
static apr_status_t OAUTH2_APACHE_POST_CONFIG(foo)( \
apr_pool_t * pool, apr_pool_t * p1, apr_pool_t * p2, \
server_rec * s) \
{ \
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, \
(const server_rec *)s, "%s: %s", __FUNCTION__, \
"enter"); \
return oauth2_apache_post_config( \
pool, p1, p2, s, &foo##_module, \
OAUTH2_PACKAGE_NAME_VERSION, foo##_parent_cleanup, \
foo##_child_cleanup); \
}
/*
* directory config
*/
#define OAUTH2_APACHE_CMD_ARGS1(module, type, primitive, func, member) \
static const char *apache_##module##_set_##primitive( \
cmd_parms *cmd, void *m, const char *v1) \
{ \
oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \
cmd->server->module_config, &module##_module); \
type *cfg = (type *)m; \
(void)cfg; \
return func(srv_cfg->log, member, v1); \
}
#define OAUTH2_APACHE_CMD_ARGS2(module, type, primitive, func, member) \
static const char *apache_##module##_set_##primitive( \
cmd_parms *cmd, void *m, const char *v1, const char *v2) \
{ \
oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \
cmd->server->module_config, &module##_module); \
type *cfg = (type *)m; \
(void)cfg; \
return func(srv_cfg->log, member, v1, v2); \
}
#define OAUTH2_APACHE_CMD_ARGS3(module, type, primitive, func, member) \
static const char *apache_##module##_set_##primitive( \
cmd_parms *cmd, void *m, const char *v1, const char *v2, \
const char *v3) \
{ \
oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \
cmd->server->module_config, &module##_module); \
type *cfg = (type *)m; \
(void)cfg; \
return func(srv_cfg->log, member, v1, v2, v3); \
}
#define OAUTH2_APACHE_CMD_ARGS(module, nargs, cmd, member, desc) \
AP_INIT_TAKE##nargs(cmd, apache_##module##_set_##member, NULL, \
RSRC_CONF | ACCESS_CONF | OR_AUTHCFG, desc)
#define OAUTH2_APACHE_DIR_CTX(type, method) oauth2_##type##_dir_##method
#define OAUTH2_APACHE_DIR_CTX_FUNCS(type) \
apr_status_t OAUTH2_APACHE_DIR_CTX(type, cleanup)(void *data) \
{ \
oauth2_##type##_t *cfg = (oauth2_##type##_t *)data; \
oauth2_##type##_free(NULL, cfg); \
return APR_SUCCESS; \
} \
\
void *OAUTH2_APACHE_DIR_CTX(type, create)(apr_pool_t * pool, \
char *path) \
{ \
oauth2_##type##_t *cfg = oauth2_##type##_create(NULL, path); \
apr_pool_cleanup_register( \
pool, cfg, OAUTH2_APACHE_DIR_CTX(type, cleanup), \
OAUTH2_APACHE_DIR_CTX(type, cleanup)); \
return cfg; \
} \
\
static void *OAUTH2_APACHE_DIR_CTX(type, merge)(apr_pool_t * pool, \
void *b, void *a) \
{ \
oauth2_##type##_t *cfg = \
OAUTH2_APACHE_DIR_CTX(type, create)(pool, NULL); \
oauth2_##type##_t *base = b; \
oauth2_##type##_t *add = a; \
oauth2_##type##_merge(NULL, cfg, base, add); \
return cfg; \
}
/*
* server config
*/
typedef struct oauth2_apache_cfg_srv_t {
oauth2_log_sink_t *sink;
oauth2_log_t *log;
bool is_child;
} oauth2_apache_cfg_srv_t;
void *oauth2_apache_cfg_srv_create(apr_pool_t *pool, server_rec *s,
oauth2_log_function_t server_log_cb);
void *oauth2_apache_cfg_srv_merge(apr_pool_t *pool, void *b, void *a);
/*
* handlers
*/
#define OAUTH2_APACHE_HANDLERS(foo) \
OAUTH2_APACHE_CHILD_CLEANUP(foo) \
OAUTH2_APACHE_PARENT_CLEANUP(foo) \
OAUTH2_APACHE_POST_CONFIG_IMPL(foo)
/*
* module config
*/
#define OAUTH2_APACHE_COMMANDS(foo) foo##_commands
#define OAUTH2_APACHE_REGISTER_HOOKS(foo) foo##_register_hooks
#define OAUTH2_APACHE_MODULE_DECLARE_EX(foo, dir_create, dir_merge) \
\
void *oauth2_apache_##foo##_cfg_srv_create(apr_pool_t *pool, \
server_rec *s) \
{ \
return oauth2_apache_cfg_srv_create(pool, s, \
foo##_log_server); \
} \
\
module AP_MODULE_DECLARE_DATA foo##_module = { \
STANDARD20_MODULE_STUFF, \
dir_create, \
dir_merge, \
oauth2_apache_##foo##_cfg_srv_create, \
oauth2_apache_cfg_srv_merge, \
OAUTH2_APACHE_COMMANDS(foo), \
OAUTH2_APACHE_REGISTER_HOOKS(foo)};
#define OAUTH2_APACHE_MODULE_DECLARE(foo, type) \
\
OAUTH2_APACHE_DIR_CTX_FUNCS(type) \
\
OAUTH2_APACHE_MODULE_DECLARE_EX(foo, \
OAUTH2_APACHE_DIR_CTX(type, create), \
OAUTH2_APACHE_DIR_CTX(type, merge))
/*
* request context
*/
#define OAUTH2_APACHE_REQUEST_CTX(r, foo) \
oauth2_apache_request_context( \
r, foo##_log_request, \
"oauth2_" OAUTH2_TOSTRING(foo) "_module_user_data_key");
typedef struct oauth2_apache_request_ctx_t {
oauth2_log_t *log;
oauth2_http_request_t *request;
request_rec *r;
} oauth2_apache_request_ctx_t;
oauth2_apache_request_ctx_t *
oauth2_apache_request_context(request_rec *r,
oauth2_log_function_t request_log_cb,
const char *user_data_key);
/*
* misc
*/
bool oauth2_apache_http_request_set(oauth2_log_t *log,
oauth2_http_request_t *request,
request_rec *r);
int oauth2_apache_return_www_authenticate(oauth2_cfg_source_token_t *cfg,
oauth2_apache_request_ctx_t *ctx,
int status_code, const char *error,
const char *error_description);
bool oauth2_apache_request_header_set(oauth2_log_t *log, void *rec,
const char *name, const char *value);
void oauth2_apache_hdr_out_add(oauth2_log_t *log, const request_rec *r,
const char *name, const char *value);
void oauth2_apache_scrub_headers(oauth2_apache_request_ctx_t *ctx,
oauth2_cfg_target_pass_t *target_pass);
bool oauth2_apache_set_request_user(oauth2_cfg_target_pass_t *target_pass,
oauth2_apache_request_ctx_t *ctx,
json_t *json_token);
void oauth2_apache_target_pass(oauth2_apache_request_ctx_t *ctx,
oauth2_cfg_target_pass_t *target_pass,
const char *target_token, json_t *json_token);
bool oauth2_apache_http_response_set(oauth2_log_t *log,
oauth2_http_response_t *response,
request_rec *r);
void oauth2_apache_request_state_set_json(oauth2_apache_request_ctx_t *ctx,
const char *key, json_t *claims);
void oauth2_apache_request_state_get_json(oauth2_apache_request_ctx_t *ctx,
const char *key, json_t **claims);
typedef bool (*oauth2_apache_authz_match_claim_fn_type)(
oauth2_apache_request_ctx_t *, const char *const, const json_t *const);
bool oauth2_apache_authz_match_claim(oauth2_apache_request_ctx_t *ctx,
const char *const attr_spec,
const json_t *const claims);
authz_status
oauth2_apache_authorize(oauth2_apache_request_ctx_t *ctx,
const json_t *const claims, const char *require_args,
oauth2_apache_authz_match_claim_fn_type match_claim_fn);
#endif /* _OAUTH2_APACHE_H_ */
liboauth2-1.4.4/include/oauth2/cache.h 0000664 0000000 0000000 00000004606 14161030125 0017451 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_CACHE_H_
#define _OAUTH2_CACHE_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/log.h"
#include "oauth2/util.h"
typedef struct oauth2_cache_t oauth2_cache_t;
typedef bool (*oauth2_cache_init_function)(oauth2_log_t *log, oauth2_cache_t *,
const oauth2_nv_list_t *options);
typedef bool (*oauth2_cache_post_config_function)(oauth2_log_t *log,
oauth2_cache_t *);
typedef bool (*oauth2_cache_child_init_function)(oauth2_log_t *log,
oauth2_cache_t *);
typedef bool (*oauth2_cache_get_function)(oauth2_log_t *log, oauth2_cache_t *,
const char *key, char **value);
typedef bool (*oauth2_cache_set_function)(oauth2_log_t *log, oauth2_cache_t *,
const char *key, const char *value,
oauth2_time_t expiry);
typedef bool (*oauth2_cache_free_function)(oauth2_log_t *log, oauth2_cache_t *);
typedef struct oauth2_cache_type_t {
const char *name;
bool encrypt_by_default;
oauth2_cache_init_function init;
oauth2_cache_post_config_function post_config;
oauth2_cache_child_init_function child_init;
oauth2_cache_get_function get;
oauth2_cache_set_function set;
oauth2_cache_free_function free;
} oauth2_cache_type_t;
oauth2_cache_t *oauth2_cache_obtain(oauth2_log_t *log, const char *name);
bool oauth2_cache_get(oauth2_log_t *log, oauth2_cache_t *ctx, const char *key,
char **value);
bool oauth2_cache_set(oauth2_log_t *log, oauth2_cache_t *ctx, const char *key,
const char *value, oauth2_time_t ttl_s);
#endif /* _OAUTH2_CACHE_H_ */
liboauth2-1.4.4/include/oauth2/cfg.h 0000664 0000000 0000000 00000021340 14161030125 0017137 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_CFG_H_
#define _OAUTH2_CFG_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include "oauth2/util.h"
#define OAUTH2_CFG_FLAG_UNSET (oauth2_flag_t) - 1
#define OAUTH2_CFG_UINT_UNSET (oauth2_uint_t) - 1
#define OAUTH2_CFG_TIME_UNSET (oauth2_time_t) - 1
/*
* generic
*/
const char *oauth2_cfg_set_flag_slot(void *cfg, size_t offset,
const char *value);
const char *oauth2_cfg_set_uint_slot(void *cfg, size_t offset,
const char *value);
const char *oauth2_cfg_set_time_slot(void *cfg, size_t offset,
const char *value);
const char *oauth2_cfg_set_str_slot(void *cfg, size_t offset,
const char *value);
#define OAUTH2_CFG_TYPE_DECLARE(module, object) \
OAUTH2_TYPE_DECLARE(module, object) \
void oauth2_##module##_##object##_merge( \
oauth2_log_t *, oauth2_##module##_##object##_t *, \
oauth2_##module##_##object##_t *, \
oauth2_##module##_##object##_t *);
const char *oauth2_crypto_passphrase_set(oauth2_log_t *log, void *dummy,
const char *passphrase);
const char *oauth2_crypto_passphrase_get(oauth2_log_t *log);
/*
* cache
*/
char *oauth2_cfg_set_cache(oauth2_log_t *log, void *dummy, const char *type,
const char *options);
/*
* webserver callbacks
*/
typedef bool(oauth2_cfg_env_get_cb)(oauth2_log_t *log, void *ctx,
const char *key, char **value);
typedef bool(oauth2_cfg_env_set_cb)(oauth2_log_t *log, void *ctx,
const char *key, const char *value);
typedef bool(oauth2_cfg_form_post_read_cb)(oauth2_log_t *log, void *ctx,
oauth2_nv_list_t **params);
typedef struct oauth2_cfg_server_callback_funcs_t {
oauth2_cfg_env_get_cb *get;
oauth2_cfg_env_set_cb *set;
oauth2_cfg_form_post_read_cb *form_post;
} oauth2_cfg_server_callback_funcs_t;
/*
* endpoint auth
*/
/*
*
*
* client_secret_basic client_id=&client_secret=
* client_secret_post client_id=&client_secret=
* client_secret_jwt client_id=&client_secret=&aud=
* private_key_jwt jwk=&aud=
* client_cert cert=&key=
* basic username=&password=
*/
typedef enum oauth2_cfg_endpoint_auth_type_t {
OAUTH2_ENDPOINT_AUTH_NONE,
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC,
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST,
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT,
OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT,
OAUTH2_ENDPOINT_AUTH_CLIENT_CERT,
OAUTH2_ENDPOINT_AUTH_BASIC
} oauth2_cfg_endpoint_auth_type_t;
OAUTH2_CFG_TYPE_DECLARE(cfg, endpoint_auth)
char *oauth2_cfg_set_endpoint_auth(oauth2_log_t *log,
oauth2_cfg_endpoint_auth_t *auth,
const char *type,
const oauth2_nv_list_t *params,
const char *prefix);
oauth2_cfg_endpoint_auth_type_t
oauth2_cfg_endpoint_auth_type(const oauth2_cfg_endpoint_auth_t *auth);
/*
* endpoint
*/
OAUTH2_CFG_TYPE_DECLARE(cfg, endpoint)
char *oauth2_cfg_set_endpoint(oauth2_log_t *log, oauth2_cfg_endpoint_t *cfg,
const char *url, const oauth2_nv_list_t *params,
const char *prefix);
const char *oauth2_cfg_endpoint_get_url(const oauth2_cfg_endpoint_t *cfg);
void oauth2_cfg_endpoint_set_url(oauth2_cfg_endpoint_t *cfg, const char *url);
const oauth2_cfg_endpoint_auth_t *
oauth2_cfg_endpoint_get_auth(const oauth2_cfg_endpoint_t *cfg);
oauth2_flag_t
oauth2_cfg_endpoint_get_ssl_verify(const oauth2_cfg_endpoint_t *cfg);
oauth2_uint_t
oauth2_cfg_endpoint_get_http_timeout(const oauth2_cfg_endpoint_t *cfg);
const char *
oauth2_cfg_endpoint_get_outgoing_proxy(const oauth2_cfg_endpoint_t *cfg);
/*
* token verify
*/
typedef enum oauth2_cfg_token_verify_type_t {
OAUTH2_TOKEN_VERIFY_BEARER,
OAUTH2_TOKEN_VERIFY_DPOP,
OAUTH2_TOKEN_VERIFY_MTLS
} oauth2_cfg_token_verify_type_t;
/*
* []
*
* plain|b64|hex [kid=] |
* pem [kid=] |
* pubkey [kid=] |
* jwk |
* jwks_uri
* [type=&refresh=&ssl_verify=] pubkey_uri
* [type=&refresh=&ssl_verify=]
*
* introspect
*
*/
OAUTH2_CFG_TYPE_DECLARE(cfg, token_verify)
char *oauth2_cfg_token_verify_add_options(oauth2_log_t *log,
oauth2_cfg_token_verify_t **verify,
const char *type, const char *value,
const char *options);
/*
* token in request
*/
typedef struct oauth2_cfg_token_in_envvar_t {
char *name;
} oauth2_cfg_token_in_envvar_t;
typedef struct oauth2_cfg_token_in_header_t {
char *name;
char *type;
} oauth2_cfg_token_in_header_t;
typedef struct oauth2_token_in_query_t {
char *param_name;
} oauth2_cfg_token_in_query_t;
typedef struct oauth2_cfg_token_in_post_t {
char *param_name;
} oauth2_cfg_token_in_post_t;
typedef struct oauth2_cfg_token_in_cookie_t {
char *name;
} oauth2_cfg_token_in_cookie_t;
typedef struct oauth2_cfg_token_in_basic_t {
// TODO: what will be the username?
uint8_t dummy;
} oauth2_cfg_token_in_basic_t;
typedef struct oauth2_cfg_token_in_t {
char value;
oauth2_cfg_token_in_envvar_t envvar;
oauth2_cfg_token_in_header_t header;
oauth2_cfg_token_in_query_t query;
oauth2_cfg_token_in_post_t post;
oauth2_cfg_token_in_cookie_t cookie;
oauth2_cfg_token_in_basic_t basic;
} oauth2_cfg_token_in_t;
typedef enum oauth2_cfg_token_in_type_t {
// undefined = 0
OAUTH2_CFG_TOKEN_IN_ENVVAR = 1,
OAUTH2_CFG_TOKEN_IN_HEADER = 2,
OAUTH2_CFG_TOKEN_IN_QUERY = 4,
OAUTH2_CFG_TOKEN_IN_POST = 8,
OAUTH2_CFG_TOKEN_IN_COOKIE = 16,
OAUTH2_CFG_TOKEN_IN_BASIC = 32
} oauth2_cfg_token_in_type_t;
#define OAUTH2_CFG_TOKEN_IN_ENVVAR_STR "environment"
#define OAUTH2_CFG_TOKEN_IN_HEADER_STR "header"
#define OAUTH2_CFG_TOKEN_IN_QUERY_STR "query"
#define OAUTH2_CFG_TOKEN_IN_POST_STR "post"
#define OAUTH2_CFG_TOKEN_IN_COOKIE_STR "cookie"
#define OAUTH2_CFG_TOKEN_IN_BASIC_STR "basic"
char *oauth2_cfg_token_in_set(oauth2_log_t *log, oauth2_cfg_token_in_t *cfg,
const char *method,
const oauth2_nv_list_t *params,
oauth2_uint_t allowed);
/*
* source token
*/
OAUTH2_CFG_TYPE_DECLARE(cfg, source_token)
char *oauth2_cfg_source_token_set_accept_in(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg,
const char *method,
const char *options);
char oauth2_cfg_source_token_get_accept_in(oauth2_cfg_source_token_t *cfg);
oauth2_flag_t oauth2_cfg_source_token_get_strip(oauth2_cfg_source_token_t *cfg);
/*
* target pass
*/
OAUTH2_CFG_TYPE_DECLARE(cfg, target_pass)
char *oauth2_cfg_set_target_pass_options(oauth2_log_t *log,
oauth2_cfg_target_pass_t *cfg,
const char *options);
oauth2_flag_t
oauth2_cfg_target_pass_get_as_headers(oauth2_cfg_target_pass_t *cfg);
oauth2_flag_t
oauth2_cfg_target_pass_get_as_envvars(oauth2_cfg_target_pass_t *cfg);
const char *oauth2_cfg_target_pass_get_prefix(oauth2_cfg_target_pass_t *cfg);
const char *
oauth2_cfg_target_pass_get_authn_header(oauth2_cfg_target_pass_t *cfg);
const char *
oauth2_cfg_target_get_remote_user_claim(oauth2_cfg_target_pass_t *cfg);
/*
* resource owner password credentials
*/
OAUTH2_CFG_TYPE_DECLARE(cfg, ropc)
char *oauth2_cfg_set_ropc(oauth2_log_t *log, oauth2_cfg_ropc_t *cfg,
const char *url, const char *options);
// TODO: just ropc_exec, no member get functions?
const oauth2_cfg_endpoint_t *
oauth2_cfg_ropc_get_token_endpoint(oauth2_cfg_ropc_t *cfg);
const char *oauth2_cfg_ropc_get_client_id(oauth2_cfg_ropc_t *cfg);
const char *oauth2_cfg_ropc_get_username(oauth2_cfg_ropc_t *cfg);
const char *oauth2_cfg_ropc_get_password(oauth2_cfg_ropc_t *cfg);
const oauth2_nv_list_t *
oauth2_cfg_ropc_get_request_parameters(oauth2_cfg_ropc_t *cfg);
#endif /* _OAUTH2_CFG_H_ */
liboauth2-1.4.4/include/oauth2/http.h 0000664 0000000 0000000 00000023374 14161030125 0017370 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_HTTP_H_
#define _OAUTH2_HTTP_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/util.h"
#include
/*
* header names
*/
// TODO: can these be http.c internal with the set and get functions available?
#define OAUTH2_HTTP_HDR_X_FORWARDED_PROTO "X-Forwarded-Proto"
#define OAUTH2_HTTP_HDR_X_FORWARDED_PORT "X-Forwarded-Port"
#define OAUTH2_HTTP_HDR_X_FORWARDED_HOST "X-Forwarded-Host"
#define OAUTH2_HTTP_HDR_HOST "Host"
#define OAUTH2_HTTP_HDR_COOKIE "Cookie"
#define OAUTH2_HTTP_HDR_CONTENT_TYPE "Content-Type"
#define OAUTH2_HTTP_HDR_CONTENT_LENGTH "Content-Length"
#define OAUTH2_HTTP_HDR_AUTHORIZATION "Authorization"
#define OAUTH2_HTTP_HDR_X_REQUESTED_WITH "X-Requested-With"
#define OAUTH2_HTTP_HDR_ACCEPT "Accept"
#define OAUTH2_HTTP_HDR_LOCATION "Location"
#define OAUTH2_HTTP_HDR_SET_COOKIE "Set-Cookie"
#define OAUTH2_HTTP_HDR_BEARER "Bearer"
#define OAUTH2_HTTP_HDR_BASIC "Basic"
#define OAUTH2_HTTP_HDR_REALM "realm"
#define OAUTH2_HTTP_HDR_WWW_AUTHENTICATE "WWW-Authenticate"
#define OAUTH2_HTTP_HDR_XML_HTTP_REQUEST "XMLHttpRequest"
/*
* content type
*/
#define OAUTH2_CONTENT_TYPE_FORM_ENCODED "application/x-www-form-urlencoded"
#define OAUTH2_CONTENT_TYPE_JSON "application/json"
#define OAUTH2_CONTENT_TYPE_TEXT_HTML "text/html"
#define OAUTH2_CONTENT_TYPE_APP_XHTML_XML "application/xhtml+xml"
#define OAUTH2_CONTENT_TYPE_ANY "*/*"
/*
* protocol
*/
#define OAUTH2_HTTP_SCHEME_HTTP "http"
#define OAUTH2_HTTP_SCHEME_HTTPS "https"
typedef enum {
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_GET,
OAUTH2_HTTP_METHOD_PUT,
OAUTH2_HTTP_METHOD_POST,
OAUTH2_HTTP_METHOD_DELETE,
OAUTH2_HTTP_METHOD_CONNECT,
OAUTH2_HTTP_METHOD_OPTIONS
} oauth2_http_method_t;
typedef oauth2_uint_t oauth2_http_status_code_t;
/*
* TODO: make sure the caller calls:
* 1. oauth2_http_request_scheme_set to set the "native" URL scheme on
* which the request was received i.e. without taking into account headers
* 2. oauth2_http_request_hostname_set to set the configured server
* hostname
* 3. oauth2_http_request_port_set to set the "hative" port on which the
* request was received
* 4. oauth2_http_request_path_set for the path that is accessed
* 5. oauth2_http_request_method_set for the HTTP method used
* 6. oauth2_http_request_query_set for the query string
* 7. oauth2_http_request_header_set for each incoming header
*/
OAUTH2_TYPE_DECLARE(http, request)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, scheme, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, hostname, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, path, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, method, oauth2_http_method_t)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, query, char *)
const char *oauth2_http_request_method_get_str(oauth2_log_t *,
oauth2_http_request_t *);
bool oauth2_http_request_context_set(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value);
const char *oauth2_http_request_context_get(
oauth2_log_t *log, const oauth2_http_request_t *request, const char *name);
OAUTH2_TYPE_DECLARE(http, response)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, response, headers, oauth2_nv_list_t *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, response, status_code,
oauth2_http_status_code_t)
bool oauth2_http_response_header_set(oauth2_log_t *log,
oauth2_http_response_t *response,
const char *name, const char *value);
const char *
oauth2_http_response_header_get(oauth2_log_t *log,
const oauth2_http_response_t *response,
const char *name);
const char *oauth2_http_response_header_set_cookie_prefix_get(
oauth2_log_t *log, oauth2_http_response_t *response, const char *prefix);
bool oauth2_http_response_cookie_set(oauth2_log_t *log,
oauth2_http_response_t *response,
const char *name, const char *value,
const char *path, const bool is_secure,
oauth2_time_t max_age);
void oauth2_http_response_headers_loop(oauth2_log_t *log,
const oauth2_http_response_t *response,
oauth2_nv_list_loop_cb_t *callback,
void *rec);
// typedef bool (*oauth2_http_read_post_callback_t)(oauth2_log_t *log,
// oauth2_http_request_t *request, char **data);
bool oauth2_http_request_port_set(oauth2_log_t *log, oauth2_http_request_t *r,
unsigned long port);
char *oauth2_http_request_port_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
/*
* currently accessed url functions
*/
char *oauth2_http_request_url_base_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
char *oauth2_http_request_url_path_get(oauth2_log_t *log,
const oauth2_http_request_t *request);
char *oauth2_http_request_url_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
/*
* request header functions
*/
OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, request, header)
void oauth2_http_request_headers_loop(oauth2_log_t *log,
oauth2_http_request_t *request,
oauth2_nv_list_loop_cb_t *callback,
void *rec);
const char *
oauth2_http_request_header_content_type_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
const char *
oauth2_http_request_header_cookie_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
const char *
oauth2_http_request_header_content_length_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
bool oauth2_http_request_header_content_length_set(oauth2_log_t *log,
oauth2_http_request_t *r,
size_t len);
const char *
oauth2_http_request_header_x_requested_with_get(oauth2_log_t *log,
const oauth2_http_request_t *r);
const char *
oauth2_http_request_header_accept_get(oauth2_log_t *log,
const oauth2_http_request_t *request);
bool oauth2_http_request_is_xml_http_request(
oauth2_log_t *log, const oauth2_http_request_t *request);
bool oauth2_http_request_is_secure(oauth2_log_t *log,
const oauth2_http_request_t *request);
/*
* request args functions
*/
char *oauth2_http_url_query_encode(oauth2_log_t *log, const char *url,
const oauth2_nv_list_t *args);
char *oauth2_http_url_form_encode(oauth2_log_t *log,
const oauth2_nv_list_t *args);
bool oauth2_http_request_query_param_add(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value);
const char *oauth2_http_request_query_param_get(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name);
bool oauth2_http_request_query_param_unset(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name);
/*
* http call context object
*/
OAUTH2_TYPE_DECLARE(http, call_ctx)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, bearer_token, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, content_type, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, outgoing_proxy, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ca_info, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_cert, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_key, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, timeout, int)
OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_verify, bool)
OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, call_ctx, cookie)
OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, call_ctx, hdr)
bool oauth2_http_call_ctx_basic_auth_set(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx,
const char *username,
const char *password, bool url_encode);
/*
* http call functions
*/
bool oauth2_http_call(oauth2_log_t *log, const char *url, const char *data,
oauth2_http_call_ctx_t *ctx, char **response,
oauth2_http_status_code_t *status_code);
bool oauth2_http_get(oauth2_log_t *log, const char *url,
const oauth2_nv_list_t *params,
oauth2_http_call_ctx_t *ctx, char **response,
oauth2_http_status_code_t *status_code);
bool oauth2_http_post_form(oauth2_log_t *log, const char *url,
const oauth2_nv_list_t *params,
oauth2_http_call_ctx_t *ctx, char **response,
oauth2_http_status_code_t *status_code);
bool oauth2_http_post_json(oauth2_log_t *log, const char *url,
const json_t *json, oauth2_http_call_ctx_t *ctx,
char **response,
oauth2_http_status_code_t *status_code);
/*
* http cookie functions
*/
char *oauth2_http_request_cookie_get(oauth2_log_t *log,
oauth2_http_request_t *r, const char *name,
bool strip);
bool oauth2_http_request_cookie_set(oauth2_log_t *log, oauth2_http_request_t *r,
const char *name, const char *value);
/*
* http auth
*/
bool oauth2_http_auth_client_cert(oauth2_log_t *log, const char *ssl_cert,
const char *ssl_key,
oauth2_http_call_ctx_t *ctx);
bool oauth2_http_auth_basic(oauth2_log_t *log, const char *username,
const char *passwd, oauth2_http_call_ctx_t *ctx);
#endif /* _OAUTH2_HTTP_H_ */
liboauth2-1.4.4/include/oauth2/ipc.h 0000664 0000000 0000000 00000004132 14161030125 0017153 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_IPC_H_
#define _OAUTH2_IPC_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include "oauth2/util.h"
// TODO: macro for post_config and child_init functions
OAUTH2_TYPE_DECLARE(ipc, mutex)
bool oauth2_ipc_mutex_post_config(oauth2_log_t *log, oauth2_ipc_mutex_t *m);
bool oauth2_ipc_mutex_lock(oauth2_log_t *log, oauth2_ipc_mutex_t *m);
bool oauth2_ipc_mutex_unlock(oauth2_log_t *log, oauth2_ipc_mutex_t *m);
OAUTH2_TYPE_DECLARE(ipc, sema)
bool oauth2_ipc_sema_post_config(oauth2_log_t *log, oauth2_ipc_sema_t *sema);
bool oauth2_ipc_sema_post(oauth2_log_t *log, oauth2_ipc_sema_t *sema);
bool oauth2_ipc_sema_wait(oauth2_log_t *log, oauth2_ipc_sema_t *sema);
bool oauth2_ipc_sema_trywait(oauth2_log_t *log, oauth2_ipc_sema_t *sema);
typedef struct oauth2_ipc_shm_t oauth2_ipc_shm_t;
oauth2_ipc_shm_t *oauth2_ipc_shm_init(oauth2_log_t *log, size_t size);
void oauth2_ipc_shm_free(oauth2_log_t *, oauth2_ipc_shm_t *);
bool oauth2_ipc_shm_post_config(oauth2_log_t *log, oauth2_ipc_shm_t *shm);
bool oauth2_ipc_shm_child_init(oauth2_log_t *log, oauth2_ipc_shm_t *shm);
void *oauth2_ipc_shm_get(oauth2_log_t *log, oauth2_ipc_shm_t *s);
#endif /* _OAUTH2_IPC_H_ */
liboauth2-1.4.4/include/oauth2/jose.h 0000664 0000000 0000000 00000005676 14161030125 0017356 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_JOSE_H_
#define _OAUTH2_JOSE_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include "oauth2/cfg.h"
#include "oauth2/log.h"
#include "oauth2/util.h"
#define OAUTH2_JOSE_OPENSSL_ALG_SHA1 "sha1"
#define OAUTH2_JOSE_OPENSSL_ALG_SHA256 "sha256"
#define OAUTH2_JOSE_HDR_TYP "typ"
#define OAUTH2_JOSE_HDR_TYP_JWT "JWT"
#define OAUTH2_JOSE_JWT_ISS "iss"
#define OAUTH2_JOSE_JWT_IAT "iat"
#define OAUTH2_JOSE_JWT_EXP "exp"
#define OAUTH2_JOSE_JWT_SUB "sub"
#define OAUTH2_JOSE_JWT_AUD "aud"
typedef struct oauth2_jose_jwk_t oauth2_jose_jwk_t;
void oauth2_jose_jwk_release(oauth2_jose_jwk_t *jwk);
typedef struct oauth2_jose_jwk_list_t oauth2_jose_jwk_list_t;
void oauth2_jose_jwk_list_free(oauth2_log_t *log, oauth2_jose_jwk_list_t *keys);
bool oauth2_jose_hash_bytes(oauth2_log_t *log, const char *digest,
const unsigned char *src, unsigned int src_len,
unsigned char **dst, unsigned int *dst_len);
bool oauth2_jose_hash2s(oauth2_log_t *log, const char *digest, const char *src,
char **dst);
bool oauth2_jose_jwk_create_symmetric(oauth2_log_t *log,
const char *client_secret,
const char *hash_algo,
oauth2_jose_jwk_t **jwk);
bool oauth2_jose_encrypt(oauth2_log_t *log, const char *secret,
const char *s_sig_payload, char **cser);
bool oauth2_jose_jwt_encrypt(oauth2_log_t *log, const char *secret,
json_t *payload, char **cser);
bool oauth2_jose_decrypt(oauth2_log_t *log, const char *secret,
const char *cser, char **result);
bool oauth2_jose_jwt_decrypt(oauth2_log_t *log, const char *secret,
const char *cser, json_t **result);
typedef struct oauth2_jose_jwt_verify_ctx_t oauth2_jose_jwt_verify_ctx_t;
bool oauth2_jose_jwt_verify(oauth2_log_t *log,
oauth2_jose_jwt_verify_ctx_t *jwt_verify_ctx,
const char *token, json_t **json_payload,
char **s_payload);
bool oauth2_jose_jwk_thumbprint(oauth2_log_t *log, const cjose_jwk_t *jwk,
unsigned char **hash_bytes,
unsigned int *hash_bytes_len);
#endif /* _OAUTH2_JOSE_H_ */
liboauth2-1.4.4/include/oauth2/log.h 0000664 0000000 0000000 00000010650 14161030125 0017163 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_LOG_H_
#define _OAUTH2_LOG_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
// don't change this without checking consequences in log.c...
typedef enum oauth2_log_level_t {
OAUTH2_LOG_ERROR,
OAUTH2_LOG_WARN,
OAUTH2_LOG_NOTICE,
OAUTH2_LOG_INFO,
OAUTH2_LOG_DEBUG,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE2
} oauth2_log_level_t;
// TODO: instead of sinks, just define oauth2_err_t and use that to return
// errors...?
// we must use #define to make the __ macro's work...
// we must use the target defined log levels...
// so #define all of oauth2_error, oauth2_warn, oauth2_debug, oauth2_trace1?
// must also keep oauth2_error etc. to printout logs in the test phase (although
// could do that with oauth2_err_t)
// probably good to print debug traces inside protocol functions!
// #define _oauth2_log
// ap_log_rerror(APLOG_MARK, level, 0, r,"%s: %s", __FUNCTION__,
// apr_psprintf(r->pool, fmt, ##__VA_ARGS__))
// and:
// #define_oauth2_log
// ap_log_error(APLOG_MARK, level, 0, s, "%s: %s", __FUNCTION__,
// apr_psprintf(s->process->pool, fmt, ##__VA_ARGS__))
// can also #define _oauth2_log to a no-op for speed or other reasons
#ifndef _oauth2_log
#define _oauth2_log(log, level, fmt, ...) \
oauth2_log(log, __FILE__, __LINE__, __FUNCTION__, level, fmt, \
##__VA_ARGS__)
#endif
#define oauth2_error(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_ERROR, fmt, ##__VA_ARGS__)
#define oauth2_warn(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_WARN, fmt, ##__VA_ARGS__)
#define oauth2_notice(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_NOTICE, fmt, ##__VA_ARGS__)
#define oauth2_info(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_INFO, fmt, ##__VA_ARGS__)
#define oauth2_debug(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_DEBUG, fmt, ##__VA_ARGS__)
#define oauth2_trace1(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_TRACE1, fmt, ##__VA_ARGS__)
#define oauth2_trace2(log, fmt, ...) \
_oauth2_log(log, OAUTH2_LOG_TRACE2, fmt, ##__VA_ARGS__)
/*
* log context definitions
*/
typedef struct oauth2_log_t oauth2_log_t;
/*
* log sink types
*/
typedef struct oauth2_log_sink_t oauth2_log_sink_t;
typedef void (*oauth2_log_function_t)(oauth2_log_sink_t *sink,
const char *filename, unsigned long line,
const char *function,
oauth2_log_level_t level,
const char *msg);
/*
* API
*/
extern oauth2_log_sink_t oauth2_log_sink_stderr;
extern oauth2_log_sink_t oauth2_log_sink_stdout;
void oauth2_log(oauth2_log_t *log, const char *filename, unsigned long line,
const char *function, oauth2_log_level_t level, const char *fmt,
...);
oauth2_log_sink_t *oauth2_log_sink_create(oauth2_log_level_t level,
oauth2_log_function_t callback,
void *ctx);
void *oauth2_log_sink_ctx_get(oauth2_log_sink_t *sink);
oauth2_log_function_t oauth2_log_sink_callback_get(oauth2_log_sink_t *sink);
void oauth2_log_sink_add(oauth2_log_t *log, oauth2_log_sink_t *add);
void oauth2_log_sink_level_set(oauth2_log_sink_t *sink,
oauth2_log_level_t level);
/*
* internals
*/
oauth2_log_t *oauth2_log_init(oauth2_log_level_t level,
oauth2_log_sink_t *sink);
void oauth2_log_free(oauth2_log_t *);
#endif /* _OAUTH2_LOG_H_ */
liboauth2-1.4.4/include/oauth2/mem.h 0000664 0000000 0000000 00000004036 14161030125 0017161 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_MEM_H_
#define _OAUTH2_MEM_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
typedef void *(*oauth2_mem_alloc_fn_t)(size_t);
typedef void *(*oauth2_mem_realloc_fn_t)(void *, size_t);
typedef void (*oauth2_mem_dealloc_fn_t)(void *);
oauth2_mem_alloc_fn_t oauth2_mem_get_alloc();
oauth2_mem_realloc_fn_t oauth2_mem_get_realloc();
oauth2_mem_dealloc_fn_t oauth2_mem_get_dealloc();
void oauth2_mem_set_alloc_funcs(oauth2_mem_alloc_fn_t alloc,
oauth2_mem_realloc_fn_t realloc,
oauth2_mem_dealloc_fn_t dealloc);
typedef void *(*oauth2_mem_alloc3_fn_t)(size_t, const char *, int);
typedef void *(*oauth2_mem_realloc3_fn_t)(void *, size_t, const char *, int);
typedef void (*oauth2_mem_dealloc3_fn_t)(void *, const char *, int);
oauth2_mem_alloc3_fn_t oauth2_mem_get_alloc3();
oauth2_mem_realloc3_fn_t oauth2_mem_get_realloc3();
oauth2_mem_dealloc3_fn_t oauth2_mem_get_dealloc3();
void oauth2_mem_set_alloc_ex_funcs(oauth2_mem_alloc3_fn_t alloc3,
oauth2_mem_realloc3_fn_t realloc3,
oauth2_mem_dealloc3_fn_t dealloc3);
void *oauth2_mem_alloc(size_t);
void oauth2_mem_free(void *);
#endif /* _OAUTH2_MEM_H_ */
liboauth2-1.4.4/include/oauth2/nginx.h 0000664 0000000 0000000 00000014702 14161030125 0017527 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_NGINX_H_
#define _OAUTH2_NGINX_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
// module
#define OAUTH2_NGINX_MODULE(module) \
extern ngx_module_t ngx_##module##_module; \
\
ngx_module_t *ngx_modules[] = {&ngx_##module##_module, NULL}; \
\
char *ngx_module_names[] = {OAUTH2_TOSTRING(ngx_##module##_module), \
NULL}; \
\
char *ngx_module_order[] = {NULL};
// functions
#define OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \
static char *ngx_##module##_set_##primitive( \
ngx_conf_t *cf, ngx_command_t *cmd, void *conf) \
{ \
const char *rv = NULL; \
type *cfg = (type *)conf; \
ngx_str_t *value = cf->args->elts;
// fprintf(stderr, " ## %s: %p (log=%p)\n", __FUNCTION__, cfg, cf->log);
#define OAUTH2_NGINX_CFG_FUNC_END(cf, rv) \
if (rv) \
ngx_log_error(NGX_LOG_ERR, cf->log, 0, rv); \
return rv ? NGX_CONF_ERROR : NGX_CONF_OK; \
}
#define OAUTH2_NGINX_CFG_FUNC_ARGS1(module, type, primitive, func, member) \
OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \
char *v1 = cf->args->nelts > 1 \
? oauth2_strndup((const char *)value[1].data, \
(size_t)value[1].len) \
: NULL; \
rv = func(cfg->log, member, v1); \
oauth2_mem_free(v1); \
OAUTH2_NGINX_CFG_FUNC_END(cf, rv)
#define OAUTH2_NGINX_CFG_FUNC_ARGS2(module, type, primitive, func, member) \
OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \
char *v1 = cf->args->nelts > 1 \
? oauth2_strndup((const char *)value[1].data, \
(size_t)value[1].len) \
: NULL; \
char *v2 = cf->args->nelts > 2 \
? oauth2_strndup((const char *)value[2].data, \
(size_t)value[2].len) \
: NULL; \
rv = func(cfg->log, member, v1, v2); \
oauth2_mem_free(v2); \
oauth2_mem_free(v1); \
OAUTH2_NGINX_CFG_FUNC_END(cf, rv)
#define OAUTH2_NGINX_CFG_FUNC_ARGS3(module, type, primitive, func, member) \
OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \
char *v1 = cf->args->nelts > 1 \
? oauth2_strndup((const char *)value[1].data, \
(size_t)value[1].len) \
: NULL; \
char *v2 = cf->args->nelts > 2 \
? oauth2_strndup((const char *)value[2].data, \
(size_t)value[2].len) \
: NULL; \
char *v3 = cf->args->nelts > 3 \
? oauth2_strndup((const char *)value[3].data, \
(size_t)value[3].len) \
: NULL; \
rv = func(cfg->log, member, v1, v2, v3); \
oauth2_mem_free(v3); \
oauth2_mem_free(v2); \
oauth2_mem_free(v1); \
OAUTH2_NGINX_CFG_FUNC_END(cf, rv)
// commands
#define OAUTH2_NGINX_CMD(take, module, directive, primitive) \
{ \
ngx_string(directive), \
NGX_HTTP_LOC_CONF | NGX_CONF_TAKE##take, \
ngx_##module##_set_##primitive, NGX_HTTP_LOC_CONF_OFFSET, \
0, NULL \
}
// logging
void oauth2_nginx_log(oauth2_log_sink_t *sink, const char *filename,
unsigned long line, const char *function,
oauth2_log_level_t level, const char *msg);
// requests
typedef struct oauth2_nginx_request_context_t {
oauth2_log_t *log;
ngx_http_request_t *r;
oauth2_http_request_t *request;
} oauth2_nginx_request_context_t;
oauth2_nginx_request_context_t *
oauth2_nginx_request_context_init(ngx_http_request_t *r);
void oauth2_nginx_request_context_free(void *rec);
ngx_int_t oauth2_nginx_http_response_set(oauth2_log_t *log,
oauth2_http_response_t *response,
ngx_http_request_t *r);
#endif /* _OAUTH2_NGINX_H_ */
liboauth2-1.4.4/include/oauth2/oauth2.h 0000664 0000000 0000000 00000005416 14161030125 0017610 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_H_
#define _OAUTH2_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include "oauth2/cfg.h"
#include "oauth2/http.h"
#include "oauth2/log.h"
#include "oauth2/util.h"
#define OAUTH2_GRANT_TYPE "grant_type"
#define OAUTH2_ACCESS_TOKEN "access_token"
#define OAUTH2_CODE "code"
#define OAUTH2_SCOPE "scope"
#define OAUTH2_NONCE "nonce"
#define OAUTH2_STATE "state"
#define OAUTH2_RESPONSE_TYPE "response_type"
#define OAUTH2_REDIRECT_URI "redirect_uri"
#define OAUTH2_GRANT_TYPE "grant_type"
#define OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE "authorization_code"
#define OAUTH2_CODE_CHALLENGE "code_challenge"
#define OAUTH2_CODE_CHALLENGE_METHOD "code_challenge_method"
#define OAUTH2_CODE_VERIFIER "code_verifier"
#define OAUTH2_RESPONSE_TYPE_CODE "code"
#define OAUTH2_CLIENT_ID "client_id"
#define OAUTH2_CLIENT_SECRET "client_secret"
#define OAUTH2_ERROR "error"
#define OAUTH2_ERROR_DESCRIPTION "error_description"
#define OAUTH2_ERROR_INVALID_TOKEN "invalid_token"
#define OAUTH2_ERROR_INVALID_REQUEST "invalid_request"
#define OAUTH2_ERROR_INSUFFICIENT_SCOPE "insufficient_scope"
#define OAUTH2_CLAIM_ISS "iss"
#define OAUTH2_CLAIM_SUB "sub"
#define OAUTH2_CLAIM_JTI "jti"
#define OAUTH2_CLAIM_EXP "exp"
#define OAUTH2_CLAIM_AUD "aud"
#define OAUTH2_CLAIM_IAT "iat"
#define OAUTH2_TLS_CERT_VAR_NAME "SSL_CLIENT_CERT"
typedef enum {
OAUTH2_UNAUTH_ACTION_UNDEFINED,
OAUTH2_UNAUTH_ACTION_AUTHENTICATE,
OAUTH2_UNAUTH_ACTION_PASS,
OAUTH2_UNAUTH_ACTION_HTTP_401,
OAUTH2_UNAUTH_ACTION_HTTP_410
} oauth2_unauth_action_t;
bool oauth2_http_ctx_auth_add(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params);
bool oauth2_token_verify(oauth2_log_t *log, oauth2_http_request_t *request,
oauth2_cfg_token_verify_t *verify, const char *token,
json_t **json_payload);
#endif /* _OAUTH2_H_ */
liboauth2-1.4.4/include/oauth2/openidc.h 0000664 0000000 0000000 00000012504 14161030125 0020023 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_OPENIDC_H_
#define _OAUTH2_OPENIDC_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/http.h"
#include "oauth2/oauth2.h"
#include "oauth2/util.h"
#define OAUTH2_OPENIDC_ID_TOKEN "id_token"
#define OAUTH2_OPENIDC_ACCESS_TOKEN "access_token"
#define OAUTH2_CLAIM_ISS "iss"
OAUTH2_CFG_TYPE_DECLARE(cfg, session)
OAUTH2_CFG_TYPE_DECLARE(cfg, openidc_provider_resolver)
/*
* location-based OpenID Connect configuration
*/
OAUTH2_CFG_TYPE_DECLARE(cfg, openidc)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, handler_path, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET(cfg, openidc, redirect_uri, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, unauth_action,
oauth2_unauth_action_t)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, session,
oauth2_cfg_session_t *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, state_cookie_name_prefix,
char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, state_cookie_timeout,
oauth2_time_t)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, state_cookie_max,
oauth2_uint_t)
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, openidc, state_cookie_delete_oldest,
oauth2_flag_t)
char *oauth2_cfg_openidc_redirect_uri_get(oauth2_log_t *,
const oauth2_cfg_openidc_t *,
const oauth2_http_request_t *);
/*
* protocol state
*/
OAUTH2_TYPE_DECLARE(openidc, proto_state)
oauth2_openidc_proto_state_t *
oauth2_openidc_proto_state_init(oauth2_log_t *log);
oauth2_openidc_proto_state_t *
oauth2_openidc_proto_state_clone(oauth2_log_t *log,
const oauth2_openidc_proto_state_t *src);
void oauth2_openidc_proto_state_free(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p);
bool oauth2_openidc_proto_state_set(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
const char *name, const char *value);
bool oauth2_openidc_proto_state_set_int(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
const char *name,
const json_int_t value);
json_t *
oauth2_openidc_proto_state_json_get(const oauth2_openidc_proto_state_t *p);
/*
* OpenID Connect provider configuration
*/
OAUTH2_TYPE_DECLARE(openidc, provider)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, issuer, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, authorization_endpoint,
char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, token_endpoint, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, userinfo_endpoint, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, jwks_uri, char *)
bool oauth2_cfg_openidc_provider_resolver_set(
oauth2_log_t *log, oauth2_cfg_openidc_t *cfg,
oauth2_cfg_openidc_provider_resolver_t *resolver);
oauth2_cfg_openidc_provider_resolver_t *
oauth2_cfg_openidc_provider_resolver_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg);
char *oauth2_cfg_openidc_provider_resolver_set_options(
oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, const char *type,
const char *value, const char *options);
/*
* OpenID Connect client configuration
*/
OAUTH2_TYPE_DECLARE(openidc, client)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, scope, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, client_id, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, client_secret, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, token_endpoint_auth,
oauth2_cfg_endpoint_auth_t *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, ssl_verify, oauth2_flag_t)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_timeout, oauth2_uint_t)
char *oauth2_openidc_client_set_options(oauth2_log_t *log,
oauth2_cfg_openidc_t *cfg,
const char *type, const char *value,
const char *options);
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, openidc, client, oauth2_openidc_client_t *)
/*
* OpenID Connect configuration
*/
char *oauth2_cfg_openidc_set_options(oauth2_log_t *log,
oauth2_cfg_openidc_t *cfg,
const char *options);
char *oauth2_cfg_openidc_redirect_uri_get_iss(
oauth2_log_t *, const oauth2_cfg_openidc_t *, const oauth2_http_request_t *,
const oauth2_openidc_provider_t *);
bool oauth2_openidc_is_request_to_redirect_uri(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request);
bool oauth2_openidc_handle(oauth2_log_t *log, const oauth2_cfg_openidc_t *c,
oauth2_http_request_t *r,
oauth2_http_response_t **response, json_t **claims);
#endif /* _OAUTH2_OPENIDC_H_ */
liboauth2-1.4.4/include/oauth2/proto.h 0000664 0000000 0000000 00000002667 14161030125 0017556 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_PROTO_H_
#define _OAUTH2_PROTO_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/http.h"
char *oauth2_get_source_token(oauth2_log_t *log, oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request,
oauth2_cfg_server_callback_funcs_t *srv_cb,
void *srv_cb_ctx);
bool oauth2_ropc_exec(oauth2_log_t *log, oauth2_cfg_ropc_t *cfg,
const char *username, const char *password, char **rtoken,
oauth2_uint_t *status_code);
#endif /* _OAUTH2_PROTO_H_ */
liboauth2-1.4.4/include/oauth2/session.h 0000664 0000000 0000000 00000007140 14161030125 0020065 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_SESSION_H_
#define _OAUTH2_SESSION_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cache.h"
#include "oauth2/openidc.h"
#include "oauth2/util.h"
OAUTH2_CFG_TYPE_DECLARE(cfg, session)
void oauth2_cfg_session_release(oauth2_log_t *log,
oauth2_cfg_session_t *session);
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, cookie_name, char *)
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, cookie_path, char *)
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, inactivity_timeout_s,
oauth2_time_t)
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, max_duration_s, oauth2_time_t)
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, cache, oauth2_cache_t *)
typedef bool(oauth2_session_load_callback_t)(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
oauth2_http_request_t *request,
json_t **json);
typedef bool(oauth2_session_save_callback_t)(
oauth2_log_t *log, const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request, oauth2_http_response_t *response,
json_t *json);
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, load_callback,
oauth2_session_load_callback_t *)
OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, save_callback,
oauth2_session_save_callback_t *)
char *oauth2_cfg_session_set_options(oauth2_log_t *log,
oauth2_cfg_session_t *cfg,
const char *type, const char *options);
OAUTH2_TYPE_DECLARE(session, rec);
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, user, char *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, id_token, char *)
OAUTH2_TYPE_DECLARE_MEMBER_GET(session, rec, id_token_claims, json_t *)
OAUTH2_TYPE_DECLARE_MEMBER_GET(session, rec, userinfo_claims, json_t *)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, start, oauth2_time_t)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, expiry, oauth2_time_t)
bool oauth2_session_rec_id_token_claims_set(oauth2_log_t *log,
oauth2_session_rec_t *session,
json_t *id_token);
bool oauth2_session_rec_userinfo_claims_set(oauth2_log_t *log,
oauth2_session_rec_t *session,
json_t *userinfo_claims);
bool oauth2_session_load(oauth2_log_t *log, const oauth2_cfg_session_t *c,
oauth2_http_request_t *r,
oauth2_session_rec_t **session);
bool oauth2_session_save(oauth2_log_t *log, const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response,
oauth2_session_rec_t *session);
void oauth2_session_rec_free(oauth2_log_t *log, oauth2_session_rec_t *s);
bool oauth2_session_handle(oauth2_log_t *log, const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response,
oauth2_session_rec_t *session);
#endif /* _OAUTH2_SESSION_H_ */
liboauth2-1.4.4/include/oauth2/util.h 0000664 0000000 0000000 00000020315 14161030125 0017356 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_UTIL_H
#define _OAUTH2_UTIL_H
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include "oauth2/log.h"
#include
#define OAUTH2_STRINGIFY(x) #x
#define OAUTH2_TOSTRING(x) OAUTH2_STRINGIFY(x)
typedef char oauth2_flag_t;
typedef unsigned int oauth2_uint_t;
typedef uint64_t oauth2_time_t;
#define OAUTH2_UINT_FORMAT "%u"
#define OAUTH2_TIME_T_FORMAT "%lu"
#define OAUTH2_MSEC_PER_SEC 1000
#define OAUTH2_USEC_PER_MSEC 1000
#define OAUTH2_TYPE_DECLARE(module, object) \
typedef struct oauth2_##module##_##object##_t \
oauth2_##module##_##object##_t; \
oauth2_##module##_##object##_t *oauth2_##module##_##object##_init( \
oauth2_log_t *); \
oauth2_##module##_##object##_t *oauth2_##module##_##object##_clone( \
oauth2_log_t *, const oauth2_##module##_##object##_t *); \
void oauth2_##module##_##object##_free( \
oauth2_log_t *, oauth2_##module##_##object##_t *);
#define OAUTH2_TYPE_DECLARE_MEMBER_SET(module, object, member, type) \
bool oauth2_##module##_##object##_##member##_set( \
oauth2_log_t *, oauth2_##module##_##object##_t *, const type);
#define OAUTH2_TYPE_DECLARE_MEMBER_GET(module, object, member, type) \
type oauth2_##module##_##object##_##member##_get( \
oauth2_log_t *, const oauth2_##module##_##object##_t *);
#define OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(module, object, member, type) \
OAUTH2_TYPE_DECLARE_MEMBER_SET(module, object, member, type) \
OAUTH2_TYPE_DECLARE_MEMBER_GET(module, object, member, type)
#define OAUTH2_MEMBER_LIST_DECLARE_SET(module, object, member) \
bool oauth2_##module##_##object##_##member##_set( \
oauth2_log_t *, oauth2_##module##_##object##_t *, const char *, \
const char *);
#define OAUTH2_MEMBER_LIST_DECLARE_UNSET(module, object, member) \
bool oauth2_##module##_##object##_##member##_unset( \
oauth2_log_t *, oauth2_##module##_##object##_t *, const char *);
#define OAUTH2_MEMBER_LIST_DECLARE_ADD(module, object, member) \
bool oauth2_##module##_##object##_##member##_add( \
oauth2_log_t *, oauth2_##module##_##object##_t *, const char *, \
const char *);
#define OAUTH2_MEMBER_LIST_DECLARE_GET(module, object, member) \
const char *oauth2_##module##_##object##_##member##_get( \
oauth2_log_t *, const oauth2_##module##_##object##_t *, \
const char *);
#define OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(module, object, member) \
OAUTH2_MEMBER_LIST_DECLARE_SET(module, object, member) \
OAUTH2_MEMBER_LIST_DECLARE_UNSET(module, object, member) \
OAUTH2_MEMBER_LIST_DECLARE_ADD(module, object, member) \
OAUTH2_MEMBER_LIST_DECLARE_GET(module, object, member)
#define OAUTH2_LIST_DECLARE_SET(module, type) \
bool oauth2_##module##_##type##_set(oauth2_log_t *, \
oauth2_##module##_##type##_t *, \
const char *, const char *);
#define OAUTH2_LIST_DECLARE_UNSET(module, type) \
bool oauth2_##module##_##type##_unset( \
oauth2_log_t *, oauth2_##module##_##type##_t *, const char *);
#define OAUTH2_LIST_DECLARE_ADD(module, type) \
bool oauth2_##module##_##type##_add(oauth2_log_t *, \
oauth2_##module##_##type##_t *, \
const char *, const char *);
#define OAUTH2_LIST_DECLARE_GET(module, type) \
const char *oauth2_##module##_##type##_get( \
oauth2_log_t *, const oauth2_##module##_##type##_t *, \
const char *);
#define OAUTH2_LIST_DECLARE_SET_UNSET_ADD_GET(module, type) \
OAUTH2_LIST_DECLARE_SET(module, type) \
OAUTH2_LIST_DECLARE_UNSET(module, type) \
OAUTH2_LIST_DECLARE_ADD(module, type) \
OAUTH2_LIST_DECLARE_GET(module, type)
OAUTH2_TYPE_DECLARE(nv, list)
OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(nv, list, case_sensitive, bool)
OAUTH2_LIST_DECLARE_SET_UNSET_ADD_GET(nv, list)
typedef bool(oauth2_nv_list_loop_cb_t)(oauth2_log_t *log, void *rec,
const char *key, const char *value);
void oauth2_nv_list_loop(oauth2_log_t *log, const oauth2_nv_list_t *list,
oauth2_nv_list_loop_cb_t *callback, void *rec);
char *oauth2_nv_list2s(oauth2_log_t *log, const oauth2_nv_list_t *list);
void oauth2_nv_list_merge_into(oauth2_log_t *log,
const oauth2_nv_list_t *source,
oauth2_nv_list_t *target);
oauth2_log_t *oauth2_init(oauth2_log_level_t level, oauth2_log_sink_t *sink);
void oauth2_shutdown(oauth2_log_t *);
int oauth2_snprintf(char *dst, size_t len, const char *fmt, ...);
char *oauth2_strdup(const char *src);
char *oauth2_strndup(const char *src, size_t len);
char *oauth2_stradd(char *src, const char *add1, const char *add2,
const char *add3);
char *oauth2_getword(const char **line, char stop);
size_t oauth2_base64url_encode(oauth2_log_t *log, const uint8_t *src,
const size_t src_len, char **dst);
bool oauth2_base64url_decode(oauth2_log_t *log, const char *src, uint8_t **dst,
size_t *dst_len);
size_t oauth2_base64_encode(oauth2_log_t *log, const uint8_t *src,
const size_t src_len, char **dst);
bool oauth2_base64_decode(oauth2_log_t *log, const char *src, uint8_t **dst,
size_t *dst_len);
char *oauth2_url_encode(oauth2_log_t *log, const char *str);
char *oauth2_url_decode(oauth2_log_t *log, const char *str);
char *oauth2_html_escape(oauth2_log_t *log, const char *src);
bool oauth2_parse_form_encoded_params(oauth2_log_t *log, const char *data,
oauth2_nv_list_t **params);
bool oauth2_json_decode_check_error(oauth2_log_t *log, const char *str,
json_t **json);
bool oauth2_json_decode_object(oauth2_log_t *log, const char *payload,
json_t **json);
bool oauth2_json_object_get(oauth2_log_t *log, const json_t *json,
const char *name, json_t **value);
bool oauth2_json_string_get(oauth2_log_t *log, const json_t *json,
const char *name, char **value,
const char *default_value);
bool oauth2_json_number_get(oauth2_log_t *log, const json_t *json,
const char *name, json_int_t *number,
const json_int_t default_value);
char *oauth2_rand_str(oauth2_log_t *log, size_t len);
oauth2_time_t oauth2_time_now_sec();
oauth2_time_t oauth2_parse_time_sec(oauth2_log_t *log, const char *seconds,
oauth2_time_t default_value);
bool oauth2_parse_bool(oauth2_log_t *log, const char *value,
bool default_value);
oauth2_uint_t oauth2_parse_uint(oauth2_log_t *log, const char *int_value,
oauth2_uint_t default_value);
int oauth2_strnenvcmp(const char *a, const char *b, int len);
char *oauth2_json_encode(oauth2_log_t *log, json_t *json, size_t flags);
char *oauth2_normalize_header_name(const char *str);
char *oauth_read_file(oauth2_log_t *log, const char *filename);
#endif /* _OAUTH2_UTIL_H */
liboauth2-1.4.4/include/oauth2/version.h.in 0000664 0000000 0000000 00000002424 14161030125 0020474 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_VERSION_H_
#define _OAUTH2_VERSION_H_
/* include/oauth2/version.h Generated from version.h.in by autoheader. */
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#define OAUTH2_PACKAGE_NAME "@PACKAGE_NAME@"
#define OAUTH2_PACKAGE_VERSION "@PACKAGE_VERSION@"
const char *oauth2_version();
const char *oauth2_package_string();
#endif // _OAUTH2_VERSION_H_
liboauth2-1.4.4/liboauth2.pc.in 0000664 0000000 0000000 00000000633 14161030125 0016226 0 ustar 00root root 0000000 0000000 #
# liboauth2 - OAuth 2.0 / OpenID Connect implementation for C
#
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: liboauth2
URL: https://github.com/zmartzone/liboauth2
Description: OAuth 2.0 / OpenID Connect implementation for C
Version: @VERSION@
Requires: jansson >= 2.3, libcrypto >= 1.0.1@MEMCACHE_PC@@HIREDIS_PC@
Cflags: -I${includedir}
Libs: -L${libdir} -loauth2
liboauth2-1.4.4/liboauth2_apache.pc.in 0000664 0000000 0000000 00000000572 14161030125 0017531 0 ustar 00root root 0000000 0000000 #
# liboauth2_apache - Apache C bindings for liboauth2
#
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: liboauth2_apache
URL: https://github.com/zmartzone/liboauth2
Description: Apache C bindings for liboauth2
Version: @VERSION@
Requires: liboauth2 >= 1.4.0, apr-1, apr-util-1
Cflags: -I${includedir}
Libs: -L${libdir} -loauth2_apache
liboauth2-1.4.4/liboauth2_nginx.pc.in 0000664 0000000 0000000 00000000542 14161030125 0017430 0 ustar 00root root 0000000 0000000 #
# liboauth2_nginx - NGINX C bindings for liboauth2
#
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: liboauth2_nginx
URL: https://github.com/zmartzone/liboauth2
Description: NGINX C bindings for liboauth2
Version: @VERSION@
Requires: liboauth2 >= 1.4.0
Cflags: -I${includedir}
Libs: -L${libdir} -loauth2_nginx
liboauth2-1.4.4/m4/ 0000775 0000000 0000000 00000000000 14161030125 0013722 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/m4/.gitignore 0000664 0000000 0000000 00000000104 14161030125 0015705 0 ustar 00root root 0000000 0000000 /libtool.m4
/ltoptions.m4
/ltsugar.m4
/ltversion.m4
/lt~obsolete.m4
liboauth2-1.4.4/m4/ax_code_coverage.m4 0000664 0000000 0000000 00000027075 14161030125 0017454 0 ustar 00root root 0000000 0000000 # ===========================================================================
# 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 .
#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])])
])
liboauth2-1.4.4/src/ 0000775 0000000 0000000 00000000000 14161030125 0014171 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/src/.gitignore 0000664 0000000 0000000 00000000074 14161030125 0016162 0 ustar 00root root 0000000 0000000 /*.lo
/*.o
/*.la
/*.gcno
/*.gcno
/.deps/
/.libs/
/.dirstamp
liboauth2-1.4.4/src/cache.c 0000664 0000000 0000000 00000023377 14161030125 0015414 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include "oauth2/cache.h"
#include "oauth2/ipc.h"
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "cache_int.h"
#include "cfg_int.h"
#include "util_int.h"
_OAUTH2_CFG_GLOBAL_LIST(cache_type, oauth2_cache_type_t)
_OAUTH2_CFG_GLOBAL_LIST(cache, oauth2_cache_t)
extern oauth2_cache_type_t oauth2_cache_shm;
extern oauth2_cache_type_t oauth2_cache_file;
#ifdef HAVE_LIBMEMCACHE
extern oauth2_cache_type_t oauth2_cache_memcache;
#endif
#ifdef HAVE_LIBHIREDIS
extern oauth2_cache_type_t oauth2_cache_redis;
#endif
#define _OAUTH2_CACHE_OPENSSL_ERR ERR_error_string(ERR_get_error(), NULL)
static bool _oauth2_cache_global_initialized = false;
static void _oauth2_cache_global_init(oauth2_log_t *log)
{
if (_oauth2_cache_global_initialized == true)
goto end;
_M_cache_type_list_register(log, oauth2_cache_shm.name,
&oauth2_cache_shm, NULL);
_M_cache_type_list_register(log, oauth2_cache_file.name,
&oauth2_cache_file, NULL);
#ifdef HAVE_LIBMEMCACHE
_M_cache_type_list_register(log, oauth2_cache_memcache.name,
&oauth2_cache_memcache, NULL);
#endif
#ifdef HAVE_LIBHIREDIS
_M_cache_type_list_register(log, oauth2_cache_redis.name,
&oauth2_cache_redis, NULL);
#endif
_oauth2_cache_global_initialized = true;
end:
return;
}
static void _oauth2_cache_free(oauth2_log_t *log, oauth2_cache_t *cache)
{
oauth2_debug(log, "enter");
if ((cache == NULL) || (cache->type == NULL))
goto end;
if (cache->key_hash_algo)
oauth2_mem_free(cache->key_hash_algo);
if (cache->enc_key)
oauth2_mem_free(cache->enc_key);
if (cache->passphrase_hash_algo)
oauth2_mem_free(cache->passphrase_hash_algo);
if (cache->type->free)
cache->type->free(log, cache);
oauth2_mem_free(cache);
end:
oauth2_debug(log, "leave");
return;
}
oauth2_cache_t *_oauth2_cache_init(oauth2_log_t *log, const char *type,
const oauth2_nv_list_t *params)
{
oauth2_cache_t *cache = NULL;
oauth2_cache_type_t *cache_type = NULL;
_oauth2_cache_global_init(log);
if (type == NULL)
type = "shm";
cache_type = _M_cache_type_list_get(log, type);
if (cache_type == NULL) {
oauth2_error(log, "cache type %s is not registered", type);
goto end;
}
if (cache_type->init == NULL)
goto end;
cache = oauth2_mem_alloc(sizeof(oauth2_cache_t));
if (cache == NULL)
goto end;
if (cache_type->init(log, cache, params) == false)
goto end;
cache->key_hash_algo =
oauth2_strdup(oauth2_nv_list_get(log, params, "key_hash_algo"));
cache->passphrase_hash_algo = oauth2_strdup(
oauth2_nv_list_get(log, params, "passphrase_hash_algo"));
cache->encrypt =
oauth2_parse_bool(log, oauth2_nv_list_get(log, params, "encrypt"),
cache->type->encrypt_by_default);
if (cache->encrypt == false) {
cache->enc_key = NULL;
goto end;
}
end:
if (cache)
_M_cache_list_register(log,
oauth2_nv_list_get(log, params, "name"),
cache, _oauth2_cache_free);
return cache;
}
oauth2_cache_t *oauth2_cache_obtain(oauth2_log_t *log, const char *name)
{
oauth2_cache_t *c = NULL;
oauth2_debug(log, "enter: %s", name);
if (_M_cache_list_empty(log)) {
c = _oauth2_cache_init(log, NULL, NULL);
if (c == NULL)
goto end;
if (_oauth2_cache_post_config(log, c) == false) {
c = NULL;
goto end;
}
}
c = _M_cache_list_get(log, name);
end:
oauth2_debug(log, "leave: %p", c);
return c;
}
void _oauth2_cache_global_cleanup(oauth2_log_t *log)
{
oauth2_debug(log, "enter");
_M_cache_list_release(log);
_M_cache_type_list_release(log);
_oauth2_cache_global_initialized = false;
oauth2_debug(log, "leave");
}
bool _oauth2_cache_post_config(oauth2_log_t *log, oauth2_cache_t *cache)
{
bool rc = false;
oauth2_debug(log, "enter");
if ((cache == NULL) || (cache->type == NULL))
goto end;
if (cache->type->post_config == NULL) {
rc = true;
goto end;
}
rc = cache->type->post_config(log, cache);
end:
oauth2_debug(log, "return: %d", rc);
return rc;
}
bool oauth2_cache_child_init(oauth2_log_t *log, oauth2_cache_t *cache)
{
bool rc = false;
if ((cache == NULL) || (cache->type == NULL))
goto end;
if (cache->type->child_init == NULL) {
rc = true;
goto end;
}
rc = cache->type->child_init(log, cache);
end:
return rc;
}
static bool _oauth2_cache_hash_key(oauth2_log_t *log, const char *key,
const char *algo, char **hash)
{
bool rc = false;
oauth2_debug(log, "enter: key=%s, algo=%s", key, algo);
if ((algo) && (strcmp(algo, "none") == 0)) {
*hash = oauth2_strdup(key);
rc = true;
goto end;
}
if (algo == NULL)
algo = OAUTH2_JOSE_OPENSSL_ALG_SHA256;
rc = oauth2_jose_hash2s(log, algo, key, hash);
end:
oauth2_debug(log, "leave: hashed key: %s", *hash);
return rc;
}
static const char *_oauth_cache_get_enc_key(oauth2_log_t *log,
oauth2_cache_t *cache)
{
const char *passphrase = NULL, *passphrase_hash_algo = NULL;
if (cache->enc_key != NULL)
goto end;
passphrase = oauth2_crypto_passphrase_get(log);
if (passphrase == NULL)
goto end;
passphrase_hash_algo = cache->passphrase_hash_algo
? passphrase_hash_algo
: OAUTH2_JOSE_OPENSSL_ALG_SHA256;
if (strcmp(passphrase_hash_algo, "none") == 0) {
cache->enc_key = oauth2_strdup(passphrase);
} else {
// if (oauth2_jose_hash_bytes(log,
//passphrase_hash_algo, (const unsigned char *)passphrase,
// strlen(passphrase),
//&cache->enc_key, &enc_key_len) == false) {
if (oauth2_jose_hash2s(log, passphrase_hash_algo, passphrase,
&cache->enc_key) == false) {
oauth2_error(
log, "could not hash cache encryption passphrase");
goto end;
}
}
end:
return cache->enc_key;
}
static int oauth2_cache_decrypt(oauth2_log_t *log, oauth2_cache_t *cache,
const char *value, char **plaintext)
{
int len = -1;
oauth2_debug(log, "enter");
if (oauth2_jose_decrypt(
log, (const char *)_oauth_cache_get_enc_key(log, cache), value,
plaintext) == false)
goto end;
len = strlen(*plaintext);
end:
oauth2_debug(log, "leave: len=%d", len);
return len;
}
bool oauth2_cache_get(oauth2_log_t *log, oauth2_cache_t *cache, const char *key,
char **value)
{
bool rc = false;
char *hashed_key = NULL;
char *encrypted_value = NULL;
oauth2_debug(log, "enter: key=%s, type=%s, decrypt=%d",
key ? key : "",
cache && cache->type ? cache->type->name : "",
cache ? cache->encrypt : -1);
if ((cache == NULL) || (cache->type == NULL) ||
(cache->type->get == NULL) || (key == NULL) || (value == NULL))
goto end;
if (_oauth2_cache_hash_key(log, key, cache->key_hash_algo,
&hashed_key) == false)
goto end;
if (cache->type->get(log, cache, hashed_key, value) == false)
goto end;
if ((cache->encrypt) && (*value)) {
if (oauth2_cache_decrypt(log, cache, *value, &encrypted_value) <
0) {
oauth2_mem_free(*value);
*value = NULL;
goto end;
}
oauth2_mem_free(*value);
*value = encrypted_value;
}
rc = true;
end:
if (hashed_key)
oauth2_mem_free(hashed_key);
oauth2_debug(log, "leave: cache %s for key: %s return: %lu bytes",
rc ? (*value ? "hit" : "miss") : "error",
key ? key : "",
*value ? (unsigned long)strlen(*value) : 0);
return rc;
}
static int oauth2_cache_encrypt(oauth2_log_t *log, oauth2_cache_t *cache,
const char *plaintext, char **result)
{
int len = -1;
oauth2_debug(log, "enter: %s", plaintext);
if (oauth2_jose_encrypt(
log, (const char *)_oauth_cache_get_enc_key(log, cache),
plaintext, result) == false)
goto end;
len = strlen(*result);
end:
oauth2_debug(log, "leave: len=%d", (int)len);
return len;
}
bool oauth2_cache_set(oauth2_log_t *log, oauth2_cache_t *cache, const char *key,
const char *value, oauth2_time_t ttl_s)
{
bool rc = false;
char *hashed_key = NULL;
char *encrypted = NULL;
oauth2_debug(log,
"enter: key=%s, len=%lu, ttl(s)=" OAUTH2_TIME_T_FORMAT
", type=%s, encrypt=%d",
key ? key : "",
value ? (unsigned long)strlen(value) : 0, ttl_s,
(cache && cache->type) ? cache->type->name : "",
cache ? cache->encrypt : -1);
if ((cache == NULL) || (cache->type == NULL) ||
(cache->type->set == NULL) || (key == NULL))
goto end;
if (_oauth2_cache_hash_key(log, key, cache->key_hash_algo,
&hashed_key) == false)
goto end;
if ((cache->encrypt) && (value))
if (oauth2_cache_encrypt(log, cache, value, &encrypted) < 0)
goto end;
if (cache->type->set(log, cache, hashed_key,
encrypted ? encrypted : value, ttl_s) == false) {
goto end;
}
rc = true;
end:
if (hashed_key)
oauth2_mem_free(hashed_key);
if (encrypted)
oauth2_mem_free(encrypted);
if (rc)
oauth2_debug(log, "leave: successfully stored: %s",
key ? key : "");
else
oauth2_error(log, "leave: could NOT store: %s",
key ? key : "");
return rc;
}
liboauth2-1.4.4/src/cache/ 0000775 0000000 0000000 00000000000 14161030125 0015234 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/src/cache/.gitignore 0000664 0000000 0000000 00000000056 14161030125 0017225 0 ustar 00root root 0000000 0000000 /.dirstamp
/.deps/
/.libs/
/*.lo
/*.o
/*.gcno
liboauth2-1.4.4/src/cache/file.c 0000664 0000000 0000000 00000025137 14161030125 0016327 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#ifndef _WIN32
#include
#define _unlink unlink
#endif
#include
#include
#include
#include
#include "cache_int.h"
typedef struct oauth2_cache_impl_file_t {
oauth2_ipc_mutex_t *mutex;
char *dir;
oauth2_time_t clean_interval;
} oauth2_cache_impl_file_t;
typedef struct {
oauth2_uint_t len;
oauth2_time_t expire;
} oauth2_cache_file_info_t;
#define OAUTH2_CACHE_FILE_PREFIX "oauth2-cache-"
oauth2_cache_type_t oauth2_cache_file;
static bool oauth2_cache_file_init(oauth2_log_t *log, oauth2_cache_t *cache,
const oauth2_nv_list_t *options)
{
bool rc = false;
const char *v = NULL;
oauth2_cache_impl_file_t *impl = NULL;
oauth2_debug(log, "enter");
impl = oauth2_mem_alloc(sizeof(oauth2_cache_impl_file_t));
if (impl == NULL)
goto end;
cache->impl = impl;
cache->type = &oauth2_cache_file;
impl->mutex = oauth2_ipc_mutex_init(log);
if (impl->mutex == NULL)
goto end;
v = oauth2_nv_list_get(log, options, "dir");
if (v == NULL) {
#ifdef WIN32
v = "C:\\TEMP";
#else
v = "/tmp";
#endif
}
impl->dir = oauth2_strdup(v);
v = oauth2_nv_list_get(log, options, "clean_interval");
impl->clean_interval = oauth2_parse_time_sec(log, v, 60);
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_file_free(oauth2_log_t *log, oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_file_t *impl =
(oauth2_cache_impl_file_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (impl->mutex != NULL) {
oauth2_ipc_mutex_free(log, impl->mutex);
impl->mutex = NULL;
}
if (impl->dir) {
oauth2_mem_free(impl->dir);
impl->dir = NULL;
}
oauth2_mem_free(impl);
cache->impl = NULL;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_file_post_config(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_file_t *impl =
(oauth2_cache_impl_file_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
rc = oauth2_ipc_mutex_post_config(log, impl->mutex);
if (rc == false)
goto end;
// TODO: check directory accessibility ?
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_file_child_init(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_file_t *impl =
(oauth2_cache_impl_file_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
// TOOD: nothing? then put function pointer in type struct to NULL
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static char *_oauth2_cache_file_path(oauth2_log_t *log,
oauth2_cache_impl_file_t *impl,
const char *key)
{
char *path = NULL;
// TODO: WIN32 \ ?
path = oauth2_strdup(impl->dir);
path = oauth2_stradd(path, "/", OAUTH2_CACHE_FILE_PREFIX, key);
return path;
}
static bool _oauth2_cache_file_read(oauth2_log_t *log, FILE *f, void *buf,
const size_t len)
{
bool rc = false;
int n = 0;
n = fread(buf, 1, len, f);
if (n <= 0) {
oauth2_error(log, "fread failed: %s", strerror(errno));
goto end;
}
if (n != len) {
oauth2_error(log,
"fread returned %zu bytes but requested %zu bytes",
n, len);
goto end;
}
rc = true;
end:
return rc;
}
static bool _oauth2_cache_file_remove(oauth2_log_t *log, const char *path)
{
bool rc = true;
if (_unlink(path) != 0) {
oauth2_error(log, "could not delete cache file \"%s\" (%s)",
path, strerror(errno));
rc = false;
}
return rc;
}
static bool oauth2_cache_file_get(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, char **value)
{
bool rc = false;
char *path = NULL;
FILE *f = NULL;
oauth2_cache_file_info_t info;
oauth2_cache_impl_file_t *impl =
(oauth2_cache_impl_file_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
// TODO:
// if (oauth2_cache_shm_check_key(log, impl, key) == false)
// goto end;
//
// and/or url-encode to make a valid filename?
*value = NULL;
path = _oauth2_cache_file_path(log, cache->impl, key);
if (oauth2_ipc_mutex_lock(log, impl->mutex) == false)
goto end;
f = fopen(path, "rb");
if (f == NULL) {
if (errno == ENOENT) {
oauth2_debug(log, "cache miss for key \"%s\"", key);
rc = true;
} else {
oauth2_error(log, "fopen failed: %s", strerror(errno));
}
goto unlock;
}
if (fseek(f, 0, SEEK_SET) != 0) {
oauth2_error(log, "fseek failed: %s", strerror(errno));
goto unlock;
}
if (_oauth2_cache_file_read(log, f, &info,
sizeof(oauth2_cache_file_info_t)) == false)
goto unlock;
if (oauth2_time_now_sec() >= info.expire) {
fclose(f);
f = NULL;
oauth2_debug(log,
"cache entry \"%s\" expired, removing file \"%s\"",
key, path);
rc = _oauth2_cache_file_remove(log, path);
goto unlock;
}
*value = oauth2_mem_alloc(info.len);
if (*value == NULL)
goto unlock;
rc = _oauth2_cache_file_read(log, f, (void *)*value, info.len);
unlock:
oauth2_ipc_mutex_unlock(log, impl->mutex);
end:
if (f)
fclose(f);
if (path)
oauth2_mem_free(path);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool _oauth2_cache_file_write(oauth2_log_t *log, FILE *f, void *buf,
const size_t len)
{
bool rc = false;
int n = 0;
n = fwrite(buf, 1, len, f);
if (n <= 0) {
oauth2_error(log, "fwrite failed: %s", strerror(errno));
goto end;
}
if (n != len) {
oauth2_error(
log, "fwrite returned %zu bytes but requested %zu bytes", n,
len);
goto end;
}
rc = true;
end:
return rc;
}
#ifdef __APPLE__
#ifndef st_mtime
#define st_mtime st_mtimespec.tv_sec
#endif
#endif
static void _oauth2_cache_files_clean(oauth2_log_t *log,
oauth2_cache_impl_file_t *impl)
{
bool rc = false;
char *path = NULL, *fpath = NULL, *filename = NULL;
struct stat fi;
FILE *f = NULL;
DIR *d = NULL;
struct dirent *dep = NULL;
oauth2_cache_file_info_t info;
// TODO: pretty unique, right?
path = oauth2_stradd(NULL, impl->dir, "/", "__oauth2-cache-cleaned__");
if (stat(path, &fi) == 0) {
if (oauth2_time_now_sec() <
(fi.st_mtime + impl->clean_interval)) {
oauth2_debug(log,
"last cleanup call was less "
"than " OAUTH2_TIME_T_FORMAT
" seconds ago (next one as early as "
"in " OAUTH2_TIME_T_FORMAT " secs)",
impl->clean_interval,
fi.st_mtime + impl->clean_interval -
oauth2_time_now_sec());
goto end;
}
oauth2_debug(log, "start cleaning cycle");
}
// create and/or set file modification time
f = fopen(path, "wb");
if (f == NULL) {
oauth2_error(log, "fopen failed: %s", strerror(errno));
goto end;
}
_oauth2_cache_file_write(log, f, (void *)"", 1);
fclose(f);
f = NULL;
d = opendir(impl->dir);
if (d == NULL) {
oauth2_error(log, "opendir failed: %s", strerror(errno));
goto end;
}
while ((dep = readdir(d))) {
filename = oauth2_stradd(NULL, impl->dir, "/", dep->d_name);
if (filename == NULL)
goto cont;
if (stat(filename, &fi) != 0) {
oauth2_error(log, "stat failed on %s: %s\n", filename,
strerror(errno));
goto cont;
}
if ((fi.st_mode & S_IFMT) == S_IFDIR)
goto cont;
if (strstr(dep->d_name, OAUTH2_CACHE_FILE_PREFIX) !=
dep->d_name)
goto cont;
fpath = oauth2_stradd(NULL, impl->dir, "/", dep->d_name);
if (fpath == NULL)
goto cont;
f = fopen(fpath, "rb");
if (f == NULL) {
oauth2_error(log, "fopen failed: %s", strerror(errno));
goto cont;
}
rc = _oauth2_cache_file_read(log, f, &info,
sizeof(oauth2_cache_file_info_t));
if ((rc == false) || (oauth2_time_now_sec() < info.expire)) {
oauth2_debug(
log,
"cache entry (%s) expired, removing file \"%s\")",
dep->d_name, fpath);
if (_oauth2_cache_file_remove(log, fpath) == false) {
oauth2_error(log,
"could not delete cache file: %s",
fpath);
}
}
cont:
if (f) {
fclose(f);
f = NULL;
}
if (filename) {
oauth2_mem_free(filename);
filename = NULL;
}
if (fpath) {
oauth2_mem_free(fpath);
fpath = NULL;
}
continue;
}
end:
if (d)
closedir(d);
if (path)
oauth2_mem_free(path);
return;
}
static bool oauth2_cache_file_set(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, const char *value,
oauth2_time_t ttl_s)
{
bool rc = false;
char *path = NULL;
FILE *f = NULL;
oauth2_cache_file_info_t info;
oauth2_cache_impl_file_t *impl =
(oauth2_cache_impl_file_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
/*
* TODO:
if (oauth2_cache_shm_check_key(log, impl, key) == false)
goto end;
if (oauth2_cache_shm_check_value(log, impl, value) == false)
goto end;
*/
path = _oauth2_cache_file_path(log, impl, key);
if (oauth2_ipc_mutex_lock(log, impl->mutex) == false)
goto end;
_oauth2_cache_files_clean(log, impl);
if (value == NULL) {
rc = _oauth2_cache_file_remove(log, path);
goto unlock;
}
f = fopen(path, "wb");
if (f == NULL) {
oauth2_error(log, "fopen failed: %s", strerror(errno));
goto unlock;
}
if (fseek(f, 0, SEEK_SET) != 0) {
oauth2_error(log, "fseek failed: %s", strerror(errno));
goto unlock;
}
info.expire = oauth2_time_now_sec() + ttl_s;
info.len = strlen(value) + 1;
if (_oauth2_cache_file_write(log, f, &info,
sizeof(oauth2_cache_file_info_t)) == false)
goto unlock;
rc = _oauth2_cache_file_write(log, f, (void *)value, info.len);
unlock:
oauth2_ipc_mutex_unlock(log, impl->mutex);
end:
if (f)
fclose(f);
if (path)
oauth2_mem_free(path);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
OAUTH2_CACHE_TYPE_DECLARE(file, true)
liboauth2-1.4.4/src/cache/memcache.c 0000664 0000000 0000000 00000011077 14161030125 0017150 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include "cache_int.h"
#include
typedef struct oauth2_cache_impl_memcache_t {
memcached_st *memc;
} oauth2_cache_impl_memcache_t;
oauth2_cache_type_t oauth2_cache_memcache;
static bool oauth2_cache_memcache_init(oauth2_log_t *log, oauth2_cache_t *cache,
const oauth2_nv_list_t *options)
{
bool rc = false;
oauth2_cache_impl_memcache_t *impl = NULL;
const char *config_string = NULL;
oauth2_debug(log, "enter");
impl = oauth2_mem_alloc(sizeof(oauth2_cache_impl_memcache_t));
if (impl == NULL)
goto end;
cache->impl = impl;
cache->type = &oauth2_cache_memcache;
config_string = oauth2_nv_list_get(log, options, "config_string");
if (config_string == NULL)
config_string = "--SERVER=localhost";
impl->memc = memcached(config_string, strlen(config_string));
if (impl->memc == NULL) {
oauth2_error(log, "call to memcached() failed");
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_memcache_free(oauth2_log_t *log, oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_memcache_t *impl =
(oauth2_cache_impl_memcache_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (impl->memc) {
memcached_free(impl->memc);
impl->memc = NULL;
}
oauth2_mem_free(impl);
cache->impl = NULL;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_memcache_post_config(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_memcache_t *impl =
(oauth2_cache_impl_memcache_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
//...
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_memcache_child_init(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_memcache_t *impl =
(oauth2_cache_impl_memcache_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
//...
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_memcache_get(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, char **value)
{
bool rc = false;
memcached_return mrc;
size_t len;
uint32_t flags;
oauth2_cache_impl_memcache_t *impl =
(oauth2_cache_impl_memcache_t *)cache->impl;
oauth2_debug(log, "enter");
if ((impl == NULL) || (impl->memc == NULL))
goto end;
*value = NULL;
*value =
memcached_get(impl->memc, key, strlen(key), &len, &flags, &mrc);
if ((mrc != MEMCACHED_SUCCESS) && (mrc != MEMCACHED_NOTFOUND)) {
oauth2_error(log, "memcached_get failed: %s\n",
memcached_strerror(impl->memc, mrc));
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_memcache_set(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, const char *value,
oauth2_time_t ttl_s)
{
bool rc = false;
memcached_return mrc;
uint32_t flags = 0;
oauth2_cache_impl_memcache_t *impl =
(oauth2_cache_impl_memcache_t *)cache->impl;
oauth2_debug(log, "enter");
if ((impl == NULL) || (impl->memc == NULL))
goto end;
mrc = memcached_set(impl->memc, key, strlen(key), value,
value ? strlen(value) : 0, (time_t)ttl_s, flags);
if (mrc != MEMCACHED_SUCCESS) {
oauth2_error(log, "memcached_set failed: %s\n",
memcached_strerror(impl->memc, mrc));
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
OAUTH2_CACHE_TYPE_DECLARE(memcache, true)
liboauth2-1.4.4/src/cache/redis.c 0000664 0000000 0000000 00000017630 14161030125 0016515 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include
#include "cache_int.h"
#include "hiredis/hiredis.h"
typedef struct oauth2_cache_impl_redis_t {
oauth2_ipc_mutex_t *mutex;
char *host_str;
oauth2_uint_t port;
char *passwd;
redisContext *ctx;
} oauth2_cache_impl_redis_t;
oauth2_cache_type_t oauth2_cache_redis;
static bool oauth2_cache_redis_init(oauth2_log_t *log, oauth2_cache_t *cache,
const oauth2_nv_list_t *options)
{
bool rc = false;
oauth2_cache_impl_redis_t *impl = NULL;
const char *v = NULL;
oauth2_debug(log, "enter");
impl = oauth2_mem_alloc(sizeof(oauth2_cache_impl_redis_t));
if (impl == NULL)
goto end;
cache->impl = impl;
cache->type = &oauth2_cache_redis;
impl->mutex = oauth2_ipc_mutex_init(log);
if (impl->mutex == NULL)
goto end;
// TODO: #define and/or parse host:port tuple in one step
v = oauth2_nv_list_get(log, options, "host");
if (v == NULL)
v = "localhost";
impl->host_str = oauth2_strdup(v);
v = oauth2_nv_list_get(log, options, "port");
impl->port = oauth2_parse_uint(log, v, 6379);
v = oauth2_nv_list_get(log, options, "password");
impl->passwd = v ? oauth2_strdup(v) : NULL;
impl->ctx = NULL;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_redis_free(oauth2_log_t *log, oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_redis_t *impl =
(oauth2_cache_impl_redis_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (impl->mutex) {
oauth2_ipc_mutex_lock(log, impl->mutex);
if (impl->ctx) {
redisFree(impl->ctx);
impl->ctx = NULL;
}
oauth2_ipc_mutex_unlock(log, impl->mutex);
oauth2_ipc_mutex_free(log, impl->mutex);
impl->mutex = NULL;
}
if (impl->host_str)
oauth2_mem_free(impl->host_str);
if (impl->passwd)
oauth2_mem_free(impl->passwd);
oauth2_mem_free(impl);
cache->impl = NULL;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_redis_post_config(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_redis_t *impl =
(oauth2_cache_impl_redis_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
rc = oauth2_ipc_mutex_post_config(log, impl->mutex);
if (rc == false)
goto end;
// TODO: connect to the server here?
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_redis_child_init(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_redis_t *impl =
(oauth2_cache_impl_redis_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
// TODO: nothing?
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool _oauth2_cache_redis_connect(oauth2_log_t *log,
oauth2_cache_impl_redis_t *impl)
{
bool rc = false;
if (impl->ctx) {
rc = true;
goto end;
}
impl->ctx = redisConnect(impl->host_str, impl->port);
if ((impl->ctx == NULL) || (impl->ctx->err != 0)) {
oauth2_error(log,
"failed to connect to Redis server (%s:%d): '%s'",
impl->host_str, impl->port,
impl->ctx != NULL ? impl->ctx->errstr : "");
redisFree(impl->ctx);
impl->ctx = NULL;
goto end;
}
oauth2_debug(
log,
"successfully connected to Redis server (%s:" OAUTH2_UINT_FORMAT
")",
impl->host_str, impl->port);
rc = true;
end:
return rc;
}
#define OIDC_REDIS_MAX_TRIES 2
static redisReply *_oauth2_cache_redis_command(oauth2_log_t *log,
oauth2_cache_impl_redis_t *impl,
const char *command)
{
redisReply *reply = NULL;
int i = 0;
oauth2_debug(log, "enter: %s", command);
for (i = 0; i < OIDC_REDIS_MAX_TRIES; i++) {
if (_oauth2_cache_redis_connect(log, impl) == false)
break;
if (impl->passwd != NULL) {
reply =
redisCommand(impl->ctx, "AUTH %s", impl->passwd);
if ((reply == NULL) ||
(reply->type == REDIS_REPLY_ERROR))
oauth2_error(
log,
"Redis AUTH command (attempt=%d to "
"%s:" OAUTH2_UINT_FORMAT
") failed: '%s' [%s]",
i, impl->host_str, impl->port,
impl->ctx->errstr,
reply ? reply->str : "");
if (reply) {
freeReplyObject(reply);
reply = NULL;
}
}
reply = redisCommand(impl->ctx, command);
if ((reply != NULL) && (reply->type != REDIS_REPLY_ERROR))
break;
oauth2_error(
log,
"Redis command (attempt=%d to %s:" OAUTH2_UINT_FORMAT
") failed, disconnecting: '%s' [%s]",
i, impl->host_str, impl->port, impl->ctx->errstr,
reply ? reply->str : "");
if (reply) {
freeReplyObject(reply);
reply = NULL;
}
redisFree(impl->ctx);
impl->ctx = NULL;
}
oauth2_debug(log, "leave: %p", reply);
return reply;
}
static bool oauth2_cache_redis_get(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, char **value)
{
bool rc = false;
redisReply *reply = NULL;
char *cmd = NULL;
oauth2_cache_impl_redis_t *impl =
(oauth2_cache_impl_redis_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
*value = NULL;
if (oauth2_ipc_mutex_lock(log, impl->mutex) == false)
goto end;
cmd = oauth2_stradd(NULL, "GET", " ", key);
reply = _oauth2_cache_redis_command(log, impl, cmd);
if (reply == NULL)
goto unlock;
if (reply->type == REDIS_REPLY_NIL) {
rc = true;
goto unlock;
}
// TODO: should we not store the \0 and/or allow binary data?
if (reply->len != strlen(reply->str)) {
oauth2_error(
log, "redisCommand reply->len != strlen(reply->str): '%s'",
reply->str);
goto unlock;
}
*value = oauth2_strndup(reply->str, reply->len);
rc = true;
unlock:
oauth2_ipc_mutex_unlock(log, impl->mutex);
end:
if (cmd)
oauth2_mem_free(cmd);
if (reply)
freeReplyObject(reply);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
#define OAUTH2_UINT_MAX_STR 64
static bool oauth2_cache_redis_set(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, const char *value,
oauth2_time_t ttl_s)
{
bool rc = false;
redisReply *reply = NULL;
char *cmd = NULL;
char s_timeout[OAUTH2_UINT_MAX_STR];
oauth2_cache_impl_redis_t *impl =
(oauth2_cache_impl_redis_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (oauth2_ipc_mutex_lock(log, impl->mutex) == false)
goto end;
if (value) {
oauth2_snprintf(s_timeout, OAUTH2_UINT_MAX_STR,
"" OAUTH2_TIME_T_FORMAT "", ttl_s);
cmd = oauth2_strdup("SETEX ");
cmd = oauth2_stradd(cmd, key, " ", s_timeout);
cmd = oauth2_stradd(cmd, " ", value, NULL);
} else {
cmd = oauth2_stradd(NULL, "DEL", " ", key);
}
reply = _oauth2_cache_redis_command(log, impl, cmd);
if (reply == NULL)
goto unlock;
rc = (reply->type != REDIS_REPLY_ERROR);
unlock:
oauth2_ipc_mutex_unlock(log, impl->mutex);
end:
if (cmd)
oauth2_mem_free(cmd);
if (reply)
freeReplyObject(reply);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
OAUTH2_CACHE_TYPE_DECLARE(redis, true)
liboauth2-1.4.4/src/cache/shm.c 0000664 0000000 0000000 00000025662 14161030125 0016202 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include
#include "cache_int.h"
typedef struct oauth2_cache_impl_shm_t {
oauth2_ipc_shm_t *shm;
oauth2_ipc_mutex_t *mutex;
oauth2_uint_t max_key_size;
oauth2_uint_t max_val_size;
oauth2_uint_t max_entries;
} oauth2_cache_impl_shm_t;
typedef __attribute__((aligned(64))) struct oauth2_cache_shm_entry_t {
oauth2_time_t access_s;
oauth2_time_t expires_s;
uint8_t key_and_value[];
} oauth2_cache_shm_entry_t;
#define OAUTH2_CACHE_SHM_KEY_OFFSET(ptr) (uint8_t *)&ptr->key_and_value[0]
#define OAUTH2_CACHE_SHM_VALUE_OFFSET(ptr, impl) \
(uint8_t *)&ptr->key_and_value[impl->max_key_size]
#define OAUTH2_CACHE_SHM_SLOT_SIZE(impl) \
(sizeof(oauth2_cache_shm_entry_t) + impl->max_key_size + \
impl->max_val_size)
#define OAUTH2_CACHE_SHM_ADD_OFFSET(ptr, impl) \
ptr = (oauth2_cache_shm_entry_t *)((uint8_t *)ptr + \
OAUTH2_CACHE_SHM_SLOT_SIZE(impl))
#define OAUTH2_CACHE_SHM_MAX_KEY_SIZE "max_key_size"
#define OAUTH2_CACHE_SHM_MAX_VALUE_SIZE "max_val_size"
#define OAUTH2_CACHE_SHM_MAX_ENTRIES "max_entries"
#define OAUTH2_CACHE_SHM_MAX_KEY_SIZE_DEFAULT 64
#define OAUTH2_CACHE_SHM_MAX_VALUE_SIZE_DEFAULT 8192
#define OAUTH2_CACHE_SHM_MAX_ENTRIES_DEFAULT 1000
oauth2_cache_type_t oauth2_cache_shm;
static bool oauth2_cache_shm_init(oauth2_log_t *log, oauth2_cache_t *cache,
const oauth2_nv_list_t *options)
{
bool rc = false;
oauth2_cache_impl_shm_t *impl = NULL;
oauth2_uint_t n = 0;
oauth2_debug(log, "enter");
impl = oauth2_mem_alloc(sizeof(oauth2_cache_impl_shm_t));
if (impl == NULL)
goto end;
cache->impl = impl;
cache->type = &oauth2_cache_shm;
impl->mutex = oauth2_ipc_mutex_init(log);
if (impl->mutex == NULL)
goto end;
n = oauth2_parse_uint(
log,
oauth2_nv_list_get(log, options, OAUTH2_CACHE_SHM_MAX_KEY_SIZE),
OAUTH2_CACHE_SHM_MAX_KEY_SIZE_DEFAULT);
impl->max_key_size = (n / 64 + 1) * 64;
impl->max_val_size = oauth2_parse_uint(
log,
oauth2_nv_list_get(log, options, OAUTH2_CACHE_SHM_MAX_VALUE_SIZE),
OAUTH2_CACHE_SHM_MAX_VALUE_SIZE_DEFAULT);
impl->max_entries = oauth2_parse_uint(
log, oauth2_nv_list_get(log, options, OAUTH2_CACHE_SHM_MAX_ENTRIES),
OAUTH2_CACHE_SHM_MAX_ENTRIES_DEFAULT);
oauth2_debug(log,
"creating shm cache: %s=" OAUTH2_UINT_FORMAT
" %s=" OAUTH2_UINT_FORMAT " %s=" OAUTH2_UINT_FORMAT "",
OAUTH2_CACHE_SHM_MAX_KEY_SIZE, impl->max_key_size,
OAUTH2_CACHE_SHM_MAX_VALUE_SIZE, impl->max_val_size,
OAUTH2_CACHE_SHM_MAX_ENTRIES, impl->max_entries);
impl->shm = oauth2_ipc_shm_init(log, OAUTH2_CACHE_SHM_SLOT_SIZE(impl) *
impl->max_entries);
if (impl->shm == NULL)
goto end;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_shm_free(oauth2_log_t *log, oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_shm_t *impl = (oauth2_cache_impl_shm_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (impl->mutex != NULL) {
oauth2_ipc_mutex_lock(log, impl->mutex);
oauth2_ipc_shm_free(log, impl->shm);
oauth2_ipc_mutex_unlock(log, impl->mutex);
oauth2_ipc_mutex_free(log, impl->mutex);
impl->mutex = NULL;
}
oauth2_mem_free(impl);
cache->impl = NULL;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_shm_post_config(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
int i = 0;
oauth2_cache_shm_entry_t *ptr = NULL;
oauth2_cache_impl_shm_t *impl = (oauth2_cache_impl_shm_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
rc = oauth2_ipc_mutex_post_config(log, impl->mutex);
if (rc == false)
goto end;
rc = oauth2_ipc_shm_post_config(log, impl->shm);
if (rc == false)
goto end;
ptr = oauth2_ipc_shm_get(log, impl->shm);
if (ptr == NULL) {
oauth2_error(log, "oauth2_ipc_shm_get failed");
goto end;
}
for (i = 0; i < impl->max_entries;
i++, OAUTH2_CACHE_SHM_ADD_OFFSET(ptr, impl)) {
ptr->access_s = 0;
ptr->expires_s = 0;
*OAUTH2_CACHE_SHM_KEY_OFFSET(ptr) = '\0';
*OAUTH2_CACHE_SHM_VALUE_OFFSET(ptr, impl) = '\0';
}
oauth2_debug(log,
"initialized shared memory with a cache size (# "
"entries) of: " OAUTH2_UINT_FORMAT
", and a max (single) slot size of: " OAUTH2_UINT_FORMAT,
impl->max_entries, OAUTH2_CACHE_SHM_SLOT_SIZE(impl));
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_shm_child_init(oauth2_log_t *log,
oauth2_cache_t *cache)
{
bool rc = false;
oauth2_cache_impl_shm_t *impl = (oauth2_cache_impl_shm_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
rc = oauth2_ipc_shm_child_init(log, impl->shm);
if (rc == false)
goto end;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_shm_check_key(oauth2_log_t *log,
oauth2_cache_impl_shm_t *impl,
const char *key)
{
bool rc = true;
if (strlen(key) > impl->max_key_size) {
oauth2_error(log,
"could not construct cache key since key size is "
"too large (%lu > " OAUTH2_UINT_FORMAT ") : %s",
(unsigned long)strlen(key), impl->max_key_size,
key);
rc = false;
}
return rc;
}
static bool oauth2_cache_shm_get(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, char **value)
{
bool rc = false;
int i = 0;
oauth2_cache_shm_entry_t *ptr = NULL;
const char *entry_key = NULL;
oauth2_time_t now_s = 0;
oauth2_cache_impl_shm_t *impl = (oauth2_cache_impl_shm_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (oauth2_cache_shm_check_key(log, impl, key) == false)
goto end;
*value = NULL;
if (oauth2_ipc_mutex_lock(log, impl->mutex) == false)
goto end;
ptr = oauth2_ipc_shm_get(log, impl->shm);
if (ptr == NULL)
goto unlock;
now_s = oauth2_time_now_sec();
for (i = 0; i < impl->max_entries;
i++, OAUTH2_CACHE_SHM_ADD_OFFSET(ptr, impl)) {
entry_key = (const char *)OAUTH2_CACHE_SHM_KEY_OFFSET(ptr);
oauth2_trace2(log, "loop: %s", entry_key);
if ((entry_key[0] != '\0') &&
(strncmp(entry_key, key, impl->max_key_size) == 0)) {
oauth2_debug(log,
"found: %s (expires=" OAUTH2_TIME_T_FORMAT
", now=" OAUTH2_TIME_T_FORMAT ")",
entry_key, ptr->expires_s, now_s);
if (ptr->expires_s > now_s) {
oauth2_debug(log, "not expired: %s", entry_key);
ptr->access_s = now_s;
*value = oauth2_strdup(
(const char *)OAUTH2_CACHE_SHM_VALUE_OFFSET(
ptr, impl));
} else {
oauth2_debug(log, "expired, clean: %s",
entry_key);
*OAUTH2_CACHE_SHM_KEY_OFFSET(ptr) = '\0';
ptr->access_s = 0;
}
break;
}
}
rc = true;
unlock:
oauth2_ipc_mutex_unlock(log, impl->mutex);
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool oauth2_cache_shm_check_value(oauth2_log_t *log,
oauth2_cache_impl_shm_t *impl,
const char *value)
{
bool rc = true;
if ((value != NULL) && (strlen(value) > impl->max_val_size)) {
oauth2_error(log,
"could not store value since value size is too "
"large (%lu > " OAUTH2_UINT_FORMAT ")",
(unsigned long)strlen(value),
(unsigned long)impl->max_val_size);
rc = false;
}
return rc;
}
static bool oauth2_cache_shm_set(oauth2_log_t *log, oauth2_cache_t *cache,
const char *key, const char *value,
oauth2_time_t ttl_s)
{
bool rc = false;
oauth2_cache_shm_entry_t *match, *free, *lru;
oauth2_cache_shm_entry_t *ptr;
int i = 0;
oauth2_time_t now_s, age_s = 0;
oauth2_cache_impl_shm_t *impl = (oauth2_cache_impl_shm_t *)cache->impl;
oauth2_debug(log, "enter");
if (impl == NULL)
goto end;
if (oauth2_cache_shm_check_key(log, impl, key) == false)
goto end;
if (oauth2_cache_shm_check_value(log, impl, value) == false)
goto end;
if (oauth2_ipc_mutex_lock(log, impl->mutex) == false)
goto end;
ptr = oauth2_ipc_shm_get(log, impl->shm);
if (ptr == NULL)
goto unlock;
now_s = oauth2_time_now_sec();
match = NULL;
free = NULL;
lru = ptr;
for (i = 0; i < impl->max_entries;
i++, OAUTH2_CACHE_SHM_ADD_OFFSET(ptr, impl)) {
if (*OAUTH2_CACHE_SHM_KEY_OFFSET(ptr) == '\0') {
if (free == NULL)
free = ptr;
continue;
}
if (strncmp((const char *)OAUTH2_CACHE_SHM_KEY_OFFSET(ptr), key,
impl->max_key_size) == 0) {
match = ptr;
break;
}
if (ptr->expires_s <= now_s) {
if (free == NULL)
free = ptr;
continue;
}
if (ptr->access_s < lru->access_s) {
lru = ptr;
}
}
if (match == NULL && free == NULL) {
age_s = (now_s - lru->access_s);
// TODO: make this 1 hour warning window configurable?
if (age_s < 3600) {
oauth2_warn(
log,
"dropping LRU entry with age=" OAUTH2_TIME_T_FORMAT
" secs, which is less than one hour; consider "
"increasing the cache size through the setting for "
"the maximum number of cache entries that can be "
"held, which is " OAUTH2_UINT_FORMAT " now",
age_s, impl->max_entries);
}
}
ptr = match ? match : (free ? free : lru);
if (value != NULL) {
/*
memcpy((char *)OAUTH2_CACHE_SHM_KEY_OFFSET(ptr), key,
strlen(key));
((char *)OAUTH2_CACHE_SHM_KEY_OFFSET(ptr))[strlen(key)] = '\0';
memcpy((char *)OAUTH2_CACHE_SHM_VALUE_OFFSET(ptr, impl), value,
strlen(value));
((char *)OAUTH2_CACHE_SHM_VALUE_OFFSET(ptr,
impl))[strlen(value)] =
'\0';
*/
oauth2_snprintf((char *)OAUTH2_CACHE_SHM_KEY_OFFSET(ptr),
impl->max_key_size, "%s", key);
oauth2_snprintf(
(char *)OAUTH2_CACHE_SHM_VALUE_OFFSET(ptr, impl),
impl->max_val_size, "%s", value);
ptr->access_s = now_s;
ptr->expires_s = now_s + ttl_s;
} else {
*OAUTH2_CACHE_SHM_KEY_OFFSET(ptr) = '\0';
ptr->access_s = 0;
}
rc = true;
unlock:
oauth2_ipc_mutex_unlock(log, impl->mutex);
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
OAUTH2_CACHE_TYPE_DECLARE(shm, false)
liboauth2-1.4.4/src/cache_int.h 0000664 0000000 0000000 00000003712 14161030125 0016262 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_CACHE_INT_H_
#define _OAUTH2_CACHE_INT_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cache.h"
#include "oauth2/log.h"
typedef struct oauth2_cache_t {
void *impl;
oauth2_cache_type_t *type;
char *key_hash_algo;
bool encrypt;
char *enc_key;
char *passphrase_hash_algo;
} oauth2_cache_t;
oauth2_cache_t *_oauth2_cache_init(oauth2_log_t *log, const char *type,
const oauth2_nv_list_t *params);
bool _oauth2_cache_post_config(oauth2_log_t *log, oauth2_cache_t *cache);
bool _oauth2_cache_child_init(oauth2_log_t *log, oauth2_cache_t *cache);
void _oauth2_cache_global_cleanup(oauth2_log_t *log);
// clang-format off
#define OAUTH2_CACHE_TYPE_DECLARE(type, encrypt) \
oauth2_cache_type_t oauth2_cache_##type = { \
#type, \
encrypt, \
oauth2_cache_##type##_init, \
oauth2_cache_##type##_post_config, \
oauth2_cache_##type##_child_init, \
oauth2_cache_##type##_get, \
oauth2_cache_##type##_set, \
oauth2_cache_##type##_free \
};
// clang-format on
#endif /* _OAUTH2_CACHE_INT_H_ */
liboauth2-1.4.4/src/cfg/ 0000775 0000000 0000000 00000000000 14161030125 0014730 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/src/cfg/.gitignore 0000664 0000000 0000000 00000000056 14161030125 0016721 0 ustar 00root root 0000000 0000000 /.dirstamp
/.deps/
/.libs/
/*.lo
/*.o
/*.gcno
liboauth2-1.4.4/src/cfg/auth.c 0000664 0000000 0000000 00000033200 14161030125 0016033 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#include "cfg_int.h"
#include
#define OAUTH2_ENDPOINT_AUTH_NONE_STR "none"
#define OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC_STR "client_secret_basic"
#define OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST_STR "client_secret_post"
#define OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT_STR "client_secret_jwt"
#define OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT_STR "private_key_jwt"
#define OAUTH2_ENDPOINT_AUTH_CLIENT_CERT_STR "client_cert"
#define OAUTH2_ENDPOINT_AUTH_BASIC_STR "basic"
oauth2_cfg_endpoint_auth_t *oauth2_cfg_endpoint_auth_init(oauth2_log_t *log)
{
oauth2_cfg_endpoint_auth_t *auth =
(oauth2_cfg_endpoint_auth_t *)oauth2_mem_alloc(
sizeof(oauth2_cfg_endpoint_auth_t));
auth->type = OAUTH2_ENDPOINT_AUTH_NONE;
return auth;
}
void oauth2_cfg_endpoint_auth_free(oauth2_log_t *log,
oauth2_cfg_endpoint_auth_t *auth)
{
if (auth == NULL)
goto end;
switch (auth->type) {
case OAUTH2_ENDPOINT_AUTH_NONE:
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC:
if (auth->client_secret_basic.client_id)
oauth2_mem_free(auth->client_secret_basic.client_id);
if (auth->client_secret_basic.client_secret)
oauth2_mem_free(
auth->client_secret_basic.client_secret);
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST:
if (auth->client_secret_post.client_id)
oauth2_mem_free(auth->client_secret_post.client_id);
if (auth->client_secret_post.client_secret)
oauth2_mem_free(auth->client_secret_post.client_secret);
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT:
if (auth->client_secret_jwt.client_id)
oauth2_mem_free(auth->client_secret_jwt.client_id);
if (auth->client_secret_jwt.jwk)
cjose_jwk_release(auth->client_secret_jwt.jwk);
if (auth->client_secret_jwt.aud)
oauth2_mem_free(auth->client_secret_jwt.aud);
break;
case OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT:
if (auth->private_key_jwt.client_id)
oauth2_mem_free(auth->private_key_jwt.client_id);
if (auth->private_key_jwt.jwk)
cjose_jwk_release(auth->private_key_jwt.jwk);
if (auth->private_key_jwt.aud)
oauth2_mem_free(auth->private_key_jwt.aud);
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_CERT:
if (auth->client_cert.certfile)
oauth2_mem_free(auth->client_cert.certfile);
if (auth->client_cert.keyfile)
oauth2_mem_free(auth->client_cert.keyfile);
break;
case OAUTH2_ENDPOINT_AUTH_BASIC:
if (auth->basic.username)
oauth2_mem_free(auth->basic.username);
if (auth->basic.password)
oauth2_mem_free(auth->basic.password);
break;
}
oauth2_mem_free(auth);
end:
return;
}
oauth2_cfg_endpoint_auth_t *
oauth2_cfg_endpoint_auth_clone(oauth2_log_t *log,
const oauth2_cfg_endpoint_auth_t *src)
{
oauth2_cfg_endpoint_auth_t *dst = NULL;
cjose_err err;
if (src == NULL)
goto end;
dst = oauth2_cfg_endpoint_auth_init(log);
dst->type = src->type;
switch (dst->type) {
case OAUTH2_ENDPOINT_AUTH_NONE:
dst->none = src->none;
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC:
dst->client_secret_basic.client_id =
oauth2_strdup(src->client_secret_basic.client_id);
dst->client_secret_basic.client_secret =
oauth2_strdup(src->client_secret_basic.client_secret);
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST:
dst->client_secret_post.client_id =
oauth2_strdup(src->client_secret_post.client_id);
dst->client_secret_post.client_secret =
oauth2_strdup(src->client_secret_post.client_secret);
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT:
dst->client_secret_jwt.aud =
oauth2_strdup(src->client_secret_jwt.aud);
dst->client_secret_jwt.client_id =
oauth2_strdup(src->client_secret_jwt.client_id);
dst->client_secret_jwt.jwk =
cjose_jwk_retain(src->client_secret_jwt.jwk, &err);
break;
case OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT:
dst->private_key_jwt.aud =
oauth2_strdup(src->private_key_jwt.aud);
dst->private_key_jwt.client_id =
oauth2_strdup(src->private_key_jwt.client_id);
dst->private_key_jwt.jwk =
cjose_jwk_retain(src->private_key_jwt.jwk, &err);
break;
case OAUTH2_ENDPOINT_AUTH_CLIENT_CERT:
dst->client_cert.certfile =
oauth2_strdup(src->client_cert.certfile);
dst->client_cert.keyfile =
oauth2_strdup(src->client_cert.keyfile);
break;
case OAUTH2_ENDPOINT_AUTH_BASIC:
dst->basic.username = oauth2_strdup(src->basic.username);
dst->basic.password = oauth2_strdup(src->basic.password);
break;
}
end:
return dst;
}
static char *
oauth2_cfg_endpoint_auth_none_options_set(oauth2_log_t *log,
oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
auth->type = OAUTH2_ENDPOINT_AUTH_NONE;
return NULL;
}
static char *oauth2_cfg_endpoint_auth_client_secret_basic_options_set(
oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
auth->type = OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC;
auth->client_secret_basic.client_id =
oauth2_strdup(oauth2_nv_list_get(log, params, "client_id"));
if (auth->client_secret_basic.client_id == NULL) {
rv =
oauth2_stradd(NULL, "client_id", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC_STR);
goto end;
}
auth->client_secret_basic.client_secret =
oauth2_strdup(oauth2_nv_list_get(log, params, "client_secret"));
if (auth->client_secret_basic.client_secret == NULL) {
rv =
oauth2_stradd(NULL, "client_secret", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC_STR);
goto end;
}
end:
return rv;
}
static char *oauth2_cfg_endpoint_auth_client_secret_post_options_set(
oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
auth->type = OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST;
auth->client_secret_post.client_id =
oauth2_strdup(oauth2_nv_list_get(log, params, "client_id"));
if (auth->client_secret_post.client_id == NULL) {
rv = oauth2_stradd(NULL, "client_id", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST_STR);
goto end;
}
auth->client_secret_post.client_secret =
oauth2_strdup(oauth2_nv_list_get(log, params, "client_secret"));
if (auth->client_secret_post.client_secret == NULL) {
rv = oauth2_stradd(NULL, "client_secret", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST_STR);
goto end;
}
end:
return rv;
}
static char *oauth2_cfg_endpoint_auth_client_secret_jwt_options_set(
oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
const char *client_secret = NULL;
cjose_err err;
auth->type = OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT;
auth->client_secret_jwt.client_id =
oauth2_strdup(oauth2_nv_list_get(log, params, "client_id"));
if (auth->client_secret_jwt.client_id == NULL) {
rv = oauth2_stradd(NULL, "client_id", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT_STR);
goto end;
}
client_secret = oauth2_nv_list_get(log, params, "client_secret");
if (client_secret == NULL) {
rv = oauth2_stradd(NULL, "client_secret", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT_STR);
goto end;
}
err.code = CJOSE_ERR_NONE;
auth->client_secret_jwt.jwk = cjose_jwk_create_oct_spec(
(const unsigned char *)client_secret, strlen(client_secret), &err);
if (auth->client_secret_jwt.jwk == NULL) {
rv = oauth2_stradd(NULL, "cjose_jwk_create_oct_spec failed: ",
err.message, NULL);
goto end;
}
// auth->client_secret_jwt.client_secret =
// oauth2_strdup(oauth2_nv_list_get(log, params, "client_secret"));
// if
//(auth->client_secret_jwt.client_secret == NULL) { rv =
// oauth2_stradd(NULL, "client_secret", " must be set for ",
// OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT_STR); goto
// end;
// }
auth->client_secret_jwt.aud =
oauth2_strdup(oauth2_nv_list_get(log, params, "aud"));
if (auth->client_secret_jwt.aud == NULL) {
rv = oauth2_stradd(NULL, "aud", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT_STR);
goto end;
}
end:
return rv;
}
static char *oauth2_cfg_endpoint_auth_private_key_jwt_options_set(
oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
const char *jwk = NULL;
cjose_err err;
auth->type = OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT;
auth->private_key_jwt.client_id =
oauth2_strdup(oauth2_nv_list_get(log, params, "client_id"));
if (auth->private_key_jwt.client_id == NULL) {
rv = oauth2_stradd(NULL, "client_id", " must be set for ",
OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT_STR);
goto end;
}
err.code = CJOSE_ERR_NONE;
jwk = oauth2_nv_list_get(log, params, "jwk");
if (jwk == NULL) {
rv = oauth2_stradd(NULL, "jwk", " must be set for ",
OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT_STR);
goto end;
}
auth->private_key_jwt.jwk = cjose_jwk_import(jwk, strlen(jwk), &err);
if (auth->private_key_jwt.jwk == NULL) {
rv = oauth2_stradd(NULL, "parsing JWK failed: ",
"cjose_jws_import error: ", err.message);
goto end;
}
auth->private_key_jwt.aud =
oauth2_strdup(oauth2_nv_list_get(log, params, "aud"));
if (auth->private_key_jwt.aud == NULL) {
rv = oauth2_stradd(NULL, "aud", " must be set for ",
OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT_STR);
goto end;
}
end:
return rv;
}
static char *oauth2_cfg_endpoint_auth_client_cert_options_set(
oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
auth->type = OAUTH2_ENDPOINT_AUTH_CLIENT_CERT;
auth->client_cert.certfile =
oauth2_strdup(oauth2_nv_list_get(log, params, "cert"));
if (auth->client_cert.certfile == NULL) {
rv = oauth2_stradd(NULL, "cert", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_CERT_STR);
goto end;
}
auth->client_cert.keyfile =
oauth2_strdup(oauth2_nv_list_get(log, params, "key"));
if (auth->client_cert.keyfile == NULL) {
rv = oauth2_stradd(NULL, "key", " must be set for ",
OAUTH2_ENDPOINT_AUTH_CLIENT_CERT_STR);
goto end;
}
end:
return rv;
}
static char *
oauth2_cfg_endpoint_auth_basic_options_set(oauth2_log_t *log,
oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
auth->type = OAUTH2_ENDPOINT_AUTH_BASIC;
auth->basic.username =
oauth2_strdup(oauth2_nv_list_get(log, params, "username"));
auth->basic.password =
oauth2_strdup(oauth2_nv_list_get(log, params, "password"));
return rv;
}
typedef char *(oauth2_cfg_endpoint_auth_set_options_cb_t)(
oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth,
const oauth2_nv_list_t *params);
typedef struct oauth2_cfg_endpoint_auth_set_options_ctx_t {
const char *type;
oauth2_cfg_endpoint_auth_set_options_cb_t *options_callback;
} oauth2_cfg_endpoint_auth_set_options_ctx_t;
// clang-format off
static oauth2_cfg_endpoint_auth_set_options_ctx_t _oauth2_cfg_endpoint_auth_options_set[] = {
{ OAUTH2_ENDPOINT_AUTH_NONE_STR, oauth2_cfg_endpoint_auth_none_options_set },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC_STR, oauth2_cfg_endpoint_auth_client_secret_basic_options_set },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST_STR, oauth2_cfg_endpoint_auth_client_secret_post_options_set },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT_STR, oauth2_cfg_endpoint_auth_client_secret_jwt_options_set },
{ OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT_STR, oauth2_cfg_endpoint_auth_private_key_jwt_options_set },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_CERT_STR, oauth2_cfg_endpoint_auth_client_cert_options_set },
{ OAUTH2_ENDPOINT_AUTH_BASIC_STR, oauth2_cfg_endpoint_auth_basic_options_set },
{ NULL, NULL }
};
// clang-format on
char *oauth2_cfg_set_endpoint_auth(oauth2_log_t *log,
oauth2_cfg_endpoint_auth_t *auth,
const char *type,
const oauth2_nv_list_t *params,
const char *prefix)
{
char *rv = NULL;
int i = 0;
if (auth == NULL) {
rv = oauth2_strdup("internal error: auth must be set");
goto end;
}
if (type == NULL)
goto end;
i = 0;
while (_oauth2_cfg_endpoint_auth_options_set[i].type != NULL) {
if (strcmp(_oauth2_cfg_endpoint_auth_options_set[i].type,
type) == 0) {
rv = _oauth2_cfg_endpoint_auth_options_set[i]
.options_callback(log, auth, params);
goto end;
}
i++;
}
rv = oauth2_strdup("Invalid value, must be one of: ");
i = 0;
while (_oauth2_cfg_endpoint_auth_options_set[i].type != NULL) {
rv = oauth2_stradd(
rv,
_oauth2_cfg_endpoint_auth_options_set[i + 1].type == NULL
? " or "
: i > 0 ? ", "
: "",
_oauth2_cfg_endpoint_auth_options_set[i].type, NULL);
i++;
}
rv = oauth2_stradd(rv, ".", NULL, NULL);
end:
oauth2_debug(log, "leave: %s", rv);
return rv;
}
oauth2_cfg_endpoint_auth_type_t
oauth2_cfg_endpoint_auth_type(const oauth2_cfg_endpoint_auth_t *auth)
{
return auth ? auth->type : OAUTH2_ENDPOINT_AUTH_NONE;
}
liboauth2-1.4.4/src/cfg/cache_cfg.c 0000664 0000000 0000000 00000003320 14161030125 0016754 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/mem.h"
#include "cache_int.h"
#include "cfg_int.h"
char *oauth2_cfg_set_cache(oauth2_log_t *log, void *dummy, const char *type,
const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_cache_t *cache = NULL;
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false) {
rv = "parsing cache parameters failed";
goto end;
}
cache = _oauth2_cache_init(log, type, params);
if (cache == NULL) {
rv = oauth2_strdup(
"internal error: oauth2_cache_init returned null");
goto end;
}
if (_oauth2_cache_post_config(log, cache) == false) {
rv = oauth2_strdup(
"internal error: oauth2_cache_post_config returned false");
goto end;
}
end:
if (params)
oauth2_nv_list_free(log, params);
return rv;
}
liboauth2-1.4.4/src/cfg/cfg.c 0000664 0000000 0000000 00000014764 14161030125 0015647 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#include "cfg_int.h"
#include "util_int.h"
#include
#include
#define OAUTH2_CFG_FLAG_ON "on"
#define OAUTH2_CFG_FLAG_OFF "off"
static char *_crypto_passphrase = NULL;
const char *oauth2_crypto_passphrase_set(oauth2_log_t *log, void *dummy,
const char *passphrase)
{
if (_crypto_passphrase != NULL)
oauth2_mem_free(_crypto_passphrase);
_crypto_passphrase = oauth2_strdup(passphrase);
return NULL;
}
#define OAUTH2_CFG_DEFAULT_CRYPTO_PASSPHRASE_LEN 12
const char *oauth2_crypto_passphrase_get(oauth2_log_t *log)
{
char *p = NULL;
if (_crypto_passphrase == NULL) {
oauth2_warn(log,
"no crypto passphrase configured, generating one: "
"configure it statically to survive restarts");
p = oauth2_rand_str(log,
OAUTH2_CFG_DEFAULT_CRYPTO_PASSPHRASE_LEN);
oauth2_crypto_passphrase_set(log, NULL, p);
oauth2_mem_free(p);
}
return _crypto_passphrase;
}
const char *oauth2_cfg_set_flag_slot(void *cfg, size_t offset,
const char *value)
{
const char *rv = NULL;
oauth2_flag_t *fp = NULL;
if (cfg == NULL) {
rv = "internal error: struct is NULL";
goto end;
}
if (value == NULL)
goto end;
fp = (oauth2_flag_t *)((char *)cfg + offset);
if ((strcasecmp(value, OAUTH2_CFG_FLAG_ON) == 0) ||
(strcasecmp(value, "true") == 0) || (strcasecmp(value, "1") == 0))
*fp = (oauth2_flag_t) true;
else if ((strcasecmp(value, OAUTH2_CFG_FLAG_OFF) == 0) ||
(strcasecmp(value, "false") == 0) ||
(strcasecmp(value, "0") == 0)) {
*fp = (oauth2_flag_t) false;
} else
rv =
"value must be \"true\", \"false\", \"1\", \"0\", "
"\"" OAUTH2_CFG_FLAG_ON "\" or \"" OAUTH2_CFG_FLAG_OFF "\"";
end:
return rv;
}
static const char *_oauth2_cfg_parse_long_uint(const char *value,
long int *rvalue)
{
const char *rv = NULL;
char *endptr = NULL;
long int v = 0;
if ((value == NULL) || (rvalue == NULL)) {
rv = "internal error: value or rvalue is NULL";
goto end;
}
errno = 0;
v = strtol(value, &endptr, 10);
if (endptr == value)
rv = "strtol: no digits found";
else if ((errno == ERANGE) && (v == LONG_MIN))
rv = "strtol: underflow occurred";
else if ((errno == ERANGE) && (v == LONG_MAX))
rv = "strtol: overflow occurred";
else if (errno == EINVAL)
rv = "strtol: invalid, base contains unsupported value";
else if ((errno != 0) && (v == 0))
rv = "strtol: invalid, unspecified error occurred";
else if ((errno == 0) && (*endptr != '\0'))
rv = "strtol: valid, but additional characters remain";
else if (v < 0) {
rv = "strtol: negative value found";
} else if ((errno == 0) && (*endptr == '\0')) {
*rvalue = v;
}
end:
return rv;
}
const char *oauth2_cfg_set_uint_slot(void *cfg, size_t offset,
const char *value)
{
const char *rv = NULL;
oauth2_uint_t *fp = NULL;
long int v = 0;
if (cfg == NULL) {
rv = "internal error: struct is NULL";
goto end;
}
rv = _oauth2_cfg_parse_long_uint(value, &v);
if (rv != NULL)
goto end;
fp = (oauth2_uint_t *)((char *)cfg + offset);
*fp = (oauth2_uint_t)v;
end:
return rv;
}
const char *oauth2_cfg_set_time_slot(void *cfg, size_t offset,
const char *value)
{
const char *rv = NULL;
oauth2_time_t *fp = NULL;
long int v = 0;
if (cfg == NULL) {
rv = "internal error: struct is NULL";
goto end;
}
rv = _oauth2_cfg_parse_long_uint(value, &v);
if (rv != NULL)
goto end;
fp = (oauth2_time_t *)((char *)cfg + offset);
*fp = (oauth2_time_t)v;
end:
return rv;
}
const char *oauth2_cfg_set_str_slot(void *cfg, size_t offset, const char *value)
{
const char *rv = NULL;
char **fp = NULL;
if ((cfg == NULL) || (value == NULL)) {
rv = "internal error: struct or value is NULL";
goto end;
}
fp = (char **)((char *)cfg + offset);
*fp = oauth2_strdup(value);
if (*fp == NULL)
rv = "oauth2_strdup() in oauth2_cfg_set_str_slot failed";
end:
return rv;
}
oauth2_cfg_ctx_t *oauth2_cfg_ctx_init(oauth2_log_t *log)
{
oauth2_cfg_ctx_t *ctx =
(oauth2_cfg_ctx_t *)oauth2_mem_alloc(sizeof(oauth2_cfg_ctx_t));
ctx->ptr = NULL;
ctx->callbacks = NULL;
return ctx;
}
void oauth2_cfg_ctx_free(oauth2_log_t *log, oauth2_cfg_ctx_t *ctx)
{
if (ctx == NULL)
goto end;
if (ctx->ptr)
ctx->callbacks->free(log, ctx->ptr);
oauth2_mem_free(ctx);
end:
return;
}
oauth2_cfg_ctx_t *oauth2_cfg_ctx_clone(oauth2_log_t *log, oauth2_cfg_ctx_t *src)
{
oauth2_cfg_ctx_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_ctx_init(NULL);
dst->callbacks = src->callbacks;
if (dst->callbacks)
dst->ptr = dst->callbacks->clone(log, src->ptr);
end:
return dst;
}
char *oauth2_cfg_set_options(oauth2_log_t *log, void *cfg, const char *type,
const char *value, const char *options,
const oauth2_cfg_set_options_ctx_t *set)
{
char *rv = NULL;
int i = 0;
oauth2_nv_list_t *params = NULL;
if (cfg == NULL)
goto end;
oauth2_debug(log, "enter: type=%s, value=%s, options=%s", type, value,
options);
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
i = 0;
while (set[i].type != NULL) {
if (strcmp(set[i].type, type) == 0) {
rv = set[i].set_options_callback(log, value, params,
cfg);
goto end;
}
i++;
}
rv = oauth2_strdup("Invalid value, must be one of: ");
i = 0;
while (set[i].type != NULL) {
rv = oauth2_stradd(rv,
set[i + 1].type == NULL ? " or "
: i > 0 ? ", "
: "",
set[i].type, NULL);
i++;
}
rv = oauth2_stradd(rv, ".", NULL, NULL);
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv ? rv : "(null)");
return rv;
}
liboauth2-1.4.4/src/cfg/openidc_cfg.c 0000664 0000000 0000000 00000030435 14161030125 0017341 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#include "cfg_int.h"
#include "util_int.h"
oauth2_cfg_openidc_t *oauth2_cfg_openidc_init(oauth2_log_t *log)
{
oauth2_cfg_openidc_t *c = NULL;
c = oauth2_mem_alloc(sizeof(oauth2_cfg_openidc_t));
if (c == NULL)
goto end;
c->handler_path = NULL;
c->redirect_uri = NULL;
c->provider_resolver = NULL;
c->unauth_action = OAUTH2_UNAUTH_ACTION_UNDEFINED;
c->session = NULL;
c->client = NULL;
c->state_cookie_name_prefix = NULL;
c->state_cookie_timeout = OAUTH2_CFG_TIME_UNSET;
c->state_cookie_max = OAUTH2_CFG_UINT_UNSET;
c->state_cookie_delete_oldest = OAUTH2_CFG_FLAG_UNSET;
end:
return c;
}
oauth2_cfg_openidc_t *oauth2_cfg_openidc_clone(oauth2_log_t *log,
const oauth2_cfg_openidc_t *src)
{
oauth2_cfg_openidc_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_openidc_init(log);
if (dst == NULL)
goto end;
dst->handler_path = oauth2_strdup(src->handler_path);
dst->redirect_uri = oauth2_strdup(src->redirect_uri);
dst->provider_resolver = oauth2_cfg_openidc_provider_resolver_clone(
log, src->provider_resolver);
dst->unauth_action = src->unauth_action;
dst->session = src->session;
dst->client = oauth2_openidc_client_clone(log, src->client);
dst->state_cookie_name_prefix =
oauth2_strdup(src->state_cookie_name_prefix);
dst->state_cookie_timeout = src->state_cookie_timeout;
dst->state_cookie_max = src->state_cookie_max;
dst->state_cookie_delete_oldest = src->state_cookie_delete_oldest;
end:
return dst;
}
#define _OAUTH_CFG_MERGE_STRING(cfg, base, add, x) \
cfg->x = oauth2_strdup(add->x ? add->x : base->x);
#define _OAUTH_CFG_MERGE_VALUE(cfg, base, add, x, undefined) \
cfg->x = add->x != undefined ? add->x : base->x;
void oauth2_cfg_openidc_merge(oauth2_log_t *log, oauth2_cfg_openidc_t *cfg,
oauth2_cfg_openidc_t *base,
oauth2_cfg_openidc_t *add)
{
if ((cfg == NULL) || (base == NULL) || (add == NULL))
goto end;
_OAUTH_CFG_MERGE_STRING(cfg, base, add, handler_path);
_OAUTH_CFG_MERGE_STRING(cfg, base, add, redirect_uri);
cfg->provider_resolver =
add->provider_resolver ? oauth2_cfg_openidc_provider_resolver_clone(
log, add->provider_resolver)
: oauth2_cfg_openidc_provider_resolver_clone(
log, base->provider_resolver);
_OAUTH_CFG_MERGE_VALUE(cfg, base, add, unauth_action,
OAUTH2_UNAUTH_ACTION_UNDEFINED)
cfg->session = add->session ? add->session : base->session;
cfg->client = add->client
? oauth2_openidc_client_clone(log, add->client)
: oauth2_openidc_client_clone(log, base->client);
_OAUTH_CFG_MERGE_STRING(cfg, base, add, state_cookie_name_prefix);
_OAUTH_CFG_MERGE_VALUE(cfg, base, add, state_cookie_timeout,
OAUTH2_CFG_TIME_UNSET);
_OAUTH_CFG_MERGE_VALUE(cfg, base, add, state_cookie_max,
OAUTH2_CFG_UINT_UNSET);
_OAUTH_CFG_MERGE_VALUE(cfg, base, add, state_cookie_delete_oldest,
OAUTH2_CFG_FLAG_UNSET);
end:
return;
}
void oauth2_cfg_openidc_free(oauth2_log_t *log, oauth2_cfg_openidc_t *c)
{
if (c == NULL)
goto end;
if (c->handler_path)
oauth2_mem_free(c->handler_path);
if (c->redirect_uri)
oauth2_mem_free(c->redirect_uri);
if (c->provider_resolver)
oauth2_cfg_openidc_provider_resolver_free(log,
c->provider_resolver);
if (c->client)
oauth2_openidc_client_free(log, c->client);
if (c->state_cookie_name_prefix)
oauth2_mem_free(c->state_cookie_name_prefix);
oauth2_mem_free(c);
end:
return;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(cfg, openidc, handler_path, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(cfg, openidc, redirect_uri, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(cfg, openidc, session,
oauth2_cfg_session_t *, ptr)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(cfg, openidc, state_cookie_name_prefix,
char *, str)
#define OAUTH2_OPENIDC_STATE_COOKIE_NAME_PREFIX_DEFAULT "openidc_state_"
char *
oauth2_cfg_openidc_state_cookie_name_prefix_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg)
{
return cfg->state_cookie_name_prefix
? cfg->state_cookie_name_prefix
: OAUTH2_OPENIDC_STATE_COOKIE_NAME_PREFIX_DEFAULT;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(cfg, openidc, state_cookie_timeout,
oauth2_time_t, time)
#define OAUTH2_OPENIDC_STATE_COOKIE_TIMEOUT_DEFAULT 300
oauth2_time_t
oauth2_cfg_openidc_state_cookie_timeout_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg)
{
return cfg->state_cookie_timeout != OAUTH2_CFG_TIME_UNSET
? cfg->state_cookie_timeout
: OAUTH2_OPENIDC_STATE_COOKIE_TIMEOUT_DEFAULT;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(cfg, openidc, state_cookie_max, oauth2_uint_t,
uint)
#define OAUTH2_OPENIDC_STATE_COOKIE_MAX_DEFAULT 6
oauth2_uint_t
oauth2_cfg_openidc_state_cookie_max_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg)
{
return cfg->state_cookie_max != OAUTH2_CFG_UINT_UNSET
? cfg->state_cookie_max
: OAUTH2_OPENIDC_STATE_COOKIE_MAX_DEFAULT;
}
#define OAUTH2_OPENIDC_STATE_COOKIE_DELETE_OLDEST_DEFAULT false
oauth2_flag_t oauth2_cfg_openidc_state_cookie_delete_oldest_get(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg)
{
return (cfg->state_cookie_delete_oldest != OAUTH2_CFG_FLAG_UNSET)
? cfg->state_cookie_delete_oldest
: OAUTH2_OPENIDC_STATE_COOKIE_DELETE_OLDEST_DEFAULT;
}
bool oauth2_cfg_openidc_provider_resolver_set(
oauth2_log_t *log, oauth2_cfg_openidc_t *cfg,
oauth2_cfg_openidc_provider_resolver_t *resolver)
{
cfg->provider_resolver = resolver;
return true;
}
oauth2_cfg_openidc_provider_resolver_t *
oauth2_cfg_openidc_provider_resolver_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg)
{
return cfg ? cfg->provider_resolver : NULL;
}
#define OAUTH2_OPENIDC_CFG_HANDLER_PATH_DEFAULT "/openid-connect"
char *oauth2_cfg_openidc_handler_path_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *c)
{
return c->handler_path ? c->handler_path
: OAUTH2_OPENIDC_CFG_HANDLER_PATH_DEFAULT;
}
char *oauth2_cfg_openidc_redirect_uri_get(oauth2_log_t *log,
const oauth2_cfg_openidc_t *c,
const oauth2_http_request_t *r)
{
char *redirect_uri = NULL, *path = NULL;
if (c == NULL)
goto end;
if (c->redirect_uri) {
if (c->redirect_uri[0] != _OAUTH2_CHAR_FSLASH) {
// absolute redirect uri
redirect_uri = oauth2_strdup(c->redirect_uri);
goto end;
}
path = oauth2_strdup(c->redirect_uri);
} else {
path = oauth2_stradd(
NULL, oauth2_cfg_openidc_handler_path_get(log, c),
"/redirect_uri", NULL);
}
redirect_uri = oauth2_http_request_url_base_get(log, r);
if (redirect_uri == NULL)
goto end;
redirect_uri = oauth2_stradd(redirect_uri, path, NULL, NULL);
oauth2_debug(log, "derived absolute redirect uri: %s", redirect_uri);
end:
if (path)
oauth2_mem_free(path);
return redirect_uri;
}
char *oauth2_cfg_openidc_redirect_uri_get_iss(
oauth2_log_t *log, const oauth2_cfg_openidc_t *c,
const oauth2_http_request_t *r, const oauth2_openidc_provider_t *provider)
{
char *redirect_uri = NULL, *issuer = NULL, *sep = NULL, *value = NULL;
redirect_uri = oauth2_cfg_openidc_redirect_uri_get(log, c, r);
if (redirect_uri == NULL)
goto end;
// if (provider->issuer_specific_redirect_uri != 0) {
issuer = oauth2_openidc_provider_issuer_get(log, provider);
if (issuer)
value = oauth2_url_encode(log, issuer);
if (value == NULL)
goto end;
sep = strchr(redirect_uri, _OAUTH2_CHAR_QUERY) != NULL
? _OAUTH2_STR_AMP
: _OAUTH2_STR_QMARK;
redirect_uri =
_oauth2_stradd4(redirect_uri, sep, "iss", _OAUTH2_STR_EQUAL, value);
end:
if (value)
oauth2_mem_free(value);
return redirect_uri;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(cfg, openidc, unauth_action,
oauth2_unauth_action_t, uint)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(cfg, openidc, client,
oauth2_openidc_client_t *, ptr)
/*
* provider resolver
*/
oauth2_cfg_openidc_provider_resolver_t *
oauth2_cfg_openidc_provider_resolver_init(oauth2_log_t *log)
{
oauth2_cfg_openidc_provider_resolver_t *c = NULL;
c = oauth2_mem_alloc(sizeof(oauth2_cfg_openidc_provider_resolver_t));
if (c == NULL)
goto end;
c->cache = NULL;
c->callback = NULL;
c->ctx = oauth2_cfg_ctx_init(log);
end:
return c;
}
oauth2_cfg_openidc_provider_resolver_t *
oauth2_cfg_openidc_provider_resolver_clone(
oauth2_log_t *log, const oauth2_cfg_openidc_provider_resolver_t *src)
{
oauth2_cfg_openidc_provider_resolver_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_openidc_provider_resolver_init(log);
if (dst == NULL)
goto end;
dst->cache = src->cache;
dst->callback = src->callback;
// TODO: sort out wrt. _init...
if (dst->ctx)
oauth2_cfg_ctx_free(log, dst->ctx);
dst->ctx = oauth2_cfg_ctx_clone(log, src->ctx);
end:
return dst;
}
void oauth2_cfg_openidc_provider_resolver_free(
oauth2_log_t *log, oauth2_cfg_openidc_provider_resolver_t *c)
{
if (c == NULL)
goto end;
if (c->ctx)
oauth2_cfg_ctx_free(log, c->ctx);
oauth2_mem_free(c);
end:
return;
}
char *oauth2_cfg_openidc_set_options(oauth2_log_t *log,
oauth2_cfg_openidc_t *cfg,
const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
const char *value = NULL;
if (cfg == NULL) {
rv = oauth2_strdup("struct is null");
goto end;
}
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
value = oauth2_nv_list_get(log, params, "handler_path");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_openidc_t, handler_path), value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "redirect_uri");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_openidc_t, redirect_uri), value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "state.cookie.name.prefix");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg,
offsetof(oauth2_cfg_openidc_t, state_cookie_name_prefix),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "state.cookie.timeout");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_time_slot(
cfg, offsetof(oauth2_cfg_openidc_t, state_cookie_timeout),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "state.cookie.max");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_uint_slot(
cfg, offsetof(oauth2_cfg_openidc_t, state_cookie_max),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "state.cookie.delete.oldest");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_flag_slot(
cfg,
offsetof(oauth2_cfg_openidc_t, state_cookie_delete_oldest),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "unauth_action");
if (value) {
if (strncasecmp(value, "auth", 4) == 0)
oauth2_cfg_openidc_unauth_action_set(
log, cfg, OAUTH2_UNAUTH_ACTION_AUTHENTICATE);
else if (strncasecmp(value, "pass", 4) == 0)
oauth2_cfg_openidc_unauth_action_set(
log, cfg, OAUTH2_UNAUTH_ACTION_PASS);
else if (strncasecmp(value, "401", 3) == 0)
oauth2_cfg_openidc_unauth_action_set(
log, cfg, OAUTH2_UNAUTH_ACTION_HTTP_401);
else if (strncasecmp(value, "410", 3) == 0)
oauth2_cfg_openidc_unauth_action_set(
log, cfg, OAUTH2_UNAUTH_ACTION_HTTP_410);
else {
rv = oauth2_strdup("unknown value for unauth_action");
goto end;
}
}
end:
if (params)
oauth2_nv_list_free(log, params);
return rv;
}
liboauth2-1.4.4/src/cfg/proto_cfg.c 0000664 0000000 0000000 00000023530 14161030125 0017061 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#include "cfg_int.h"
oauth2_cfg_endpoint_t *oauth2_cfg_endpoint_init(oauth2_log_t *log)
{
oauth2_cfg_endpoint_t *endpoint = NULL;
endpoint = (oauth2_cfg_endpoint_t *)oauth2_mem_alloc(
sizeof(oauth2_cfg_endpoint_t));
if (endpoint == NULL)
goto end;
endpoint->url = NULL;
endpoint->auth = NULL;
endpoint->ssl_verify = OAUTH2_CFG_FLAG_UNSET;
endpoint->http_timeout = OAUTH2_CFG_UINT_UNSET;
endpoint->outgoing_proxy = NULL;
end:
return endpoint;
}
void oauth2_cfg_endpoint_free(oauth2_log_t *log,
oauth2_cfg_endpoint_t *endpoint)
{
if (endpoint == NULL)
goto end;
if (endpoint->url)
oauth2_mem_free(endpoint->url);
if (endpoint->auth)
oauth2_cfg_endpoint_auth_free(log, endpoint->auth);
if (endpoint->outgoing_proxy)
oauth2_mem_free(endpoint->outgoing_proxy);
oauth2_mem_free(endpoint);
end:
return;
}
oauth2_cfg_endpoint_t *
oauth2_cfg_endpoint_clone(oauth2_log_t *log, const oauth2_cfg_endpoint_t *src)
{
oauth2_cfg_endpoint_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_endpoint_init(log);
dst->url = oauth2_strdup(src->url);
dst->auth = oauth2_cfg_endpoint_auth_clone(log, src->auth);
dst->ssl_verify = src->ssl_verify;
dst->http_timeout = src->http_timeout;
dst->outgoing_proxy = oauth2_strdup(src->outgoing_proxy);
end:
return dst;
}
#define OAUTH2_CFG_ENDPOINT_SSL_VERIFY_DEFAULT 1
#define OAUTH2_CFG_ENDPOINT_HTTP_TIMEOUT_DEFAULT 20
char *oauth2_cfg_set_endpoint(oauth2_log_t *log, oauth2_cfg_endpoint_t *cfg,
const char *url, const oauth2_nv_list_t *params,
const char *prefix)
{
char *rv = NULL;
const char *value = NULL;
char *key = NULL;
if (cfg == NULL) {
rv = oauth2_strdup("struct is null");
goto end;
}
if (url == NULL) {
key = oauth2_stradd(NULL, prefix ? prefix : NULL,
prefix ? "." : NULL, "url");
value = oauth2_nv_list_get(log, params, key);
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_endpoint_t, url), value));
if (rv)
goto end;
}
oauth2_mem_free(key);
} else {
cfg->url = oauth2_strdup(url);
}
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
"auth");
value = oauth2_nv_list_get(log, params, key);
cfg->auth = oauth2_cfg_endpoint_auth_init(log);
rv = oauth2_cfg_set_endpoint_auth(log, cfg->auth, value, params, key);
if (rv != NULL)
goto end;
oauth2_mem_free(key);
// TODO: if ssl_verify == true and url is not a https URL then fail
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
"ssl_verify");
value = oauth2_nv_list_get(log, params, key);
cfg->ssl_verify = oauth2_parse_bool(log, value, true);
oauth2_mem_free(key);
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
"http_timeout");
value = oauth2_nv_list_get(log, params, key);
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_uint_slot(
cfg, offsetof(oauth2_cfg_endpoint_t, http_timeout), value));
if (rv)
goto end;
}
oauth2_mem_free(key);
key = oauth2_stradd(NULL, prefix ? prefix : NULL, prefix ? "." : NULL,
"outgoing_proxy");
value = oauth2_nv_list_get(log, params, key);
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_endpoint_t, outgoing_proxy),
value));
if (rv)
goto end;
}
oauth2_mem_free(key);
key = NULL;
end:
if (key)
oauth2_mem_free(key);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
void oauth2_cfg_endpoint_set_url(oauth2_cfg_endpoint_t *cfg, const char *url)
{
if (cfg->url)
oauth2_mem_free(cfg->url);
cfg->url = oauth2_strdup(url);
}
const char *oauth2_cfg_endpoint_get_url(const oauth2_cfg_endpoint_t *cfg)
{
return cfg ? cfg->url : NULL;
}
const oauth2_cfg_endpoint_auth_t *
oauth2_cfg_endpoint_get_auth(const oauth2_cfg_endpoint_t *cfg)
{
return cfg ? cfg->auth : NULL;
}
oauth2_flag_t
oauth2_cfg_endpoint_get_ssl_verify(const oauth2_cfg_endpoint_t *cfg)
{
if ((cfg == NULL) || (cfg->ssl_verify == OAUTH2_CFG_FLAG_UNSET))
return OAUTH2_CFG_ENDPOINT_SSL_VERIFY_DEFAULT;
return cfg->ssl_verify;
}
oauth2_uint_t
oauth2_cfg_endpoint_get_http_timeout(const oauth2_cfg_endpoint_t *cfg)
{
if ((cfg == NULL) || (cfg->http_timeout == OAUTH2_CFG_UINT_UNSET))
return OAUTH2_CFG_ENDPOINT_HTTP_TIMEOUT_DEFAULT;
return cfg->http_timeout;
}
const char *
oauth2_cfg_endpoint_get_outgoing_proxy(const oauth2_cfg_endpoint_t *cfg)
{
return cfg ? cfg->outgoing_proxy : NULL;
}
#define OAUTH2_CFG_ROPC_CLIENT_ID_DEFAULT NULL
#define OAUTH2_CFG_ROPC_USERNAME_DEFAULT NULL
#define OAUTH2_CFG_ROPC_PASSWORD_DEFAULT NULL
typedef struct oauth2_cfg_ropc_t {
oauth2_cfg_endpoint_t *token_endpoint;
char *client_id;
char *username;
char *password;
oauth2_nv_list_t *request_parameters;
} oauth2_cfg_ropc_t;
oauth2_cfg_ropc_t *oauth2_cfg_ropc_init(oauth2_log_t *log)
{
oauth2_cfg_ropc_t *ropc = NULL;
ropc = (oauth2_cfg_ropc_t *)oauth2_mem_alloc(sizeof(oauth2_cfg_ropc_t));
if (ropc == NULL)
goto end;
ropc->token_endpoint = NULL;
ropc->client_id = NULL;
ropc->username = NULL;
ropc->password = NULL;
ropc->request_parameters = NULL;
end:
return ropc;
}
void oauth2_cfg_ropc_free(oauth2_log_t *log, oauth2_cfg_ropc_t *ropc)
{
if (ropc == NULL)
goto end;
if (ropc->token_endpoint)
oauth2_cfg_endpoint_free(log, ropc->token_endpoint);
if (ropc->client_id)
oauth2_mem_free(ropc->client_id);
if (ropc->username)
oauth2_mem_free(ropc->username);
if (ropc->password)
oauth2_mem_free(ropc->password);
if (ropc->request_parameters)
oauth2_nv_list_free(log, ropc->request_parameters);
oauth2_mem_free(ropc);
end:
return;
}
void oauth2_cfg_ropc_merge(oauth2_log_t *log, oauth2_cfg_ropc_t *dst,
oauth2_cfg_ropc_t *base, oauth2_cfg_ropc_t *add)
{
oauth2_cfg_ropc_t *src = (add && add->token_endpoint != 0) ? add
: base ? base
: NULL;
if ((src == NULL) || (dst == NULL))
goto end;
dst->token_endpoint =
oauth2_cfg_endpoint_clone(log, src->token_endpoint);
dst->client_id = oauth2_strdup(src->client_id);
dst->username = oauth2_strdup(src->username);
dst->password = oauth2_strdup(src->password);
dst->request_parameters =
oauth2_nv_list_clone(log, src->request_parameters);
end:
return;
}
oauth2_cfg_ropc_t *oauth2_cfg_ropc_clone(oauth2_log_t *log,
const oauth2_cfg_ropc_t *src)
{
oauth2_cfg_ropc_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_ropc_init(log);
dst->token_endpoint =
oauth2_cfg_endpoint_clone(log, src->token_endpoint);
dst->client_id = oauth2_strdup(src->client_id);
dst->username = oauth2_strdup(src->username);
dst->password = oauth2_strdup(src->password);
dst->request_parameters =
oauth2_nv_list_clone(log, src->request_parameters);
end:
return dst;
}
char *oauth2_cfg_set_ropc(oauth2_log_t *log, oauth2_cfg_ropc_t *cfg,
const char *url, const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
const char *value = NULL;
if (cfg == NULL) {
rv = oauth2_strdup("struct is null");
goto end;
}
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
cfg->token_endpoint = oauth2_cfg_endpoint_init(log);
rv = oauth2_cfg_set_endpoint(log, cfg->token_endpoint, url, params,
NULL);
if (rv)
goto end;
value = oauth2_nv_list_get(log, params, "client_id");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_ropc_t, client_id), value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "username");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_ropc_t, username), value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "password");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_ropc_t, password), value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "params");
if (value) {
if (oauth2_parse_form_encoded_params(
log, value, &cfg->request_parameters) == false) {
rv =
oauth2_strdup("could not parse request parameters");
goto end;
}
}
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
const oauth2_cfg_endpoint_t *
oauth2_cfg_ropc_get_token_endpoint(oauth2_cfg_ropc_t *cfg)
{
return cfg ? cfg->token_endpoint : NULL;
}
const char *oauth2_cfg_ropc_get_client_id(oauth2_cfg_ropc_t *cfg)
{
if ((cfg == NULL) || (cfg->client_id == NULL))
return OAUTH2_CFG_ROPC_CLIENT_ID_DEFAULT;
return cfg->client_id;
}
const oauth2_nv_list_t *
oauth2_cfg_ropc_get_request_parameters(oauth2_cfg_ropc_t *cfg)
{
return cfg->request_parameters;
}
const char *oauth2_cfg_ropc_get_username(oauth2_cfg_ropc_t *cfg)
{
if ((cfg == NULL) || (cfg->username == NULL))
return OAUTH2_CFG_ROPC_USERNAME_DEFAULT;
return cfg->username;
}
const char *oauth2_cfg_ropc_get_password(oauth2_cfg_ropc_t *cfg)
{
if ((cfg == NULL) || (cfg->password == NULL))
return OAUTH2_CFG_ROPC_PASSWORD_DEFAULT;
return cfg->password;
}
liboauth2-1.4.4/src/cfg/session_cfg.c 0000664 0000000 0000000 00000016767 14161030125 0017417 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/ipc.h"
#include "oauth2/mem.h"
#include "cache_int.h"
#include "cfg_int.h"
#include "util_int.h"
_OAUTH2_CFG_GLOBAL_LIST(session, oauth2_cfg_session_t)
#define OAUTH2_SESSION_TYPE_COOKIE_STR "cookie"
#define OAUTH2_SESSION_TYPE_CACHE_STR "cache"
oauth2_cfg_session_t *oauth2_cfg_session_init(oauth2_log_t *log)
{
oauth2_cfg_session_t *session = NULL;
session = (oauth2_cfg_session_t *)oauth2_mem_alloc(
sizeof(oauth2_cfg_session_t));
session->type = OAUTH2_CFG_UINT_UNSET;
session->cookie_name = NULL;
session->cookie_path = NULL;
session->inactivity_timeout_s = OAUTH2_CFG_TIME_UNSET;
session->max_duration_s = OAUTH2_CFG_TIME_UNSET;
session->cache = NULL;
session->load_callback = NULL;
session->save_callback = NULL;
return session;
}
/*
oauth2_cfg_session_t *oauth2_cfg_session_clone(oauth2_log_t *log,
oauth2_cfg_session_t *src)
{
oauth2_cfg_session_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_session_init(log);
dst->type = src->type;
dst->cookie_name = oauth2_strdup(src->cookie_name);
dst->cookie_path = oauth2_strdup(src->cookie_path);
dst->inactivity_timeout_s = src->inactivity_timeout_s;
dst->max_duration_s = src->max_duration_s;
dst->cache = src->cache;
dst->load_callback = src->load_callback;
dst->save_callback = src->save_callback;
end:
return dst;
}
*/
// void oauth2_cfg_session_merge(
// oauth2_log_t *log, oauth2_cfg_session_t *cfg,
// oauth2_cfg_session_t *add,
// oauth2_cfg_session_t *base) {
//}
void oauth2_cfg_session_free(oauth2_log_t *log, oauth2_cfg_session_t *session)
{
if (session->cookie_name)
oauth2_mem_free(session->cookie_name);
if (session->cookie_path)
oauth2_mem_free(session->cookie_path);
oauth2_mem_free(session);
}
oauth2_cfg_session_t *_oauth2_cfg_session_obtain(oauth2_log_t *log,
const char *name)
{
oauth2_cfg_session_t *cfg = NULL;
oauth2_debug(log, "enter: %s", name);
if (_M_session_list_empty(log)) {
cfg = oauth2_cfg_session_init(log);
if (cfg == NULL)
goto end;
if (oauth2_cfg_session_set_options(
log, cfg, OAUTH2_SESSION_TYPE_CACHE_STR, NULL) !=
NULL) {
cfg = NULL;
goto end;
}
}
cfg = _M_session_list_get(log, name);
end:
oauth2_debug(log, "leave: %p", cfg);
return cfg;
}
void _oauth2_session_global_cleanup(oauth2_log_t *log)
{
oauth2_debug(log, "enter");
_M_session_list_release(log);
oauth2_debug(log, "leave");
}
#define OAUTH2_INACTIVITY_TIMEOUT_S_DEFAULT 60 * 5
oauth2_time_t
oauth2_cfg_session_inactivity_timeout_s_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
if ((cfg == NULL) ||
(cfg->inactivity_timeout_s == OAUTH2_CFG_TIME_UNSET))
return OAUTH2_INACTIVITY_TIMEOUT_S_DEFAULT;
return cfg->inactivity_timeout_s;
}
#define OAUTH2_SESSION_MAX_DURATION_S_DEFAULT 60 * 60 * 8
oauth2_time_t
oauth2_cfg_session_max_duration_s_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
if ((cfg == NULL) || (cfg->max_duration_s == OAUTH2_CFG_TIME_UNSET))
return OAUTH2_SESSION_MAX_DURATION_S_DEFAULT;
return cfg->max_duration_s;
}
#define OAUTH2_SESSION_COOKIE_NAME_DEFAULT "openidc_session"
char *oauth2_cfg_session_cookie_name_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
if ((cfg == NULL) || (cfg->cookie_name == NULL))
return OAUTH2_SESSION_COOKIE_NAME_DEFAULT;
return cfg->cookie_name;
}
#define OAUTH2_SESSION_COOKIE_PATH_DEFAULT "/"
char *oauth2_cfg_session_cookie_path_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
if ((cfg == NULL) || (cfg->cookie_path == NULL))
return OAUTH2_SESSION_COOKIE_PATH_DEFAULT;
return cfg->cookie_path;
}
oauth2_session_load_callback_t *
oauth2_cfg_session_load_callback_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
if ((cfg == NULL) || (cfg->load_callback == NULL))
return oauth2_session_load_cookie;
return cfg->load_callback;
}
oauth2_session_save_callback_t *
oauth2_cfg_session_save_callback_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
if ((cfg == NULL) || (cfg->save_callback == NULL))
return oauth2_session_save_cookie;
return cfg->save_callback;
}
oauth2_cache_t *oauth2_cfg_session_cache_get(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg)
{
return cfg->cache;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_cfg_session_set_options_cookie)
{
oauth2_cfg_session_t *cfg = (oauth2_cfg_session_t *)ctx;
char *rv = NULL;
oauth2_debug(log, "enter");
cfg->type = OAUTH2_SESSION_TYPE_COOKIE;
cfg->load_callback = oauth2_session_load_cookie;
cfg->save_callback = oauth2_session_save_cookie;
oauth2_debug(log, "leave: %s", rv);
return rv;
}
#define OAUTH2_CFG_SESSION_CACHE_DEFAULT OAUTH2_SESSION_EXPIRY_S_DEFAULT
_OAUTH_CFG_CTX_CALLBACK(oauth2_cfg_session_set_options_cache)
{
oauth2_cfg_session_t *cfg = (oauth2_cfg_session_t *)ctx;
char *rv = NULL;
oauth2_debug(log, "enter");
cfg->type = OAUTH2_SESSION_TYPE_CACHE;
cfg->load_callback = oauth2_session_load_cache;
cfg->save_callback = oauth2_session_save_cache;
cfg->cache =
oauth2_cache_obtain(log, oauth2_nv_list_get(log, params, "cache"));
oauth2_debug(log, "leave: %s", rv);
return rv;
}
// clang-format off
static oauth2_cfg_set_options_ctx_t _oauth2_cfg_session_options_set[] = {
{ OAUTH2_SESSION_TYPE_COOKIE_STR, oauth2_cfg_session_set_options_cookie },
{ OAUTH2_SESSION_TYPE_CACHE_STR, oauth2_cfg_session_set_options_cache },
{ NULL, NULL }
};
// clang-format on
char *oauth2_cfg_session_set_options(oauth2_log_t *log,
oauth2_cfg_session_t *cfg,
const char *type, const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
const char *value = NULL;
if (cfg == NULL)
// global
cfg = oauth2_cfg_session_init(log);
rv = oauth2_cfg_set_options(log, cfg, type, NULL, options,
_oauth2_cfg_session_options_set);
if (rv != NULL)
goto end;
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
value = oauth2_nv_list_get(log, params, "cookie.name");
if (value)
cfg->cookie_name = oauth2_strdup(value);
value = oauth2_nv_list_get(log, params, "cookie.path");
if (value)
cfg->cookie_path = oauth2_strdup(value);
value = oauth2_nv_list_get(log, params, "max_duration");
if (value)
cfg->max_duration_s =
oauth2_parse_time_sec(log, value, OAUTH2_CFG_TIME_UNSET);
value = oauth2_nv_list_get(log, params, "inactivity_timeout");
if (value)
cfg->inactivity_timeout_s =
oauth2_parse_time_sec(log, value, OAUTH2_CFG_TIME_UNSET);
_M_session_list_register(log, oauth2_nv_list_get(log, params, "name"),
cfg, oauth2_cfg_session_free);
end:
if (params)
oauth2_nv_list_free(log, params);
return rv;
}
liboauth2-1.4.4/src/cfg/source.c 0000664 0000000 0000000 00000023221 14161030125 0016374 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#include "cfg_int.h"
#define OAUTH2_CFG_SOURCE_TOKEN_STRIP_DEFAULT (oauth2_flag_t) true
#define OAUTH2_CFG_SOURCE_TOKEN_ACCEPT_IN_DEFAULT \
(OAUTH2_CFG_TOKEN_IN_ENVVAR | OAUTH2_CFG_TOKEN_IN_HEADER)
static char *
oauth2_cfg_accept_in_envvar_options_set(oauth2_log_t *log,
oauth2_cfg_token_in_t *accept_in,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
accept_in->envvar.name =
oauth2_strdup(oauth2_nv_list_get(log, params, "name"));
return rv;
}
static char *
oauth2_cfg_accept_in_header_options_set(oauth2_log_t *log,
oauth2_cfg_token_in_t *accept_in,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
accept_in->header.name =
oauth2_strdup(oauth2_nv_list_get(log, params, "name"));
accept_in->header.type =
oauth2_strdup(oauth2_nv_list_get(log, params, "type"));
return rv;
}
static char *
oauth2_cfg_accept_in_query_options_set(oauth2_log_t *log,
oauth2_cfg_token_in_t *accept_in,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
accept_in->query.param_name =
oauth2_strdup(oauth2_nv_list_get(log, params, "name"));
return rv;
}
static char *
oauth2_cfg_accept_in_post_options_set(oauth2_log_t *log,
oauth2_cfg_token_in_t *accept_in,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
accept_in->post.param_name =
oauth2_strdup(oauth2_nv_list_get(log, params, "name"));
return rv;
}
static char *
oauth2_cfg_accept_in_cookie_options_set(oauth2_log_t *log,
oauth2_cfg_token_in_t *accept_in,
const oauth2_nv_list_t *params)
{
char *rv = NULL;
accept_in->cookie.name =
oauth2_strdup(oauth2_nv_list_get(log, params, "name"));
return rv;
}
typedef char *(oauth2_cfg_accept_token_in_set_options_cb_t)(
oauth2_log_t *log, oauth2_cfg_token_in_t *accept_in,
const oauth2_nv_list_t *params);
typedef struct oauth2_cfg_accept_token_in_set_options_ctx_t {
const char *method;
oauth2_cfg_token_in_type_t type;
oauth2_cfg_accept_token_in_set_options_cb_t *options_callback;
} oauth2_cfg_accept_token_in_set_options_ctx_t;
// clang-format off
static oauth2_cfg_accept_token_in_set_options_ctx_t _oauth2_cfg_accept_in_options_set[] = {
{ OAUTH2_CFG_TOKEN_IN_ENVVAR_STR, OAUTH2_CFG_TOKEN_IN_ENVVAR, oauth2_cfg_accept_in_envvar_options_set },
{ OAUTH2_CFG_TOKEN_IN_HEADER_STR, OAUTH2_CFG_TOKEN_IN_HEADER, oauth2_cfg_accept_in_header_options_set },
{ OAUTH2_CFG_TOKEN_IN_QUERY_STR, OAUTH2_CFG_TOKEN_IN_QUERY, oauth2_cfg_accept_in_query_options_set },
{ OAUTH2_CFG_TOKEN_IN_POST_STR, OAUTH2_CFG_TOKEN_IN_POST, oauth2_cfg_accept_in_post_options_set },
{ OAUTH2_CFG_TOKEN_IN_COOKIE_STR, OAUTH2_CFG_TOKEN_IN_COOKIE, oauth2_cfg_accept_in_cookie_options_set },
{ OAUTH2_CFG_TOKEN_IN_BASIC_STR, OAUTH2_CFG_TOKEN_IN_BASIC, NULL },
{ NULL, 0, NULL }
};
// clang-format on
char *oauth2_cfg_token_in_set(oauth2_log_t *log, oauth2_cfg_token_in_t *cfg,
const char *method,
const oauth2_nv_list_t *params,
oauth2_uint_t allowed)
{
char *rv = NULL;
int i = 0;
if (method == NULL) {
rv = oauth2_strdup("Invalid value, method must be set");
goto end;
}
i = 0;
while (_oauth2_cfg_accept_in_options_set[i].method != NULL) {
if ((strcmp(_oauth2_cfg_accept_in_options_set[i].method,
method) == 0) &&
(allowed & _oauth2_cfg_accept_in_options_set[i].type)) {
cfg->value |= _oauth2_cfg_accept_in_options_set[i].type;
if (_oauth2_cfg_accept_in_options_set[i]
.options_callback)
rv = _oauth2_cfg_accept_in_options_set[i]
.options_callback(log, cfg, params);
goto end;
}
i++;
}
rv = oauth2_strdup("Invalid value, must be one of: ");
i = 0;
while (_oauth2_cfg_accept_in_options_set[i].method != NULL) {
rv = oauth2_stradd(
rv,
_oauth2_cfg_accept_in_options_set[i + 1].method == NULL
? " or "
: i > 0 ? ", "
: "",
_oauth2_cfg_accept_in_options_set[i].method, NULL);
i++;
}
rv = oauth2_stradd(rv, ".", NULL, NULL);
end:
oauth2_debug(log, "leave: %s", rv);
return rv;
}
#define OAUTH2_CFG_SET_TAKE1_IMPL(ctype, name, member, type) \
const char *oauth2_cfg_set_##name##_##member( \
oauth2_log_t *log, ctype *cfg, const char *value) \
{ \
return oauth2_cfg_set_##type##_slot( \
cfg, offsetof(ctype, member), value); \
}
OAUTH2_CFG_SET_TAKE1_IMPL(oauth2_cfg_source_token_t, source_token, strip, flag)
oauth2_flag_t oauth2_cfg_source_token_get_strip(oauth2_cfg_source_token_t *cfg)
{
if (cfg->strip == OAUTH2_CFG_FLAG_UNSET)
return OAUTH2_CFG_SOURCE_TOKEN_STRIP_DEFAULT;
return cfg->strip;
}
char *oauth2_cfg_source_token_set_accept_in(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg,
const char *method,
const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
static oauth2_uint_t allowed =
OAUTH2_CFG_TOKEN_IN_ENVVAR | OAUTH2_CFG_TOKEN_IN_HEADER |
OAUTH2_CFG_TOKEN_IN_QUERY | OAUTH2_CFG_TOKEN_IN_POST |
OAUTH2_CFG_TOKEN_IN_COOKIE | OAUTH2_CFG_TOKEN_IN_BASIC;
const char *strip = NULL;
if (cfg == NULL) {
rv = oauth2_strdup("struct is null");
goto end;
}
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
rv = oauth2_cfg_token_in_set(log, &cfg->accept_in, method, params,
allowed);
if (rv != NULL)
goto end;
strip = oauth2_nv_list_get(log, params, "strip");
if (strip == NULL)
goto end;
rv = oauth2_strdup(oauth2_cfg_set_source_token_strip(log, cfg, strip));
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
char oauth2_cfg_source_token_get_accept_in(oauth2_cfg_source_token_t *cfg)
{
if (cfg->accept_in.value == 0)
return OAUTH2_CFG_SOURCE_TOKEN_ACCEPT_IN_DEFAULT;
return cfg->accept_in.value;
}
oauth2_cfg_source_token_t *oauth2_cfg_source_token_init(oauth2_log_t *log)
{
oauth2_cfg_source_token_t *cfg =
(oauth2_cfg_source_token_t *)oauth2_mem_alloc(
sizeof(oauth2_cfg_source_token_t));
cfg->accept_in.value = 0;
cfg->accept_in.query.param_name = NULL;
cfg->accept_in.post.param_name = NULL;
cfg->accept_in.cookie.name = NULL;
cfg->accept_in.envvar.name = NULL;
cfg->accept_in.header.name = NULL;
cfg->accept_in.header.type = NULL;
cfg->strip = OAUTH2_CFG_FLAG_UNSET;
// cfg->encryption_keys = NULL;
return cfg;
}
void oauth2_cfg_source_token_free(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg)
{
if (cfg == NULL)
goto end;
if (cfg->accept_in.query.param_name)
oauth2_mem_free(cfg->accept_in.query.param_name);
if (cfg->accept_in.post.param_name)
oauth2_mem_free(cfg->accept_in.post.param_name);
if (cfg->accept_in.cookie.name)
oauth2_mem_free(cfg->accept_in.cookie.name);
if (cfg->accept_in.envvar.name)
oauth2_mem_free(cfg->accept_in.envvar.name);
if (cfg->accept_in.header.name)
oauth2_mem_free(cfg->accept_in.header.name);
if (cfg->accept_in.header.type)
oauth2_mem_free(cfg->accept_in.header.type);
oauth2_mem_free(cfg);
end:
return;
}
oauth2_cfg_source_token_t *
oauth2_cfg_source_token_clone(oauth2_log_t *log,
const oauth2_cfg_source_token_t *src)
{
oauth2_cfg_source_token_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_source_token_init(log);
dst->accept_in.value = src->accept_in.value;
dst->accept_in.query.param_name =
oauth2_strdup(src->accept_in.query.param_name);
dst->accept_in.post.param_name =
oauth2_strdup(src->accept_in.post.param_name);
dst->accept_in.cookie.name = oauth2_strdup(src->accept_in.cookie.name);
dst->accept_in.envvar.name = oauth2_strdup(src->accept_in.envvar.name);
dst->accept_in.header.name = oauth2_strdup(src->accept_in.header.name);
dst->accept_in.header.type = oauth2_strdup(src->accept_in.header.type);
dst->strip = src->strip;
end:
return dst;
}
void oauth2_cfg_source_token_merge(oauth2_log_t *log,
oauth2_cfg_source_token_t *dst,
oauth2_cfg_source_token_t *base,
oauth2_cfg_source_token_t *add)
{
oauth2_cfg_source_token_t *src = (add && add->accept_in.value != 0)
? add
: base ? base
: NULL;
if (src == NULL)
goto end;
dst->accept_in.value = src->accept_in.value;
dst->accept_in.query.param_name =
oauth2_strdup(src->accept_in.query.param_name);
dst->accept_in.post.param_name =
oauth2_strdup(src->accept_in.post.param_name);
dst->accept_in.cookie.name = oauth2_strdup(src->accept_in.cookie.name);
dst->accept_in.envvar.name = oauth2_strdup(src->accept_in.envvar.name);
dst->accept_in.header.name = oauth2_strdup(src->accept_in.header.name);
dst->accept_in.header.type = oauth2_strdup(src->accept_in.header.type);
dst->strip = src->strip;
end:
return;
}
liboauth2-1.4.4/src/cfg/target.c 0000664 0000000 0000000 00000013061 14161030125 0016363 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#define OAUTH2_CFG_PASS_TARGET_AS_ENVVARS_DEFAULT true
#define OAUTH2_CFG_PASS_TARGET_AS_HEADERS_DEFAULT true
#define OAUTH2_CFG_PASS_TARGET_PREFIX_DEFAULT "OAUTH2_CLAIM_"
#define OAUTH2_CFG_PASS_TARGET_AUTHN_HEADER_DEFAULT NULL
#define OAUTH2_CFG_PASS_TARGET_REMOTE_USER_CLAIM_DEFAULT "sub"
typedef struct oauth2_cfg_target_pass_t {
oauth2_flag_t as_envvars;
oauth2_flag_t as_headers;
char *authn_header;
char *prefix;
char *remote_user_claim;
} oauth2_cfg_target_pass_t;
oauth2_cfg_target_pass_t *oauth2_cfg_target_pass_init(oauth2_log_t *log)
{
oauth2_cfg_target_pass_t *pass = NULL;
pass = (oauth2_cfg_target_pass_t *)oauth2_mem_alloc(
sizeof(oauth2_cfg_target_pass_t));
if (pass == NULL)
goto end;
pass->as_envvars = OAUTH2_CFG_FLAG_UNSET;
pass->as_headers = OAUTH2_CFG_FLAG_UNSET;
pass->authn_header = NULL;
pass->prefix = NULL;
pass->remote_user_claim = NULL;
end:
return pass;
}
void oauth2_cfg_target_pass_free(oauth2_log_t *log,
oauth2_cfg_target_pass_t *pass)
{
if (pass == NULL)
goto end;
if (pass->authn_header)
oauth2_mem_free(pass->authn_header);
if (pass->prefix)
oauth2_mem_free(pass->prefix);
if (pass->remote_user_claim)
oauth2_mem_free(pass->remote_user_claim);
oauth2_mem_free(pass);
end:
return;
}
void oauth2_cfg_target_pass_merge(oauth2_log_t *log,
oauth2_cfg_target_pass_t *cfg,
oauth2_cfg_target_pass_t *base,
oauth2_cfg_target_pass_t *add)
{
if ((cfg == NULL) || (base == NULL) || (add == NULL))
goto end;
cfg->as_envvars = add->as_envvars != OAUTH2_CFG_FLAG_UNSET
? add->as_envvars
: base->as_envvars;
cfg->as_headers = add->as_headers != OAUTH2_CFG_FLAG_UNSET
? add->as_headers
: base->as_headers;
cfg->authn_header = oauth2_strdup(
add->authn_header != NULL ? add->authn_header : base->authn_header);
cfg->prefix =
oauth2_strdup(add->prefix != NULL ? add->prefix : base->prefix);
cfg->remote_user_claim = oauth2_strdup(add->remote_user_claim != NULL
? add->remote_user_claim
: base->remote_user_claim);
end:
return;
}
char *oauth2_cfg_set_target_pass_options(oauth2_log_t *log,
oauth2_cfg_target_pass_t *cfg,
const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
const char *value = NULL;
if (cfg == NULL) {
rv = oauth2_strdup("struct is null");
goto end;
}
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
value = oauth2_nv_list_get(log, params, "envvars");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_flag_slot(
cfg, offsetof(oauth2_cfg_target_pass_t, as_envvars),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "headers");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_flag_slot(
cfg, offsetof(oauth2_cfg_target_pass_t, as_headers),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "authn_header");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_target_pass_t, authn_header),
value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "prefix");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_target_pass_t, prefix), value));
if (rv)
goto end;
}
value = oauth2_nv_list_get(log, params, "remote_user_claim");
if (value) {
rv = oauth2_strdup(oauth2_cfg_set_str_slot(
cfg, offsetof(oauth2_cfg_target_pass_t, remote_user_claim),
value));
if (rv)
goto end;
}
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
oauth2_flag_t
oauth2_cfg_target_pass_get_as_envvars(oauth2_cfg_target_pass_t *cfg)
{
if (cfg->as_headers == OAUTH2_CFG_FLAG_UNSET)
return OAUTH2_CFG_PASS_TARGET_AS_ENVVARS_DEFAULT;
return cfg->as_envvars;
}
oauth2_flag_t
oauth2_cfg_target_pass_get_as_headers(oauth2_cfg_target_pass_t *cfg)
{
if (cfg->as_headers == OAUTH2_CFG_FLAG_UNSET)
return OAUTH2_CFG_PASS_TARGET_AS_HEADERS_DEFAULT;
return cfg->as_headers;
}
const char *oauth2_cfg_target_pass_get_prefix(oauth2_cfg_target_pass_t *cfg)
{
if (cfg->prefix == NULL)
return OAUTH2_CFG_PASS_TARGET_PREFIX_DEFAULT;
return cfg->prefix;
}
const char *
oauth2_cfg_target_pass_get_authn_header(oauth2_cfg_target_pass_t *cfg)
{
if (cfg->authn_header == NULL)
return OAUTH2_CFG_PASS_TARGET_AUTHN_HEADER_DEFAULT;
return cfg->authn_header;
}
const char *
oauth2_cfg_target_get_remote_user_claim(oauth2_cfg_target_pass_t *cfg)
{
if (cfg->remote_user_claim == NULL)
return OAUTH2_CFG_PASS_TARGET_REMOTE_USER_CLAIM_DEFAULT;
return cfg->remote_user_claim;
}
liboauth2-1.4.4/src/cfg/verify.c 0000664 0000000 0000000 00000021654 14161030125 0016410 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "cache_int.h"
#include "cfg_int.h"
#include "jose_int.h"
#include "oauth2_int.h"
#define OAUTH2_JOSE_VERIFY_JWK_PLAIN_STR "plain"
#define OAUTH2_JOSE_VERIFY_JWK_BASE64_STR "base64"
#define OAUTH2_JOSE_VERIFY_JWK_BASE64URL_STR "base64url"
#define OAUTH2_JOSE_VERIFY_JWK_HEX_STR "hex"
#define OAUTH2_JOSE_VERIFY_JWK_PEM_STR "pem"
#define OAUTH2_JOSE_VERIFY_JWK_PUBKEY_STR "pubkey"
#define OAUTH2_JOSE_VERIFY_JWK_JWK_STR "jwk"
#define OAUTH2_JOSE_VERIFY_JWK_JWKS_URI_STR "jwks_uri"
#define OAUTH2_JOSE_VERIFY_JWK_ECKEY_URI_STR "eckey_uri"
#define OAUTH2_CFG_VERIFY_INTROSPECT_URL_STR "introspect"
#define OAUTH2_CFG_VERIFY_METADATA_URL_STR "metadata"
// clang-format off
static oauth2_cfg_set_options_ctx_t _oauth2_cfg_verify_options_set[] = {
{ OAUTH2_JOSE_VERIFY_JWK_PLAIN_STR, oauth2_jose_verify_options_jwk_set_plain },
{ OAUTH2_JOSE_VERIFY_JWK_BASE64_STR, oauth2_jose_verify_options_jwk_set_base64 },
{ OAUTH2_JOSE_VERIFY_JWK_BASE64URL_STR, oauth2_jose_verify_options_jwk_set_base64url },
{ OAUTH2_JOSE_VERIFY_JWK_HEX_STR, oauth2_jose_verify_options_jwk_set_hex },
{ OAUTH2_JOSE_VERIFY_JWK_PEM_STR, oauth2_jose_verify_options_jwk_set_pem },
{ OAUTH2_JOSE_VERIFY_JWK_PUBKEY_STR, oauth2_jose_verify_options_jwk_set_pubkey },
{ OAUTH2_JOSE_VERIFY_JWK_JWK_STR, oauth2_jose_verify_options_jwk_set_jwk },
{ OAUTH2_JOSE_VERIFY_JWK_JWKS_URI_STR, oauth2_jose_verify_options_jwk_set_jwks_uri },
{ OAUTH2_JOSE_VERIFY_JWK_ECKEY_URI_STR, oauth2_jose_verify_options_jwk_set_eckey_uri },
{ OAUTH2_CFG_VERIFY_INTROSPECT_URL_STR, oauth2_verify_options_set_introspect_url },
{ OAUTH2_CFG_VERIFY_METADATA_URL_STR, oauth2_verify_options_set_metadata_url },
{ NULL, NULL }
};
// clang-format on
oauth2_cfg_token_verify_t *oauth2_cfg_token_verify_init(oauth2_log_t *log)
{
oauth2_cfg_token_verify_t *verify =
(oauth2_cfg_token_verify_t *)oauth2_mem_alloc(
sizeof(oauth2_cfg_token_verify_t));
verify->ctx = NULL;
verify->callback = NULL;
verify->cache = NULL;
verify->type = OAUTH2_CFG_UINT_UNSET;
verify->dpop.cache = NULL;
verify->dpop.expiry_s = OAUTH2_CFG_UINT_UNSET;
verify->dpop.iat_validate = OAUTH2_CFG_UINT_UNSET;
verify->dpop.iat_slack_after = OAUTH2_CFG_UINT_UNSET;
verify->dpop.iat_slack_before = OAUTH2_CFG_UINT_UNSET;
verify->expiry_s = OAUTH2_CFG_UINT_UNSET;
verify->mtls.env_var_name = NULL;
verify->mtls.policy = OAUTH2_CFG_UINT_UNSET;
verify->next = NULL;
return verify;
}
void oauth2_cfg_token_verify_free(oauth2_log_t *log,
oauth2_cfg_token_verify_t *verify)
{
oauth2_cfg_token_verify_t *ptr = verify;
while (ptr) {
verify = verify->next;
if (ptr->mtls.env_var_name != NULL)
oauth2_mem_free(ptr->mtls.env_var_name);
if (ptr->ctx)
oauth2_cfg_ctx_free(log, ptr->ctx);
oauth2_mem_free(ptr);
ptr = verify;
}
}
oauth2_cfg_token_verify_t *
oauth2_cfg_token_verify_clone(oauth2_log_t *log,
const oauth2_cfg_token_verify_t *src)
{
oauth2_cfg_token_verify_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_cfg_token_verify_init(NULL);
dst->cache = src->cache;
dst->expiry_s = src->expiry_s;
dst->callback = src->callback;
dst->type = src->type;
dst->dpop.cache = src->dpop.cache;
dst->dpop.expiry_s = src->dpop.expiry_s;
dst->dpop.iat_slack_after = src->dpop.iat_slack_after;
dst->dpop.iat_slack_before = src->dpop.iat_slack_before;
dst->dpop.iat_validate = src->dpop.iat_validate;
dst->mtls.env_var_name = oauth2_strdup(src->mtls.env_var_name);
dst->mtls.policy = src->mtls.policy;
dst->ctx = oauth2_cfg_ctx_clone(log, src->ctx);
dst->next = oauth2_cfg_token_verify_clone(NULL, src->next);
end:
return dst;
}
static oauth2_cfg_token_verify_t *
_oauth2_cfg_token_verify_add(oauth2_log_t *log,
oauth2_cfg_token_verify_t **verify)
{
oauth2_cfg_token_verify_t *v = NULL, *last = NULL;
if (verify == NULL)
goto end;
v = oauth2_cfg_token_verify_init(log);
if (v == NULL)
goto end;
v->cache = NULL;
v->callback = NULL;
v->ctx = oauth2_cfg_ctx_init(log);
if (v->ctx == NULL)
goto end;
if (*verify == NULL) {
*verify = v;
goto end;
}
for (last = *verify; last->next; last = last->next)
;
last->next = v;
end:
return v;
}
static char *
_oauth2_cfg_token_verify_type_set(oauth2_log_t *log,
oauth2_cfg_token_verify_t *verify,
oauth2_nv_list_t *params)
{
char *rv = NULL;
const char *v = NULL;
v = oauth2_nv_list_get(log, params, "type");
if (v == NULL)
goto end;
if (strcasecmp(v, OAUTH2_TOKEN_VERIFY_BEARER_STR) == 0) {
verify->type = OAUTH2_TOKEN_VERIFY_BEARER;
goto end;
}
if (strcasecmp(v, OAUTH2_TOKEN_VERIFY_DPOP_STR) == 0) {
verify->type = OAUTH2_TOKEN_VERIFY_DPOP;
goto end;
}
if (strcasecmp(v, OAUTH2_TOKEN_VERIFY_MTLS_STR) == 0) {
verify->type = OAUTH2_TOKEN_VERIFY_MTLS;
goto end;
}
rv = oauth2_strdup("Invalid value, must be one of: \"");
rv = oauth2_stradd(rv, OAUTH2_TOKEN_VERIFY_BEARER_STR, "\", \"", NULL);
rv = oauth2_stradd(rv, OAUTH2_TOKEN_VERIFY_DPOP_STR, "\" or \"", NULL);
rv = oauth2_stradd(rv, OAUTH2_TOKEN_VERIFY_MTLS_STR, "\".", NULL);
end:
return rv;
}
#define OAUTH2_CFG_VERIFY_DPOP_CACHE_DEFAULT 10
#define OAUTH2_VERIFY_DPOP_SLACK_DEFAULT (oauth2_uint_t)5
static char *
_oauth2_cfg_token_verify_options_dpop_set(oauth2_log_t *log,
oauth2_cfg_token_verify_t *verify,
oauth2_nv_list_t *params)
{
char *rv = NULL;
verify->dpop.cache = oauth2_cache_obtain(
log, oauth2_nv_list_get(log, params, "dpop.cache"));
verify->dpop.expiry_s = oauth2_parse_uint(
log, oauth2_nv_list_get(log, params, "dpop.expiry"),
OAUTH2_CFG_VERIFY_DPOP_CACHE_DEFAULT);
verify->dpop.iat_validate = oauth2_parse_validate_claim_option(
log, oauth2_nv_list_get(log, params, "dpop.iat.verify"),
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
verify->dpop.iat_slack_before = oauth2_parse_uint(
log, oauth2_nv_list_get(log, params, "dpop.iat.slack.before"),
OAUTH2_VERIFY_DPOP_SLACK_DEFAULT);
verify->dpop.iat_slack_after = oauth2_parse_uint(
log, oauth2_nv_list_get(log, params, "dpop.iat.slack.after"),
OAUTH2_VERIFY_DPOP_SLACK_DEFAULT);
return rv;
}
static char *
_oauth2_cfg_token_verify_options_mtls_set(oauth2_log_t *log,
oauth2_cfg_token_verify_t *verify,
oauth2_nv_list_t *params)
{
char *rv = NULL;
const char *policy = NULL;
verify->mtls.env_var_name =
oauth2_strdup(oauth2_nv_list_get(log, params, "mtls.env_var_name"));
policy = oauth2_nv_list_get(log, params, "mtls.policy");
if (policy != NULL) {
if (strcmp(policy, "optional") == 0)
verify->mtls.policy =
OAUTH2_MTLS_VERIFY_POLICY_OPTIONAL;
else if (strcmp(policy, "required") == 0)
verify->mtls.policy =
OAUTH2_MTLS_VERIFY_POLICY_REQUIRED;
}
return rv;
}
#define OAUTH2_CFG_VERIFY_RESULT_CACHE_DEFAULT 300
char *oauth2_cfg_token_verify_add_options(oauth2_log_t *log,
oauth2_cfg_token_verify_t **verify,
const char *type, const char *value,
const char *options)
{
char *rv = NULL;
oauth2_cfg_token_verify_t *v = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_debug(log, "enter: type=%s, value=%s, options=%s", type, value,
options);
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false)
goto end;
v = _oauth2_cfg_token_verify_add(log, verify);
v->cache = oauth2_cache_obtain(
log, oauth2_nv_list_get(log, params, "verify.cache"));
v->expiry_s =
oauth2_parse_uint(log, oauth2_nv_list_get(log, params, "expiry"),
OAUTH2_CFG_VERIFY_RESULT_CACHE_DEFAULT);
rv = _oauth2_cfg_token_verify_type_set(log, v, params);
if (rv != NULL)
goto end;
if (v->type == OAUTH2_TOKEN_VERIFY_DPOP) {
rv = _oauth2_cfg_token_verify_options_dpop_set(log, v, params);
if (rv != NULL)
goto end;
} else if (v->type == OAUTH2_TOKEN_VERIFY_MTLS) {
rv = _oauth2_cfg_token_verify_options_mtls_set(log, v, params);
if (rv != NULL)
goto end;
}
rv = oauth2_cfg_set_options(log, v, type, value, options,
_oauth2_cfg_verify_options_set);
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv ? rv : "(null)");
return rv;
}
liboauth2-1.4.4/src/cfg_int.h 0000664 0000000 0000000 00000050127 14161030125 0015760 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_CFG_INT_H_
#define _OAUTH2_CFG_INT_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cache.h"
#include "oauth2/cfg.h"
#include "oauth2/oauth2.h"
#include "oauth2/openidc.h"
#include "oauth2/session.h"
#include
typedef enum oauth2_jose_jwt_validate_claim_t {
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_OPTIONAL,
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED,
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_SKIP
} oauth2_jose_jwt_validate_claim_t;
oauth2_jose_jwt_validate_claim_t oauth2_parse_validate_claim_option(
oauth2_log_t *log, const char *value,
oauth2_jose_jwt_validate_claim_t default_value);
/*
* endpoint
*/
typedef struct oauth2_cfg_endpoint_t {
char *url;
oauth2_cfg_endpoint_auth_t *auth;
oauth2_flag_t ssl_verify;
oauth2_uint_t http_timeout;
char *outgoing_proxy;
} oauth2_cfg_endpoint_t;
/*
* auth
*/
typedef struct oauth2_cfg_endpoint_auth_none_t {
uint8_t dummy;
} oauth2_cfg_endpoint_auth_none_t;
typedef struct oauth2_cfg_endpoint_auth_client_secret_basic_t {
char *client_id;
char *client_secret;
} oauth2_cfg_endpoint_auth_client_secret_basic_t;
typedef struct oauth2_cfg_endpoint_auth_client_secret_post_t {
char *client_id;
char *client_secret;
} oauth2_cfg_endpoint_auth_client_secret_post_t;
typedef struct oauth2_cfg_endpoint_auth_client_secret_jwt_t {
char *client_id;
cjose_jwk_t *jwk;
char *aud;
} oauth2_cfg_endpoint_auth_client_secret_jwt_t;
typedef struct oauth2_cfg_endpoint_auth_private_key_jwt_t {
char *client_id;
cjose_jwk_t *jwk;
char *aud;
} oauth2_cfg_endpoint_auth_private_key_jwt_t;
typedef struct oauth2_cfg_endpoint_auth_client_cert_t {
char *certfile;
char *keyfile;
} oauth2_cfg_endpoint_auth_client_cert_t;
typedef struct oauth2_cfg_endpoint_auth_basic_t {
char *username;
char *password;
} oauth2_cfg_endpoint_auth_basic_t;
typedef struct oauth2_cfg_endpoint_auth_t {
oauth2_cfg_endpoint_auth_type_t type;
union {
oauth2_cfg_endpoint_auth_none_t none;
oauth2_cfg_endpoint_auth_client_secret_basic_t
client_secret_basic;
oauth2_cfg_endpoint_auth_client_secret_post_t
client_secret_post;
oauth2_cfg_endpoint_auth_client_secret_jwt_t client_secret_jwt;
oauth2_cfg_endpoint_auth_private_key_jwt_t private_key_jwt;
oauth2_cfg_endpoint_auth_client_cert_t client_cert;
oauth2_cfg_endpoint_auth_basic_t basic;
};
} oauth2_cfg_endpoint_auth_t;
/*
* source token
*/
typedef struct oauth2_cfg_source_token_t {
oauth2_cfg_token_in_t accept_in;
oauth2_flag_t strip;
} oauth2_cfg_source_token_t;
/*
* cache
*/
// typedef struct oauth2_cfg_cache_t {
// oauth2_cache_t *cache;
// oauth2_time_t expiry_s;
//} oauth2_cfg_cache_t;
//
// oauth2_cfg_cache_t *oauth2_cfg_cache_init(oauth2_log_t *log);
// oauth2_cfg_cache_t *oauth2_cfg_cache_clone(oauth2_log_t *log,
// oauth2_cfg_cache_t *src);
// void oauth2_cfg_cache_free(oauth2_log_t *log, oauth2_cfg_cache_t *cache);
char *oauth2_cfg_cache_set_options(oauth2_log_t *log, const char *type,
const oauth2_nv_list_t *params);
/*
* verify
*/
typedef void *(oauth2_cfg_ctx_init_cb)(oauth2_log_t *log);
typedef void *(oauth2_cfg_ctx_clone_cb)(oauth2_log_t *log, void *src);
typedef void(oauth2_cfg_ctx_free_cb)(oauth2_log_t *log, void *);
typedef struct oauth2_cfg_ctx_funcs_t {
oauth2_cfg_ctx_init_cb *init;
oauth2_cfg_ctx_clone_cb *clone;
oauth2_cfg_ctx_free_cb *free;
} oauth2_cfg_ctx_funcs_t;
typedef bool(oauth2_cfg_token_verify_cb_t)(oauth2_log_t *,
oauth2_cfg_token_verify_t *,
const char *, json_t **,
char **s_payload);
typedef struct oauth2_cfg_ctx_t {
void *ptr;
oauth2_cfg_ctx_funcs_t *callbacks;
} oauth2_cfg_ctx_t;
oauth2_cfg_ctx_t *oauth2_cfg_ctx_init(oauth2_log_t *log);
oauth2_cfg_ctx_t *oauth2_cfg_ctx_clone(oauth2_log_t *log,
oauth2_cfg_ctx_t *src);
void oauth2_cfg_ctx_free(oauth2_log_t *log, oauth2_cfg_ctx_t *ctx);
#define OAUTH2_TOKEN_VERIFY_BEARER_STR "bearer"
#define OAUTH2_TOKEN_VERIFY_DPOP_STR "dpop"
#define OAUTH2_TOKEN_VERIFY_MTLS_STR "mtls"
typedef struct oauth2_cfg_dpop_verify_t {
oauth2_cache_t *cache;
oauth2_time_t expiry_s;
oauth2_jose_jwt_validate_claim_t iat_validate;
oauth2_uint_t iat_slack_before;
oauth2_uint_t iat_slack_after;
} oauth2_cfg_dpop_verify_t;
typedef enum oauth2_cfg_mtls_verify_policy_t {
OAUTH2_MTLS_VERIFY_POLICY_OPTIONAL,
OAUTH2_MTLS_VERIFY_POLICY_REQUIRED,
} oauth2_cfg_mtls_verify_policy_t;
typedef struct oauth2_cfg_mtls_verify_t {
char *env_var_name;
oauth2_cfg_mtls_verify_policy_t policy;
} oauth2_cfg_mtls_verify_t;
typedef struct oauth2_cfg_token_verify_t {
oauth2_cfg_token_verify_cb_t *callback;
oauth2_cfg_ctx_t *ctx;
oauth2_cache_t *cache;
oauth2_time_t expiry_s;
oauth2_cfg_token_verify_type_t type;
oauth2_cfg_dpop_verify_t dpop;
oauth2_cfg_mtls_verify_t mtls;
struct oauth2_cfg_token_verify_t *next;
} oauth2_cfg_token_verify_t;
typedef char *(oauth2_cfg_set_options_cb_t)(oauth2_log_t *log,
const char *value,
const oauth2_nv_list_t *params,
void *cfg);
#define _OAUTH_CFG_CTX_CALLBACK(name) \
char *name(oauth2_log_t *log, const char *value, \
const oauth2_nv_list_t *params, void *ctx)
typedef struct oauth2_cfg_set_options_ctx_t {
const char *type;
oauth2_cfg_set_options_cb_t *set_options_callback;
} oauth2_cfg_set_options_ctx_t;
char *oauth2_cfg_set_options(oauth2_log_t *log, void *cfg, const char *type,
const char *value, const char *options,
const oauth2_cfg_set_options_ctx_t *set);
/*
* session
*/
bool oauth2_session_load_cookie(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
oauth2_http_request_t *request, json_t **json);
bool oauth2_session_save_cookie(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response, json_t *json);
bool oauth2_session_load_cache(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
oauth2_http_request_t *request, json_t **json);
bool oauth2_session_save_cache(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response, json_t *json);
typedef enum oauth2_cfg_session_type_t {
OAUTH2_SESSION_TYPE_COOKIE,
OAUTH2_SESSION_TYPE_CACHE
} oauth2_cfg_session_type_t;
typedef struct oauth2_cfg_session_t {
oauth2_cfg_session_type_t type;
char *cookie_name;
char *cookie_path;
oauth2_time_t inactivity_timeout_s;
oauth2_time_t max_duration_s;
oauth2_cache_t *cache;
oauth2_session_load_callback_t *load_callback;
oauth2_session_save_callback_t *save_callback;
} oauth2_cfg_session_t;
void _oauth2_session_global_cleanup(oauth2_log_t *log);
/*
* openidc
*/
typedef bool(oauth2_openidc_provider_resolver_func_t)(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *, char **);
typedef struct oauth2_cfg_openidc_provider_resolver_t {
oauth2_openidc_provider_resolver_func_t *callback;
oauth2_cfg_ctx_t *ctx;
oauth2_cache_t *cache;
} oauth2_cfg_openidc_provider_resolver_t;
// TODO: set add log
typedef struct oauth2_cfg_openidc_t {
char *handler_path;
char *redirect_uri;
oauth2_cfg_openidc_provider_resolver_t *provider_resolver;
oauth2_openidc_client_t *client;
oauth2_unauth_action_t unauth_action;
oauth2_cfg_session_t *session;
char *state_cookie_name_prefix;
oauth2_time_t state_cookie_timeout;
oauth2_uint_t state_cookie_max;
oauth2_flag_t state_cookie_delete_oldest;
} oauth2_cfg_openidc_t;
/*
* generic
*/
#define _OAUTH2_CFG_CTX_TYPE_START(type) typedef struct type##_t {
#define _OAUTH2_CFG_CTX_TYPE_END(type) \
} \
type##_t;
#define _OAUTH2_CFG_CTX_INIT_START(type) \
void *type##_init(oauth2_log_t *log) \
{ \
type##_t *ctx = (type##_t *)oauth2_mem_alloc(sizeof(type##_t));
#define _OAUTH2_CFG_CTX_INIT_END \
return ctx; \
}
#define _OAUTH2_CFG_CTX_CLONE_START(type) \
void *type##_clone(oauth2_log_t *log, void *s) \
{ \
type##_t *src = s; \
type##_t *dst = NULL; \
if (src == NULL) \
goto end; \
dst = type##_init(log);
#define _OAUTH2_CFG_CTX_CLONE_END \
end: \
return dst; \
}
#define _OAUTH2_CFG_CTX_FREE_START(type) \
void type##_free(oauth2_log_t *log, void *c) \
{ \
type##_t *ctx = (type##_t *)c;
#define _OAUTH2_CFG_CTX_FREE_END \
if (ctx) \
oauth2_mem_free(ctx); \
}
#define _OAUTH2_CFG_CTX_FUNCS(type) \
static oauth2_cfg_ctx_funcs_t type##_funcs = { \
type##_init, type##_clone, type##_free};
#define _OAUTH2_CFG_CTX_TYPE_SINGLE_STRING(type, member) \
_OAUTH2_CFG_CTX_TYPE_START(type) \
char *member; \
_OAUTH2_CFG_CTX_TYPE_END(type) \
\
_OAUTH2_CFG_CTX_INIT_START(type) \
ctx->member = NULL; \
_OAUTH2_CFG_CTX_INIT_END \
\
_OAUTH2_CFG_CTX_CLONE_START(type) \
dst->member = oauth2_strdup(src->member); \
_OAUTH2_CFG_CTX_CLONE_END \
\
_OAUTH2_CFG_CTX_FREE_START(type) \
if (ctx->member) \
oauth2_mem_free(ctx->member); \
_OAUTH2_CFG_CTX_FREE_END \
\
_OAUTH2_CFG_CTX_FUNCS(type)
oauth2_cfg_session_t *_oauth2_cfg_session_obtain(oauth2_log_t *log,
const char *name);
#define _OAUTH2_CFG_GLOBAL_LIST(name, type) \
typedef void (*_oauth2_##name##_free_fn)(oauth2_log_t * log, \
type * mtype); \
\
typedef struct oauth2_##name##_list_t { \
char *mname; \
type *mtype; \
_oauth2_##name##_free_fn free_fn; \
struct oauth2_##name##_list_t *next; \
} oauth2_##name##_list_t; \
\
static oauth2_##name##_list_t *_oauth2_##name##_list = NULL; \
static oauth2_ipc_mutex_t *_oauth2_##name##_list_mutex = NULL; \
\
static bool _M_##name##_list_lock(oauth2_log_t *log) \
{ \
bool rc = false; \
if (_oauth2_##name##_list_mutex == NULL) { \
_oauth2_##name##_list_mutex = \
oauth2_ipc_mutex_init(log); \
oauth2_ipc_mutex_post_config( \
log, _oauth2_##name##_list_mutex); \
} \
rc = oauth2_ipc_mutex_lock(log, _oauth2_##name##_list_mutex); \
return rc; \
} \
\
static bool _M_##name##_list_unlock(oauth2_log_t *log) \
{ \
bool rc = false; \
rc = \
oauth2_ipc_mutex_unlock(log, _oauth2_##name##_list_mutex); \
return rc; \
} \
\
static void _M_##name##_list_register( \
oauth2_log_t *log, const char *name, type *mtype, \
_oauth2_##name##_free_fn mfree_fn) \
{ \
oauth2_##name##_list_t *ptr = NULL, *prev = NULL; \
\
/* oauth2_debug(log, "registering: %s", name); */ \
\
ptr = oauth2_mem_alloc(sizeof(oauth2_##name##_list_t)); \
ptr->mname = oauth2_strdup(name); \
ptr->mtype = mtype; \
ptr->next = NULL; \
ptr->free_fn = mfree_fn; \
\
_M_##name##_list_lock(log); \
\
if (_oauth2_##name##_list) { \
prev = _oauth2_##name##_list; \
while (prev->next) \
prev = prev->next; \
prev->next = ptr; \
} else { \
_oauth2_##name##_list = ptr; \
} \
\
_M_##name##_list_unlock(log); \
} \
\
bool _M_##name##_list_empty(oauth2_log_t *log) \
{ \
return (_oauth2_##name##_list == NULL); \
} \
\
static type *_M_##name##_list_get(oauth2_log_t *log, \
const char *mname) \
{ \
oauth2_##name##_list_t *ptr = NULL, *match = NULL; \
\
_M_##name##_list_lock(log); \
\
ptr = _oauth2_##name##_list; \
while (ptr) { \
\
/* oauth2_debug(log, "comparing: \
* \"%s\" with \%s\"", ptr->mname, mname); */ \
\
if ((mname) && (ptr->mname)) { \
if (strcmp(ptr->mname, mname) == 0) { \
match = ptr; \
break; \
} \
} else if ((mname == NULL) || \
(strcmp("default", mname) == 0)) { \
match = ptr; \
} \
ptr = ptr->next; \
} \
\
_M_##name##_list_unlock(log); \
\
oauth2_debug(log, "returning: %p, %p, %s", match, \
match ? match->mtype : NULL, \
match ? match->mname : NULL); \
\
return match ? match->mtype : NULL; \
} \
\
static void _M_##name##_list_release(oauth2_log_t *log) \
{ \
oauth2_##name##_list_t *ptr = NULL; \
\
_M_##name##_list_lock(log); \
\
while ((ptr = _oauth2_##name##_list)) { \
_oauth2_##name##_list = _oauth2_##name##_list->next; \
if (ptr->free_fn) \
ptr->free_fn(log, ptr->mtype); \
oauth2_mem_free(ptr->mname); \
oauth2_mem_free(ptr); \
} \
\
_M_##name##_list_unlock(log); \
\
if (_oauth2_##name##_list_mutex != NULL) { \
oauth2_ipc_mutex_free(log, \
_oauth2_##name##_list_mutex); \
_oauth2_##name##_list_mutex = NULL; \
} \
}
#endif /* _OAUTH2_CFG_INT_H_ */
liboauth2-1.4.4/src/dpop.c 0000664 0000000 0000000 00000027666 14161030125 0015320 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include "cfg_int.h"
#include "jose_int.h"
#define OAUTH2_HTTP_HDR_DPOP "DPoP"
#define OAUTH_DPOP_HDR_JWK "jwk"
#define OAUTH_DPOP_CLAIM_HTM "htm"
#define OAUTH_DPOP_CLAIM_HTU "htu"
#define OAUTH_DPOP_HDR_TYP_VALUE "dpop+jwt"
static bool _oauth2_dpop_jwt_validate(oauth2_log_t *log, const char *s_dpop,
cjose_jws_t **jws, cjose_header_t **hdr,
json_t **dpop_payload)
{
bool rc = false;
cjose_err err;
uint8_t *plaintext = NULL;
size_t plaintext_len = 0;
char *s_payload = NULL;
*jws = cjose_jws_import(s_dpop, strlen(s_dpop), &err);
if (*jws == NULL) {
oauth2_error(log, "cjose_jws_import failed: %s", err.message);
goto end;
}
*hdr = cjose_jws_get_protected(*jws);
if (*hdr == NULL)
goto end;
if (cjose_jws_get_plaintext(*jws, &plaintext, &plaintext_len, &err) ==
false) {
oauth2_error(log, "cjose_jws_get_plaintext failed: %s",
err.message);
goto end;
}
s_payload = oauth2_strndup((const char *)plaintext, plaintext_len);
oauth2_debug(log, "DPOP payload: %s", s_payload);
if (oauth2_json_decode_object(log, s_payload, dpop_payload) == false) {
oauth2_error(log, "decoding JWT payload failed");
goto end;
}
rc = true;
end:
if (s_payload)
oauth2_mem_free(s_payload);
return rc;
}
static bool _oauth2_dpop_claims_validate(oauth2_log_t *log, cjose_header_t *hdr,
json_t *dpop_payload,
cjose_jwk_t **jwk,
const char **hdr_typ,
const char **hdr_alg, char **clm_htm,
char **clm_htu, char **clm_jti)
{
bool rc = false;
cjose_err err;
char *hdr_jwk = NULL;
json_int_t clm_iat;
*hdr_typ = cjose_header_get(hdr, OAUTH2_JOSE_HDR_TYP, &err);
if (*hdr_typ == NULL) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP header",
OAUTH2_JOSE_HDR_TYP);
goto end;
}
*hdr_alg = cjose_header_get(hdr, CJOSE_HDR_ALG, &err);
if (*hdr_alg == NULL) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP header",
CJOSE_HDR_ALG);
goto end;
}
hdr_jwk = cjose_header_get_raw(hdr, OAUTH_DPOP_HDR_JWK, &err);
if (hdr_jwk == NULL) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP header",
OAUTH_DPOP_HDR_JWK);
goto end;
}
*jwk = cjose_jwk_import(hdr_jwk, strlen(hdr_jwk), &err);
if (*jwk == NULL) {
oauth2_error(log, "cjose_jwk_import failed: %s", err.message);
goto end;
}
if (oauth2_json_string_get(log, dpop_payload, OAUTH_DPOP_CLAIM_HTU,
clm_htu, NULL) == false) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP payload",
OAUTH_DPOP_CLAIM_HTU);
goto end;
}
if (oauth2_json_string_get(log, dpop_payload, OAUTH_DPOP_CLAIM_HTM,
clm_htm, NULL) == false) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP payload",
OAUTH_DPOP_CLAIM_HTM);
goto end;
}
if (oauth2_json_string_get(log, dpop_payload, OAUTH2_CLAIM_JTI, clm_jti,
NULL) == false) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP payload",
OAUTH2_CLAIM_JTI);
goto end;
}
if (oauth2_json_number_get(log, dpop_payload, OAUTH2_CLAIM_IAT,
&clm_iat, 0) == false) {
oauth2_error(log,
"required claim \"%s\" not found in DPOP payload",
OAUTH2_CLAIM_IAT);
goto end;
}
rc = true;
end:
if (hdr_jwk)
cjose_get_dealloc()(hdr_jwk);
return rc;
}
static bool _oauth2_dpop_hdr_typ_validate(oauth2_log_t *log,
const char *hdr_typ)
{
bool rc = false;
if (strcasecmp(hdr_typ, OAUTH_DPOP_HDR_TYP_VALUE) != 0) {
oauth2_error(
log,
"the %s header value (%s) does not match DPOP value (%s)",
OAUTH2_JOSE_HDR_TYP, hdr_typ, OAUTH_DPOP_HDR_TYP_VALUE);
goto end;
}
rc = true;
end:
return rc;
}
static bool _oauth2_dpop_hdr_alg_validate(oauth2_log_t *log,
const char *hdr_alg)
{
bool rc = false;
if (strcasecmp(hdr_alg, CJOSE_HDR_ALG_NONE) == 0) {
oauth2_error(log, "the %s header value cannot be \"%s\"",
CJOSE_HDR_ALG, CJOSE_HDR_ALG_NONE);
goto end;
}
if ((strstr(hdr_alg, "RS") != hdr_alg) &&
(strstr(hdr_alg, "PS") != hdr_alg) &&
(strstr(hdr_alg, "ES") != hdr_alg)) {
oauth2_error(log,
"the %s header value must be asymmetric and "
"starting with \"RS\", \"PS\" or \"ES\".",
CJOSE_HDR_ALG);
goto end;
}
rc = true;
end:
return rc;
}
static bool _oauth2_dpop_sig_verify(oauth2_log_t *log, cjose_jws_t *jws,
const cjose_jwk_t *jwk)
{
bool rc = false;
cjose_err err;
if (cjose_jws_verify(jws, jwk, &err) == false) {
oauth2_error(log, "DPOP signature verification failed: %s",
err.message);
goto end;
}
rc = true;
end:
return rc;
}
static bool _oauth2_dpop_htm_validate(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *clm_htm)
{
bool rc = false;
const char *method = NULL;
method = oauth2_http_request_method_get_str(log, request);
if (strcasecmp(method, clm_htm) != 0) {
oauth2_error(log,
"requested HTTP method (%s) does not match DPOP "
"\"%s\" value (%s)",
method, OAUTH_DPOP_CLAIM_HTM, clm_htm);
goto end;
}
rc = true;
end:
return rc;
}
static bool _oauth2_dpop_htu_validate(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *clm_htu)
{
bool rc = false;
char *url = NULL;
url = oauth2_http_request_url_path_get(log, request);
if (url == NULL)
goto end;
if (strcasecmp(url, clm_htu) != 0) {
oauth2_error(
log,
"requested URL (%s) does not match DPOP \"%s\" value (%s)",
url, OAUTH_DPOP_CLAIM_HTU, clm_htu);
goto end;
}
rc = true;
end:
if (url)
oauth2_mem_free(url);
return rc;
}
static bool _oauth2_dpop_iat_validate(oauth2_log_t *log,
oauth2_cfg_dpop_verify_t *verify,
json_t *dpop_payload)
{
bool rc = false;
if (oauth2_jose_jwt_validate_iat(
log, dpop_payload, verify->iat_validate,
verify->iat_slack_before, verify->iat_slack_after) == false)
goto end;
rc = true;
end:
return rc;
}
static bool _oauth2_dpop_jti_validate(oauth2_log_t *log,
oauth2_cfg_dpop_verify_t *verify,
const char *clm_jti, const char *s_dpop)
{
bool rc = false;
char *s_value = NULL;
oauth2_cache_get(log, verify->cache, clm_jti, &s_value);
if (s_value != NULL) {
oauth2_error(log,
"a token with the same JTI \"%s\" exists in "
"the cache: possible replay attack",
clm_jti);
goto end;
}
oauth2_cache_set(log, verify->cache, clm_jti, s_dpop, verify->expiry_s);
rc = true;
end:
if (s_value)
oauth2_mem_free(s_value);
return rc;
}
static bool _oauth2_dpop_parse_and_validate(oauth2_log_t *log,
oauth2_cfg_dpop_verify_t *verify,
oauth2_http_request_t *request,
cjose_jwk_t **jwk)
{
bool rc = false;
const char *hdr_typ = NULL, *hdr_alg = NULL;
char *clm_htm = NULL, *clm_htu = NULL, *clm_jti = NULL;
const char *s_dpop = NULL;
cjose_jws_t *jws = NULL;
cjose_header_t *hdr = NULL;
char *s_peek = NULL;
json_t *dpop_payload = NULL;
if ((request == NULL) || (verify == NULL) || (jwk == NULL))
goto end;
s_dpop =
oauth2_http_request_header_get(log, request, OAUTH2_HTTP_HDR_DPOP);
if (s_dpop == NULL)
goto end;
s_peek = oauth2_jose_jwt_header_peek(log, s_dpop, NULL);
if (s_peek)
oauth2_debug(log, "DPOP header: %s", s_peek);
/*
* 1. the string value is a well-formed JWT
*/
if (_oauth2_dpop_jwt_validate(log, s_dpop, &jws, &hdr, &dpop_payload) ==
false)
goto end;
/*
* 2. all required claims are contained in the JWT,
*/
if (_oauth2_dpop_claims_validate(log, hdr, dpop_payload, jwk, &hdr_typ,
&hdr_alg, &clm_htm, &clm_htu,
&clm_jti) == false)
goto end;
/*
* 3. the "typ" field in the header has the value "dpop+jwt",
*/
if (_oauth2_dpop_hdr_typ_validate(log, hdr_typ) == false)
goto end;
/*
* 4. the algorithm in the header of the JWT indicates an asymmetric
* digital signature algorithm, is not "none", is supported by the
* application, and is deemed secure,
*/
if (_oauth2_dpop_hdr_alg_validate(log, hdr_alg) == false)
goto end;
/*
* 5. that the JWT is signed using the public key contained in the
* "jwk" header of the JWT,
*/
if (_oauth2_dpop_sig_verify(log, jws, *jwk) == false)
goto end;
/*
* 6. the "htm" claim matches the HTTP method value of the HTTP request
* in which the JWT was received (case-insensitive),
*/
if (_oauth2_dpop_htm_validate(log, request, clm_htm) == false)
goto end;
/*
* 7. the "htu" claims matches the HTTP URI value for the HTTP request
* in which the JWT was received, ignoring any query and fragment
* parts,
*/
if (_oauth2_dpop_htu_validate(log, request, clm_htu) == false)
goto end;
/*
* 8. the token was issued within an acceptable timeframe (see
* Section 9.1), and
*/
if (_oauth2_dpop_iat_validate(log, verify, dpop_payload) == false)
goto end;
/*
* 9. that, within a reasonable consideration of accuracy and resource
* utilization, a JWT with the same "jti" value has not been
* received previously (see Section 9.1).
*/
if (_oauth2_dpop_jti_validate(log, verify, s_dpop, clm_jti) == false)
goto end;
rc = true;
end:
if (s_peek)
oauth2_mem_free(s_peek);
if (clm_htu)
oauth2_mem_free(clm_htu);
if (clm_htm)
oauth2_mem_free(clm_htm);
if (clm_jti)
oauth2_mem_free(clm_jti);
if (dpop_payload)
json_decref(dpop_payload);
if (jws)
cjose_jws_release(jws);
return rc;
}
#define OAUTH_DPOP_CLAIM_CNF "cnf"
#define OAUTH_DPOP_CLAIM_CNF_JKT "jkt"
bool oauth2_dpop_token_verify(oauth2_log_t *log,
oauth2_cfg_dpop_verify_t *verify,
oauth2_http_request_t *request,
json_t *json_payload)
{
bool rc = false;
cjose_jwk_t *jwk = NULL;
json_t *cnf = NULL;
char *calc_thumb = NULL;
const char *prov_thumb = NULL;
unsigned char *hash_bytes = NULL;
unsigned int hash_bytes_len = 0;
uint8_t *dst = NULL;
size_t dst_len = 0;
if ((request == NULL) || (json_payload == NULL))
goto end;
if (_oauth2_dpop_parse_and_validate(log, verify, request, &jwk) ==
false)
goto end;
if (oauth2_jose_jwk_thumbprint(log, jwk, &hash_bytes,
&hash_bytes_len) == false) {
oauth2_error(log, "oauth2_jose_jwk_thumbprint failed");
goto end;
}
cnf = json_object_get(json_payload, OAUTH_DPOP_CLAIM_CNF);
if (cnf == NULL) {
oauth2_error(log, "no \"%s\" claim found",
OAUTH_DPOP_CLAIM_CNF);
goto end;
}
prov_thumb =
json_string_value(json_object_get(cnf, OAUTH_DPOP_CLAIM_CNF_JKT));
if (oauth2_base64url_decode(log, prov_thumb, &dst, &dst_len) == false) {
oauth2_error(log, "oauth2_base64url_decode failed");
goto end;
}
if ((hash_bytes_len != dst_len) ||
(memcmp(hash_bytes, dst, hash_bytes_len)) != 0) {
oauth2_error(log,
"public key thumbprint in DPOP \"%s\" does not "
"match \"%s\" claim \%s\" in JWT token",
calc_thumb, OAUTH_DPOP_CLAIM_CNF_JKT, prov_thumb);
goto end;
}
rc = true;
end:
if (dst)
oauth2_mem_free(dst);
if (hash_bytes)
oauth2_mem_free(hash_bytes);
if (calc_thumb)
oauth2_mem_free(calc_thumb);
if (jwk)
cjose_jwk_release(jwk);
return rc;
}
liboauth2-1.4.4/src/http.c 0000664 0000000 0000000 00000122726 14161030125 0015326 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include "oauth2/http.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "oauth2/version.h"
#include "util_int.h"
/*
* request
*/
typedef struct oauth2_http_request_t {
// oauth2_http_server_t *server;
oauth2_nv_list_t *header;
char *scheme;
unsigned long port;
char *hostname;
oauth2_uint_t method;
char *path;
char *query;
oauth2_nv_list_t *_parsed_query;
oauth2_nv_list_t *_parsed_cookies;
oauth2_nv_list_t *_context;
} oauth2_http_request_t;
// TODO: provide scheme as part of init?
oauth2_http_request_t *oauth2_http_request_init(oauth2_log_t *log)
{
oauth2_http_request_t *request = NULL;
request = oauth2_mem_alloc(sizeof(oauth2_http_request_t));
if (request == NULL)
goto end;
request->header = oauth2_nv_list_init(log);
oauth2_nv_list_case_sensitive_set(log, request->header, false);
request->scheme = NULL;
request->hostname = NULL;
request->port = 0;
request->method = 0;
request->path = NULL;
request->query = NULL;
request->_parsed_query = NULL;
request->_parsed_cookies = NULL;
request->_context = oauth2_nv_list_init(log);
;
end:
return request;
}
void oauth2_http_request_free(oauth2_log_t *log, oauth2_http_request_t *request)
{
if (request == NULL)
goto end;
oauth2_nv_list_free(log, request->_context);
oauth2_nv_list_free(log, request->_parsed_query);
oauth2_nv_list_free(log, request->_parsed_cookies);
oauth2_nv_list_free(log, request->header);
if (request->scheme)
oauth2_mem_free(request->scheme);
if (request->hostname)
oauth2_mem_free(request->hostname);
if (request->path)
oauth2_mem_free(request->path);
if (request->query)
oauth2_mem_free(request->query);
oauth2_mem_free(request);
end:
return;
}
_OAUTH2_MEMBER_LIST_IMPLEMENT_UNSET_GET(http, request, header)
void oauth2_http_request_headers_loop(oauth2_log_t *log,
oauth2_http_request_t *request,
oauth2_nv_list_loop_cb_t *callback,
void *rec)
{
oauth2_nv_list_loop(log, request->header, callback, rec);
}
/*
* headers
*/
typedef bool(_oauth2_nv_list_set_add_sanitize_cb_t)(oauth2_log_t *,
oauth2_nv_list_t *,
const char *, const char *);
static bool _oauth2_http_request_header_set_add_sanitized(
oauth2_log_t *log, oauth2_http_request_t *request, const char *name,
const char *value, _oauth2_nv_list_set_add_sanitize_cb_t add_set_cb)
{
bool rc = false;
char *s_value = NULL, *p = NULL;
if ((request == NULL) && (name == NULL))
goto end;
if (value) {
s_value = oauth2_strdup(value);
if (s_value == NULL)
goto end;
/*
* sanitize the header value by replacing line feeds with spaces
* just like the Apache header input algorithms do for incoming
* headers
*
* this makes it impossible to have line feeds in values but
* that is
* compliant with RFC 7230 (and impossible for regular headers
* due to Apache's
* parsing of headers anyway) and fixes a security vulnerability
* on
* overwriting/setting outgoing headers when used in proxy mode
*/
while ((p = strchr(s_value, '\n')))
*p = ' ';
}
oauth2_debug(log, "%s: %s", name, s_value ? s_value : "(null)");
rc = add_set_cb(log, request->header, name, s_value);
end:
if (s_value)
oauth2_mem_free(s_value);
return rc;
}
bool oauth2_http_request_context_set(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value)
{
bool rc = false;
if (request == NULL)
goto end;
rc = oauth2_nv_list_set(log, request->_context, name, value);
end:
return rc;
}
const char *oauth2_http_request_context_get(
oauth2_log_t *log, const oauth2_http_request_t *request, const char *name)
{
const char *rv = NULL;
if (request == NULL)
goto end;
rv = oauth2_nv_list_get(log, request->_context, name);
end:
return rv;
}
bool oauth2_http_request_header_set(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value)
{
return _oauth2_http_request_header_set_add_sanitized(
log, request, name, value, oauth2_nv_list_set);
}
bool oauth2_http_request_header_add(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value)
{
return _oauth2_http_request_header_set_add_sanitized(
log, request, name, value, oauth2_nv_list_add);
}
static char *oauth2_http_request_header_get_left_most_only(
oauth2_log_t *log, const oauth2_http_request_t *request, const char *name)
{
char *rv = NULL, *v = NULL;
const char *value = NULL;
const char *separators = ", \t";
value = oauth2_http_request_header_get(log, request, name);
if (value == NULL)
goto end;
v = oauth2_strdup(value);
if (v)
rv = strtok(v, separators);
end:
return rv;
}
static char *
oauth2_http_request_header_x_forwarded_proto_get(oauth2_log_t *log,
const oauth2_http_request_t *r)
{
return oauth2_http_request_header_get_left_most_only(
log, r, OAUTH2_HTTP_HDR_X_FORWARDED_PROTO);
}
static char *oauth2_http_request_header_x_forwarded_port_get(
oauth2_log_t *log, const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get_left_most_only(
log, request, OAUTH2_HTTP_HDR_X_FORWARDED_PORT);
}
static char *oauth2_http_request_header_x_forwarded_host_get(
oauth2_log_t *log, const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get_left_most_only(
log, request, OAUTH2_HTTP_HDR_X_FORWARDED_HOST);
}
static char *
oauth2_http_request_header_host_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
return oauth2_strdup(
oauth2_http_request_header_get(log, request, OAUTH2_HTTP_HDR_HOST));
}
const char *oauth2_http_request_header_content_type_get(
oauth2_log_t *log, const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get(log, request,
OAUTH2_HTTP_HDR_CONTENT_TYPE);
}
const char *oauth2_http_request_header_content_length_get(
oauth2_log_t *log, const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get(log, request,
OAUTH2_HTTP_HDR_CONTENT_LENGTH);
}
const char *oauth2_http_request_header_x_requested_with_get(
oauth2_log_t *log, const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get(log, request,
OAUTH2_HTTP_HDR_X_REQUESTED_WITH);
}
const char *
oauth2_http_request_header_accept_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get(log, request,
OAUTH2_HTTP_HDR_ACCEPT);
}
#define OAUTH2_HTTP_HDR_CONTENT_LENGTH_MAX 256
bool oauth2_http_request_header_content_length_set(
oauth2_log_t *log, oauth2_http_request_t *request, size_t len)
{
char str[OAUTH2_HTTP_HDR_CONTENT_LENGTH_MAX];
oauth2_snprintf(str, OAUTH2_HTTP_HDR_CONTENT_LENGTH_MAX, "%lu", len);
return oauth2_http_request_header_set(
log, request, OAUTH2_HTTP_HDR_CONTENT_LENGTH, str);
}
const char *
oauth2_http_request_header_cookie_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
return oauth2_http_request_header_get(log, request,
OAUTH2_HTTP_HDR_COOKIE);
}
static bool _oauth2_http_request_header_cookie_set(
oauth2_log_t *log, oauth2_http_request_t *request, const char *value)
{
return oauth2_http_request_header_set(log, request,
OAUTH2_HTTP_HDR_COOKIE, value);
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, request, scheme, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, request, hostname, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(http, request, path, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(http, request, method,
oauth2_http_method_t, uint)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(http, request, query, char *, str)
const char *oauth2_http_request_method_get_str(oauth2_log_t *log,
oauth2_http_request_t *request)
{
const char *rv = NULL;
switch (oauth2_http_request_method_get(log, request)) {
case OAUTH2_HTTP_METHOD_GET:
rv = "GET";
break;
case OAUTH2_HTTP_METHOD_PUT:
rv = "PUT";
break;
case OAUTH2_HTTP_METHOD_POST:
rv = "POST";
break;
case OAUTH2_HTTP_METHOD_DELETE:
rv = "DELETE";
break;
case OAUTH2_HTTP_METHOD_CONNECT:
rv = "CONNECT";
break;
case OAUTH2_HTTP_METHOD_OPTIONS:
rv = "OPTIONS";
break;
case OAUTH2_HTTP_METHOD_UNKNOWN:
break;
default:
break;
}
return rv;
}
/*
* current request URI
*/
char *oauth2_http_request_scheme_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
char *scheme_str = NULL;
if (request == NULL)
goto end;
scheme_str =
oauth2_http_request_header_x_forwarded_proto_get(log, request);
if (scheme_str == NULL)
scheme_str = oauth2_strdup(request->scheme);
if ((scheme_str == NULL) ||
((strcmp(scheme_str, OAUTH2_HTTP_SCHEME_HTTP) != 0) &&
(strcmp(scheme_str, OAUTH2_HTTP_SCHEME_HTTPS) != 0))) {
oauth2_warn(log,
"detected HTTP scheme \"%s\" is not \"%s\" nor "
"\"%s\"; perhaps your reverse proxy passes a "
"wrongly configured \"%s\" header: falling back "
"to default \"%s\"",
scheme_str, OAUTH2_HTTP_SCHEME_HTTP,
OAUTH2_HTTP_SCHEME_HTTPS,
OAUTH2_HTTP_HDR_X_FORWARDED_PROTO,
OAUTH2_HTTP_SCHEME_HTTPS);
scheme_str = oauth2_strdup(OAUTH2_HTTP_SCHEME_HTTPS);
}
end:
return scheme_str;
}
bool oauth2_http_request_port_set(oauth2_log_t *log,
oauth2_http_request_t *request,
unsigned long port)
{
request->port = port;
return (request->port > 0);
}
#define OAUTH2_PORT_STR_MAX 16
char *oauth2_http_request_port_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
char *proto_str = NULL, *port_str = NULL, *scheme_str = NULL;
port_str =
oauth2_http_request_header_x_forwarded_port_get(log, request);
if (port_str)
goto end;
char *host_hdr =
oauth2_http_request_header_x_forwarded_host_get(log, request);
if (host_hdr) {
port_str = strchr(host_hdr, _OAUTH2_CHAR_COLON);
if (port_str) {
port_str++;
port_str = oauth2_strdup(port_str);
}
oauth2_mem_free(host_hdr);
goto end;
}
host_hdr = oauth2_http_request_header_host_get(log, request);
if (host_hdr) {
port_str = strchr(host_hdr, _OAUTH2_CHAR_COLON);
if (port_str) {
port_str++;
port_str = oauth2_strdup(port_str);
}
oauth2_mem_free(host_hdr);
if (port_str)
goto end;
}
proto_str =
oauth2_http_request_header_x_forwarded_proto_get(log, request);
if (proto_str)
goto end;
unsigned long port = request->port;
scheme_str = oauth2_http_request_scheme_get(log, request);
if (scheme_str) {
if ((strcasecmp(scheme_str, OAUTH2_HTTP_SCHEME_HTTPS) == 0) &&
port == 443)
goto end;
else if ((strcasecmp(scheme_str, OAUTH2_HTTP_SCHEME_HTTP) ==
0) &&
port == 80)
goto end;
}
if (port > 0) {
port_str = oauth2_mem_alloc(OAUTH2_PORT_STR_MAX);
oauth2_snprintf(port_str, OAUTH2_PORT_STR_MAX, "%lu", port);
}
end:
if (proto_str)
oauth2_mem_free(proto_str);
if (scheme_str)
oauth2_mem_free(scheme_str);
return port_str;
}
char *oauth2_http_request_hostname_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
char *host_str = NULL;
if (request == NULL)
goto end;
host_str =
oauth2_http_request_header_x_forwarded_host_get(log, request);
if (host_str == NULL)
host_str = oauth2_http_request_header_host_get(log, request);
if (host_str) {
char *p = strchr(host_str, _OAUTH2_CHAR_COLON);
if (p != NULL)
*p = '\0';
goto end;
}
if (request->hostname)
host_str = oauth2_strdup(request->hostname);
end:
return host_str;
}
char *oauth2_http_request_url_base_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
// TODO: store static in request so this is evaluated only once for each
// request
// or do we want to allow dynamically inserted header evaluation?
char *url = NULL, *host_str = NULL, *port_str = NULL;
if (request == NULL)
goto end;
url = oauth2_http_request_scheme_get(log, request);
if (url == NULL)
goto end;
host_str = oauth2_http_request_hostname_get(log, request);
if (host_str == NULL) {
oauth2_mem_free(url);
url = NULL;
goto end;
}
port_str = oauth2_http_request_port_get(log, request);
url = _oauth2_stradd4(url, "://", host_str, port_str ? ":" : NULL,
port_str);
end:
if (host_str)
oauth2_mem_free(host_str);
if (port_str)
oauth2_mem_free(port_str);
return url;
}
char *oauth2_http_request_url_path_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
char *url = NULL, *base_str = NULL, *path_str = NULL;
base_str = oauth2_http_request_url_base_get(log, request);
if (base_str == NULL)
goto end;
// TODO: in Apache r->path (or r->uri) can be absolute
// for forwarding proxy setups; are we dealing with that?
path_str = request->path ? request->path : "";
url = oauth2_stradd(NULL, base_str, path_str, NULL);
end:
oauth2_debug(log, "%s", url);
if (base_str)
oauth2_mem_free(base_str);
return url;
}
char *oauth2_http_request_url_get(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
char *url = NULL, *url_path_str = NULL, *query_str = NULL, *sep = NULL;
url_path_str = oauth2_http_request_url_path_get(log, request);
if (url_path_str == NULL)
goto end;
// TODO: query_args_add http until function
sep = (request->query && *request->query != '\0') ? _OAUTH2_STR_QMARK
: "";
query_str = request->query ? request->query : "";
url = oauth2_stradd(NULL, url_path_str, sep, query_str);
end:
oauth2_debug(log, "%s", url);
if (url_path_str)
oauth2_mem_free(url_path_str);
return url;
}
/*
* oauth2_http_call_ctx_t
*/
typedef struct oauth2_http_call_ctx_t {
char *basic_auth_username;
char *basic_auth_password;
char *bearer_token;
int timeout;
bool ssl_verify;
char *outgoing_proxy;
oauth2_nv_list_t *cookie;
oauth2_nv_list_t *hdr;
char *ca_info;
char *ssl_cert;
char *ssl_key;
char *to_str;
} oauth2_http_call_ctx_t;
#define OAUTH2_HTTP_CALL_TIMEOUT_DEFAULT 15
#define OAUTH2_HTTP_CALL_SSL_VERIFY_DEFAULT true
oauth2_http_call_ctx_t *oauth2_http_call_ctx_init(oauth2_log_t *log)
{
oauth2_http_call_ctx_t *ctx = NULL;
ctx = oauth2_mem_alloc(sizeof(oauth2_http_call_ctx_t));
if (ctx == NULL)
goto end;
oauth2_http_call_ctx_timeout_set(log, ctx,
OAUTH2_HTTP_CALL_TIMEOUT_DEFAULT);
oauth2_http_call_ctx_ssl_verify_set(
log, ctx, OAUTH2_HTTP_CALL_SSL_VERIFY_DEFAULT);
oauth2_http_call_ctx_outgoing_proxy_set(log, ctx, NULL);
oauth2_http_call_ctx_ca_info_set(log, ctx, NULL);
oauth2_http_call_ctx_ssl_cert_set(log, ctx, NULL);
oauth2_http_call_ctx_ssl_key_set(log, ctx, NULL);
ctx->cookie = oauth2_nv_list_init(log);
ctx->hdr = oauth2_nv_list_init(log);
oauth2_nv_list_case_sensitive_set(log, ctx->hdr, false);
ctx->to_str = NULL;
end:
return ctx;
}
void oauth2_http_call_ctx_free(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx)
{
if (ctx == NULL)
goto end;
if (ctx->basic_auth_username)
oauth2_mem_free(ctx->basic_auth_username);
if (ctx->basic_auth_password)
oauth2_mem_free(ctx->basic_auth_password);
if (ctx->bearer_token)
oauth2_mem_free(ctx->bearer_token);
if (ctx->outgoing_proxy)
oauth2_mem_free(ctx->outgoing_proxy);
if (ctx->ca_info)
oauth2_mem_free(ctx->ca_info);
if (ctx->ssl_cert)
oauth2_mem_free(ctx->ssl_cert);
if (ctx->ssl_key)
oauth2_mem_free(ctx->ssl_key);
if (ctx->cookie)
oauth2_nv_list_free(log, ctx->cookie);
if (ctx->hdr)
oauth2_nv_list_free(log, ctx->hdr);
if (ctx->to_str)
oauth2_mem_free(ctx->to_str);
oauth2_mem_free(ctx);
end:
return;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, timeout, int, integer)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, ssl_verify, bool, bln)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, outgoing_proxy, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, ca_info, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, ssl_cert, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(http, call_ctx, ssl_key, char *, str)
_OAUTH2_MEMBER_LIST_IMPLEMENT_SET_ADD_UNSET_GET(http, call_ctx, cookie);
_OAUTH2_MEMBER_LIST_IMPLEMENT_SET_ADD_UNSET_GET(http, call_ctx, hdr);
bool oauth2_http_call_ctx_content_type_set(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx,
const char *content_type)
{
return oauth2_http_call_ctx_hdr_set(
log, ctx, OAUTH2_HTTP_HDR_CONTENT_TYPE, content_type);
}
bool oauth2_http_call_ctx_bearer_token_set(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx,
const char *token)
{
bool rc = false;
char *str = NULL;
if ((ctx == NULL) || (token == NULL))
goto end;
str = oauth2_stradd(str, OAUTH2_HTTP_HDR_BEARER, " ", token);
rc = oauth2_http_call_ctx_hdr_set(log, ctx,
OAUTH2_HTTP_HDR_AUTHORIZATION, str);
end:
if (str)
oauth2_mem_free(str);
return rc;
}
bool oauth2_http_call_ctx_basic_auth_set(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx,
const char *username,
const char *password, bool url_encode)
{
if (url_encode) {
ctx->basic_auth_username = oauth2_url_encode(log, username);
ctx->basic_auth_password = oauth2_url_encode(log, password);
} else {
ctx->basic_auth_username = oauth2_strdup(username);
ctx->basic_auth_password = oauth2_strdup(password);
}
return true;
}
static char *_oauth2_http_call_ctx2s(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx)
{
char *ptr = NULL;
if (ctx == NULL)
return NULL;
if (ctx->to_str)
oauth2_mem_free(ctx->to_str);
ctx->to_str = oauth2_strdup("[");
ctx->to_str =
oauth2_stradd(ctx->to_str, " ssl_verify", _OAUTH2_STR_EQUAL,
ctx->ssl_verify ? "true" : "false");
if (ctx->basic_auth_username)
ctx->to_str =
oauth2_stradd(ctx->to_str, " basic_auth_username",
_OAUTH2_STR_EQUAL, ctx->basic_auth_username);
if (ctx->basic_auth_password)
ctx->to_str =
oauth2_stradd(ctx->to_str, " basic_auth_password",
_OAUTH2_STR_EQUAL, ctx->basic_auth_password);
if (ctx->outgoing_proxy)
ctx->to_str =
oauth2_stradd(ctx->to_str, " outgoing_proxy",
_OAUTH2_STR_EQUAL, ctx->outgoing_proxy);
if (ctx->ca_info)
ctx->to_str = oauth2_stradd(ctx->to_str, " ca_info",
_OAUTH2_STR_EQUAL, ctx->ca_info);
if (ctx->ssl_cert)
ctx->to_str = oauth2_stradd(ctx->to_str, " ssl_cert",
_OAUTH2_STR_EQUAL, ctx->ssl_cert);
if (ctx->ssl_key)
ctx->to_str = oauth2_stradd(ctx->to_str, " ssl_key",
_OAUTH2_STR_EQUAL, ctx->ssl_key);
ptr = oauth2_nv_list2s(log, ctx->hdr);
if (ptr) {
ctx->to_str =
oauth2_stradd(ctx->to_str, " hdr", _OAUTH2_STR_EQUAL, ptr);
oauth2_mem_free(ptr);
}
ptr = oauth2_nv_list2s(log, ctx->cookie);
if (ptr) {
ctx->to_str = oauth2_stradd(ctx->to_str, " cookie",
_OAUTH2_STR_EQUAL, ptr);
oauth2_mem_free(ptr);
}
ctx->to_str = oauth2_stradd(ctx->to_str, " ]", NULL, NULL);
return ctx->to_str;
}
/*
* encoding
*/
typedef struct _oauth2_http_encode_str_t {
const char *sep;
char **str;
} _oauth2_http_encode_str_t;
static bool _oauth2_http_url_encode_list(oauth2_log_t *log, void *rec,
const char *key, const char *value)
{
bool rc = false;
char *enc_key = NULL, *enc_val = NULL;
_oauth2_http_encode_str_t *state = (_oauth2_http_encode_str_t *)rec;
if ((state->str == NULL) || (key == NULL))
goto end;
oauth2_debug(log, "processing: %s=%s", key, value);
enc_key = oauth2_url_encode(log, key);
enc_val = oauth2_url_encode(log, value);
*state->str =
_oauth2_stradd4(*state->str, *state->str ? state->sep : "", enc_key,
_OAUTH2_STR_EQUAL, enc_val);
rc = true;
end:
if (enc_key)
oauth2_mem_free(enc_key);
if (enc_val)
oauth2_mem_free(enc_val);
return rc;
}
static bool _oauth2_http_url_query_encode_param(oauth2_log_t *log, void *rec,
const char *key,
const char *value)
{
_oauth2_http_encode_str_t encode_str = {_OAUTH2_STR_AMP, (char **)rec};
return _oauth2_http_url_encode_list(log, &encode_str, key, value);
}
static bool _oauth2_http_url_encode_cookie(oauth2_log_t *log, void *rec,
const char *key, const char *value)
{
_oauth2_http_encode_str_t encode_str = {_OAUTH2_STR_SEMICOL " ",
(char **)rec};
return _oauth2_http_url_encode_list(log, &encode_str, key, value);
}
static char *_oauth2_http_cookies_encode(oauth2_log_t *log,
oauth2_nv_list_t *cookies)
{
char *str = NULL;
oauth2_nv_list_loop(log, cookies, _oauth2_http_url_encode_cookie, &str);
return str;
}
char *oauth2_http_url_query_encode(oauth2_log_t *log, const char *url,
const oauth2_nv_list_t *params)
{
char *result = NULL;
const char *sep = NULL;
char *encode_str = NULL;
oauth2_nv_list_loop(log, params, _oauth2_http_url_query_encode_param,
&encode_str);
if (url && encode_str)
sep = strrchr(url, _OAUTH2_CHAR_QUERY) != NULL
? _OAUTH2_STR_AMP
: _OAUTH2_STR_QMARK;
result = oauth2_stradd(result, url, sep, encode_str);
oauth2_debug(log, "result=%s", result);
if (encode_str)
oauth2_mem_free(encode_str);
return result;
}
char *oauth2_http_url_form_encode(oauth2_log_t *log,
const oauth2_nv_list_t *args)
{
char *encode_str = NULL;
oauth2_nv_list_loop(log, args, _oauth2_http_url_query_encode_param,
&encode_str);
oauth2_debug(log, "data=%s", encode_str);
return encode_str;
}
/*
* curl
*/
typedef struct oauth2_http_curl_buf_t {
oauth2_log_t *log;
char *memory;
size_t size;
} oauth2_http_curl_buf_t;
#define _OAUTH2_HTTP_CURL_BUF_MAX 1024 * 1024
static size_t oauth2_http_curl_buf_write(void *contents, size_t size,
size_t nmemb, void *userp)
{
size_t realsize = size * nmemb, rc = 0;
oauth2_http_curl_buf_t *mem = (oauth2_http_curl_buf_t *)userp;
if (mem->size + realsize > _OAUTH2_HTTP_CURL_BUF_MAX) {
oauth2_error(mem->log,
"HTTP response larger than maximum allowed "
"size: current size=%ld, additional "
"size=%ld, max=%d",
mem->size, realsize, _OAUTH2_HTTP_CURL_BUF_MAX);
goto end;
}
char *newptr = oauth2_mem_alloc(mem->size + realsize + 1);
if (newptr == NULL) {
oauth2_error(
mem->log,
"memory allocation for new buffer of %ld bytes failed",
mem->size + realsize + 1);
goto end;
}
memcpy(newptr, mem->memory, mem->size);
memcpy(&(newptr[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory = newptr;
mem->memory[mem->size] = 0;
rc = realsize;
end:
return rc;
}
static bool _oauth2_http_curl_header_add(oauth2_log_t *log, void *rec,
const char *key, const char *value)
{
bool rc = false;
char *str = NULL;
struct curl_slist **h_list = (struct curl_slist **)rec;
if ((h_list == NULL) || (key == NULL))
goto end;
str = _oauth2_stradd4(NULL, key, _OAUTH2_STR_COLON, " ", value);
if (str == NULL)
goto end;
*h_list = curl_slist_append(*h_list, str);
rc = true;
end:
if (str)
oauth2_mem_free(str);
return rc;
}
bool oauth2_http_call(oauth2_log_t *log, const char *url, const char *data,
oauth2_http_call_ctx_t *ctx, char **response,
oauth2_http_status_code_t *status_code)
{
bool rc = false;
char *str = NULL;
long response_code = 0;
char err[CURL_ERROR_SIZE];
CURL *curl = NULL;
CURLcode errornum = CURLE_OK;
struct curl_slist *h_list = NULL;
oauth2_http_curl_buf_t buf;
buf.log = log;
buf.memory = NULL;
buf.size = 0;
oauth2_debug(log, "enter: url=%s, data=%s, ctx=%s", url,
data ? data : "(null)", _oauth2_http_call_ctx2s(log, ctx));
if ((url == NULL) || (response == NULL))
goto end;
// TODO: this is somewhat shared (at least the initialization of
// globals) with url-encode/url-decode??
curl = curl_easy_init();
if (curl == NULL) {
oauth2_error(log, "curl_easy_init() error");
goto end;
}
err[0] = 0;
curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
if (ctx)
curl_easy_setopt(curl, CURLOPT_TIMEOUT, ctx->timeout);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
oauth2_http_curl_buf_write);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buf);
#ifndef LIBCURL_NO_CURLPROTO
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS,
CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_easy_setopt(curl, CURLOPT_PROTOCOLS,
CURLPROTO_HTTP | CURLPROTO_HTTPS);
#endif
if (ctx) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,
(ctx->ssl_verify != false ? 1L : 0L));
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
(ctx->ssl_verify != false ? 2L : 0L));
}
if (ctx && (ctx->ca_info)) {
curl_easy_setopt(curl, CURLOPT_CAINFO, ctx->ca_info);
} else {
#ifdef WIN32
DWORD buflen;
char *ptr = NULL;
char *retval = oauth2_mem_alloc(sizeof(TCHAR) * (MAX_PATH + 1));
retval[0] = '\0';
buflen = SearchPath(NULL, "curl-ca-bundle.crt", NULL,
MAX_PATH + 1, retval, &ptr);
if (buflen > 0)
curl_easy_setopt(curl, CURLOPT_CAINFO, retval);
else
oauth2_warn(log,
"no curl-ca-bundle.crt file found in path");
oauth2_mem_free(retval);
#endif
}
curl_easy_setopt(curl, CURLOPT_USERAGENT, oauth2_package_string());
if (ctx && ctx->outgoing_proxy)
curl_easy_setopt(curl, CURLOPT_PROXY, ctx->outgoing_proxy);
if (ctx && (ctx->basic_auth_username || ctx->basic_auth_password)) {
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
if (ctx->basic_auth_username)
curl_easy_setopt(curl, CURLOPT_USERNAME,
ctx->basic_auth_username);
if (ctx->basic_auth_password)
curl_easy_setopt(curl, CURLOPT_PASSWORD,
ctx->basic_auth_password);
}
if (ctx) {
if (ctx->ssl_cert != NULL)
curl_easy_setopt(curl, CURLOPT_SSLCERT, ctx->ssl_cert);
if (ctx->ssl_key != NULL)
curl_easy_setopt(curl, CURLOPT_SSLKEY, ctx->ssl_key);
}
if (data != NULL) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(curl, CURLOPT_POST, 1);
}
if (ctx)
oauth2_nv_list_loop(log, ctx->hdr, _oauth2_http_curl_header_add,
&h_list);
if (h_list != NULL)
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, h_list);
if (ctx)
str = _oauth2_http_cookies_encode(log, ctx->cookie);
if (str) {
oauth2_debug(log, "passing browser cookies on backend call: %s",
str);
curl_easy_setopt(curl, CURLOPT_COOKIE, str);
oauth2_mem_free(str);
str = NULL;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
errornum = curl_easy_perform(curl);
if (errornum != CURLE_OK) {
oauth2_error(log, "curl_easy_perform() failed on: %s (%s: %s)",
url, curl_easy_strerror(errornum),
err[0] ? err : "");
if (errornum == CURLE_OPERATION_TIMEDOUT)
// 408 Request Timeout
// 504 Gateway Timeout
*status_code = 504;
goto end;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
oauth2_debug(log, "HTTP response code=%ld", response_code);
if (status_code)
*status_code = (oauth2_uint_t)response_code;
*response = oauth2_mem_alloc(buf.size + 1);
strncpy(*response, buf.memory, buf.size);
(*response)[buf.size] = '\0';
rc = true;
end:
if (buf.memory)
oauth2_mem_free(buf.memory);
if (h_list != NULL)
curl_slist_free_all(h_list);
curl_easy_cleanup(curl);
oauth2_debug(log, "leave [%d]: %s", rc,
(response && *response) ? *response : "(null)");
return rc;
}
bool oauth2_http_get(oauth2_log_t *log, const char *url,
const oauth2_nv_list_t *params,
oauth2_http_call_ctx_t *ctx, char **response,
oauth2_http_status_code_t *status_code)
{
bool rc = false;
char *query_url = NULL;
oauth2_debug(log, "enter: %s", url);
query_url = oauth2_http_url_query_encode(log, url, params);
rc = oauth2_http_call(log, query_url, NULL, ctx, response, status_code);
if (query_url)
oauth2_mem_free(query_url);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
bool oauth2_http_post_form(oauth2_log_t *log, const char *url,
const oauth2_nv_list_t *params,
oauth2_http_call_ctx_t *ctx, char **response,
oauth2_http_status_code_t *status_code)
{
bool rc = false;
char *data = NULL;
data = oauth2_http_url_form_encode(log, params);
oauth2_http_call_ctx_content_type_set(log, ctx,
OAUTH2_CONTENT_TYPE_FORM_ENCODED);
rc = oauth2_http_call(log, url, data, ctx, response, status_code);
if (data)
oauth2_mem_free(data);
return rc;
}
bool oauth2_http_post_json(oauth2_log_t *log, const char *url,
const json_t *json, oauth2_http_call_ctx_t *ctx,
char **response,
oauth2_http_status_code_t *status_code)
{
bool rc = false;
char *json_str = NULL;
if (json)
json_str = json_dumps(json, JSON_PRESERVE_ORDER | JSON_COMPACT);
oauth2_http_call_ctx_content_type_set(log, ctx,
OAUTH2_CONTENT_TYPE_JSON);
rc = oauth2_http_call(log, url, json_str, ctx, response, status_code);
if (json_str)
oauth2_mem_free(json_str);
return rc;
}
/*
* query
*/
static bool _oauth2_http_request_query_parse(oauth2_log_t *log,
oauth2_http_request_t *request)
{
bool rc = false;
if (request == NULL)
goto end;
if (request->_parsed_query != NULL) {
rc = true;
goto end;
}
request->_parsed_query = oauth2_nv_list_init(log);
if (request->_parsed_query == NULL)
goto end;
rc = _oauth2_nv_list_parse(log, request->query, request->_parsed_query,
_OAUTH2_CHAR_AMP, _OAUTH2_CHAR_EQUAL, true,
false);
end:
return rc;
}
bool oauth2_http_request_query_param_add(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value)
{
bool rc = false;
char *query_str = NULL;
oauth2_debug(log, "enter: %s=%s", name, value);
if ((request == NULL) || (name == NULL))
goto end;
if (_oauth2_http_request_query_parse(log, request) == false)
goto end;
if (oauth2_nv_list_add(log, request->_parsed_query, name, value) ==
false)
goto end;
query_str =
oauth2_http_url_query_encode(log, NULL, request->_parsed_query);
rc = oauth2_http_request_query_set(log, request, query_str);
end:
if (query_str)
oauth2_mem_free(query_str);
oauth2_debug(log, "leave (%d)", rc);
return rc;
}
const char *oauth2_http_request_query_param_get(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name)
{
const char *value = NULL;
oauth2_debug(log, "enter: %s", name);
if ((request == NULL) || (name == NULL))
goto end;
if (_oauth2_http_request_query_parse(log, request) == false)
goto end;
value = oauth2_nv_list_get(log, request->_parsed_query, name);
end:
oauth2_debug(log, "leave: %s=%s", name, value ? value : "(null)");
return value;
}
bool oauth2_http_request_query_param_unset(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name)
{
bool rc = false;
char *query_str = NULL;
oauth2_debug(log, "enter: %s", name);
if ((request == NULL) || (name == NULL))
goto end;
if (_oauth2_http_request_query_parse(log, request) == false) {
oauth2_error(log, "_oauth2_http_request_query_parse failed");
goto end;
}
if (oauth2_nv_list_unset(log, request->_parsed_query, name) == false) {
oauth2_error(log, "oauth2_nv_list_unset failed");
goto end;
}
query_str =
oauth2_http_url_query_encode(log, NULL, request->_parsed_query);
rc = oauth2_http_request_query_set(log, request, query_str);
end:
if (query_str)
oauth2_mem_free(query_str);
oauth2_debug(log, "leave: rc=%d", rc);
return rc;
}
/*
* cookies
*/
static bool
_oauth2_http_request_get_parsed_cookies(oauth2_log_t *log,
oauth2_http_request_t *request)
{
bool rc = false;
const char *cookies = NULL;
if (request == NULL)
goto end;
if (request->_parsed_cookies != NULL) {
rc = true;
goto end;
}
request->_parsed_cookies = oauth2_nv_list_init(log);
if (request->_parsed_cookies == NULL)
goto end;
cookies = oauth2_http_request_header_cookie_get(log, request);
if (cookies == NULL) {
rc = true;
goto end;
}
rc = _oauth2_nv_list_parse(log, cookies, request->_parsed_cookies,
_OAUTH2_CHAR_SEMICOL, _OAUTH2_CHAR_EQUAL,
true, true);
end:
return rc;
}
static bool _oauth2_http_request_set_parsed_cookies_in_header(
oauth2_log_t *log, oauth2_http_request_t *request)
{
char *cookies = NULL;
oauth2_debug(log, "enter");
cookies = _oauth2_http_cookies_encode(log, request->_parsed_cookies);
if (cookies == NULL)
goto end;
_oauth2_http_request_header_cookie_set(log, request, cookies);
end:
oauth2_debug(log, "leave: %s", cookies);
if (cookies)
oauth2_mem_free(cookies);
return true;
}
char *oauth2_http_request_cookie_get(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, bool strip)
{
char *rv = NULL;
const char *value = NULL;
oauth2_debug(log, "enter: %s", name);
if ((request == NULL) || (name == NULL))
goto end;
if (_oauth2_http_request_get_parsed_cookies(log, request) == false)
goto end;
value = oauth2_nv_list_get(log, request->_parsed_cookies, name);
if (value == NULL)
goto end;
rv = oauth2_strdup(value);
if (strip == false)
goto end;
oauth2_nv_list_unset(log, request->_parsed_cookies, name);
_oauth2_http_request_set_parsed_cookies_in_header(log, request);
end:
oauth2_debug(log, "leave: %s=%s", name, rv ? rv : "(null)");
return rv;
}
bool oauth2_http_request_cookie_set(oauth2_log_t *log,
oauth2_http_request_t *request,
const char *name, const char *value)
{
bool rc = false;
oauth2_debug(log, "enter: %s=%s", name, value);
if ((request == NULL) || (name == NULL))
goto end;
if (_oauth2_http_request_get_parsed_cookies(log, request) == false)
goto end;
rc = oauth2_nv_list_set(log, request->_parsed_cookies, name, value);
if (rc == false)
goto end;
rc = _oauth2_http_request_set_parsed_cookies_in_header(log, request);
end:
oauth2_debug(log, "leave (%d)", rc);
return rc;
}
/*
* authentication
*/
bool oauth2_http_auth_client_cert(oauth2_log_t *log, const char *ssl_cert,
const char *ssl_key,
oauth2_http_call_ctx_t *ctx)
{
bool rc = false;
if ((ssl_cert == NULL) || (ssl_key == NULL))
goto end;
rc = oauth2_http_call_ctx_ssl_cert_set(log, ctx, ssl_cert);
if (rc == false)
goto end;
rc = oauth2_http_call_ctx_ssl_key_set(log, ctx, ssl_key);
end:
return rc;
}
bool oauth2_http_auth_basic(oauth2_log_t *log, const char *username,
const char *passwd, oauth2_http_call_ctx_t *ctx)
{
return oauth2_http_call_ctx_basic_auth_set(log, ctx, username, passwd,
false);
}
static bool oauth2_http_request_header_contains(
oauth2_log_t *log, const oauth2_http_request_t *request, const char *name,
char sepchar, const char *needle)
{
bool rc = false;
char *save_input = NULL, *val = NULL;
const char *value = NULL, *p = NULL;
if (name == NULL)
goto end;
value = oauth2_http_request_header_get(log, request, name);
if (value == NULL)
goto end;
save_input = oauth2_strdup(value);
p = save_input;
while (p && *p) {
val = oauth2_getword(&p, sepchar);
if (val == NULL)
break;
rc = (strncasecmp(val, needle, strlen(needle)) == 0);
oauth2_mem_free(val);
if (rc == true)
break;
}
end:
if (save_input)
oauth2_mem_free(save_input);
return rc;
}
bool oauth2_http_request_is_xml_http_request(
oauth2_log_t *log, const oauth2_http_request_t *request)
{
bool rc = false;
oauth2_debug(log, "enter");
if ((oauth2_http_request_header_x_requested_with_get(log, request) !=
NULL) &&
(strcasecmp(
oauth2_http_request_header_x_requested_with_get(log, request),
OAUTH2_HTTP_HDR_XML_HTTP_REQUEST) == 0)) {
rc = true;
goto end;
}
if ((oauth2_http_request_header_contains(
log, request, OAUTH2_HTTP_HDR_ACCEPT, _OAUTH2_CHAR_COMMA,
OAUTH2_CONTENT_TYPE_TEXT_HTML) == false) &&
(oauth2_http_request_header_contains(
log, request, OAUTH2_HTTP_HDR_ACCEPT, _OAUTH2_CHAR_COMMA,
OAUTH2_CONTENT_TYPE_APP_XHTML_XML) == false) &&
(oauth2_http_request_header_contains(
log, request, OAUTH2_HTTP_HDR_ACCEPT, _OAUTH2_CHAR_COMMA,
OAUTH2_CONTENT_TYPE_ANY) == false)) {
rc = true;
goto end;
}
end:
oauth2_debug(log, "return: %d", rc);
return rc;
}
bool oauth2_http_request_is_secure(oauth2_log_t *log,
const oauth2_http_request_t *request)
{
bool rc = false;
char *scheme = oauth2_http_request_scheme_get(log, request);
rc = (strcasecmp(scheme, "https") == 0);
oauth2_mem_free(scheme);
return rc;
}
typedef struct oauth2_http_response_t {
oauth2_nv_list_t *headers;
oauth2_http_status_code_t status_code;
} oauth2_http_response_t;
oauth2_http_response_t *oauth2_http_response_init(oauth2_log_t *log)
{
oauth2_http_response_t *response = NULL;
response = oauth2_mem_alloc(sizeof(oauth2_http_response_t));
if (response == NULL)
goto end;
response->headers = oauth2_nv_list_init(log);
response->status_code = 0;
end:
return response;
}
oauth2_http_response_t *
oauth2_http_response_clone(oauth2_log_t *log, const oauth2_http_response_t *src)
{
oauth2_http_response_t *dst = oauth2_http_response_init(log);
dst->headers = oauth2_nv_list_clone(log, src->headers);
dst->status_code = src->status_code;
return dst;
}
void oauth2_http_response_free(oauth2_log_t *log,
oauth2_http_response_t *response)
{
if (response == NULL)
goto end;
if (response->headers)
oauth2_nv_list_free(log, response->headers);
oauth2_mem_free(response);
end:
return;
}
bool oauth2_http_response_headers_set(oauth2_log_t *log,
oauth2_http_response_t *response,
const oauth2_nv_list_t *hdrs)
{
return false;
}
oauth2_nv_list_t *
oauth2_http_response_headers_get(oauth2_log_t *log,
const oauth2_http_response_t *response)
{
return response->headers;
}
/*
bool oauth2_http_response_status_code_set(oauth2_log_t *, oauth2_http_response_t
*, const oauth2_http_status_code_t) { return false;
}
oauth2_http_status_code_t oauth2_http_response_status_code_get(oauth2_log_t *,
const oauth2_http_response_t *) { return 0;
}
*/
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(http, response, status_code,
oauth2_http_status_code_t, uint)
bool oauth2_http_response_header_set(oauth2_log_t *log,
oauth2_http_response_t *response,
const char *name, const char *value)
{
return oauth2_nv_list_set(log, response->headers, name, value);
}
static bool oauth2_http_response_header_add(oauth2_log_t *log,
oauth2_http_response_t *response,
const char *name, const char *value)
{
return oauth2_nv_list_add(log, response->headers, name, value);
}
const char *oauth2_http_response_header_get(
oauth2_log_t *log, const oauth2_http_response_t *response, const char *name)
{
return oauth2_nv_list_get(log, response->headers, name);
}
typedef struct _oauth2_http_response_header_set_cookie_prefix_match_t {
const char *prefix;
const char *result;
} _oauth2_http_response_header_set_cookie_prefix_match_t;
static bool _oauth2_http_response_header_set_cookie_prefix_match(
oauth2_log_t *log, void *rec, const char *name, const char *value)
{
bool rc = true;
_oauth2_http_response_header_set_cookie_prefix_match_t *ctx =
(_oauth2_http_response_header_set_cookie_prefix_match_t *)rec;
if (strcasecmp(OAUTH2_HTTP_HDR_SET_COOKIE, name) == 0) {
oauth2_debug(log, "matching: value=%s prefix=%s", value,
ctx->prefix);
if (strstr(value, ctx->prefix) == value) {
ctx->result = value;
rc = false;
}
}
return rc;
}
const char *oauth2_http_response_header_set_cookie_prefix_get(
oauth2_log_t *log, oauth2_http_response_t *response, const char *prefix)
{
_oauth2_http_response_header_set_cookie_prefix_match_t ctx;
ctx.prefix = prefix;
ctx.result = NULL;
oauth2_http_response_headers_loop(
log, response, _oauth2_http_response_header_set_cookie_prefix_match,
(void *)&ctx);
oauth2_debug(log, "on search for %s, return: %s", prefix, ctx.result);
return ctx.result;
}
#define OAUTH2_HTTP_COOKIE_MAX_AGE_LENGTH_MAX 64
bool oauth2_http_response_cookie_set(oauth2_log_t *log,
oauth2_http_response_t *response,
const char *name, const char *value,
const char *path, const bool is_secure,
oauth2_time_t max_age)
{
bool rc = false;
char *str = NULL;
oauth2_nv_list_t *cookies = NULL;
char maxagestr[OAUTH2_HTTP_COOKIE_MAX_AGE_LENGTH_MAX];
if (value) {
cookies = oauth2_nv_list_init(log);
oauth2_nv_list_set(log, cookies, name, value);
str = _oauth2_http_cookies_encode(log, cookies);
if (str == NULL)
goto end;
} else {
str = oauth2_stradd(
NULL, name, "=;",
" Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0");
}
if (path)
str = oauth2_stradd(str, "; path", "=", path);
if ((value != NULL) && (max_age != OAUTH2_CFG_TIME_UNSET)) {
oauth2_snprintf(maxagestr,
OAUTH2_HTTP_COOKIE_MAX_AGE_LENGTH_MAX,
OAUTH2_TIME_T_FORMAT, max_age);
str = oauth2_stradd(str, "; Max-Age", "=", maxagestr);
}
if (is_secure)
str = oauth2_stradd(str, "; HttpOnly", "; Secure",
"; SameSite=None");
rc = oauth2_http_response_header_add(log, response,
OAUTH2_HTTP_HDR_SET_COOKIE, str);
end:
if (cookies)
oauth2_nv_list_free(log, cookies);
if (str)
oauth2_mem_free(str);
return rc;
}
void oauth2_http_response_headers_loop(oauth2_log_t *log,
const oauth2_http_response_t *response,
oauth2_nv_list_loop_cb_t *callback,
void *rec)
{
oauth2_nv_list_loop(log, response->headers, callback, rec);
}
liboauth2-1.4.4/src/ipc.c 0000664 0000000 0000000 00000015501 14161030125 0015112 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#ifndef _WIN32
#include
#include
#else
#include "mmap-windows.c"
#ifdef _MSC_VER
#define _unlink unlink
#endif
#endif
#include
#include
#include
#include
#include "oauth2/ipc.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "oauth2/version.h"
#include "util_int.h"
// from the sema.h docs
#define _OAUTH2_IPC_NAME_MAX 63
static char *_oauth2_ipc_get_name(oauth2_log_t *log, const char *type,
void *ptr)
{
char *rv = NULL;
rv = oauth2_mem_alloc(_OAUTH2_IPC_NAME_MAX);
oauth2_snprintf(rv, _OAUTH2_IPC_NAME_MAX, "/zzo-%s-%ld.%p", type,
(long int)getpid(), ptr ? ptr : 0);
return rv;
}
/*
* semaphore
*/
typedef struct oauth2_ipc_sema_t {
char *name;
sem_t *sema;
} oauth2_ipc_sema_t;
oauth2_ipc_sema_t *oauth2_ipc_sema_init(oauth2_log_t *log)
{
oauth2_ipc_sema_t *s = oauth2_mem_alloc(sizeof(oauth2_ipc_sema_t));
if (s) {
s->sema = NULL;
}
return s;
}
void oauth2_ipc_sema_free(oauth2_log_t *log, oauth2_ipc_sema_t *s)
{
if (s == NULL)
goto end;
if (s->sema != NULL) {
if (sem_close(s->sema) != 0)
oauth2_error(log, "sem_close() failed: %s ",
strerror(errno));
s->sema = NULL;
}
if (s->name)
oauth2_mem_free(s->name);
oauth2_mem_free(s);
end:
return;
}
bool oauth2_ipc_sema_post_config(oauth2_log_t *log, oauth2_ipc_sema_t *sema)
{
bool rc = false;
if (sema == NULL)
goto end;
if (sema->name) {
oauth2_mem_free(sema->name);
sema->name = NULL;
}
sema->name = _oauth2_ipc_get_name(log, "sema", sema);
if (sema->name == NULL)
goto end;
sema->sema = sem_open(sema->name, O_CREAT, 0644, 0);
if (sema->sema == SEM_FAILED) {
oauth2_error(
log,
"sem_open() failed to create named semaphore %s: %s (%d)",
sema->name, strerror(errno), errno);
sema->sema = NULL;
goto end;
}
if (sem_unlink(sema->name) != 0)
oauth2_error(log, "sem_unlink() failed: %s ", strerror(errno));
rc = true;
end:
return rc;
}
bool oauth2_ipc_sema_post(oauth2_log_t *log, oauth2_ipc_sema_t *sema)
{
bool rc = false;
int rv = 0;
if ((sema == NULL) || (sema->sema == NULL))
goto end;
rv = sem_post(sema->sema);
if (rv != 0) {
oauth2_error(log, "sem_post() failed: %s (%d)", strerror(errno),
errno);
goto end;
}
rc = true;
end:
return rc;
}
bool oauth2_ipc_sema_wait(oauth2_log_t *log, oauth2_ipc_sema_t *sema)
{
bool rc = true;
int rv = 0;
if ((sema == NULL) || (sema->sema == NULL))
goto end;
rv = sem_wait(sema->sema);
if (rv != 0) {
oauth2_error(log, "sem_wait() failed: %s (%d)", strerror(errno),
errno);
goto end;
}
rc = true;
end:
return rc;
}
bool oauth2_ipc_sema_trywait(oauth2_log_t *log, oauth2_ipc_sema_t *sema)
{
bool rc = true;
int rv = 0;
if ((sema == NULL) || (sema->sema == NULL))
goto end;
rv = sem_trywait(sema->sema);
if (rv != 0) {
if (errno == EAGAIN)
rc = false;
else
oauth2_error(log, "sem_trywait() failed: %s (%d)",
strerror(errno), errno);
}
end:
return rc;
}
/*
* mutex
*/
typedef struct oauth2_ipc_mutex_t {
oauth2_ipc_sema_t *mutex;
} oauth2_ipc_mutex_t;
oauth2_ipc_mutex_t *oauth2_ipc_mutex_init(oauth2_log_t *log)
{
oauth2_ipc_mutex_t *m = oauth2_mem_alloc(sizeof(oauth2_ipc_mutex_t));
if (m) {
m->mutex = oauth2_ipc_sema_init(log);
}
return m;
}
void oauth2_ipc_mutex_free(oauth2_log_t *log, oauth2_ipc_mutex_t *m)
{
if ((m == NULL) && (m->mutex == NULL))
goto end;
oauth2_ipc_sema_free(log, m->mutex);
m->mutex = NULL;
oauth2_mem_free(m);
end:
return;
}
bool oauth2_ipc_mutex_post_config(oauth2_log_t *log, oauth2_ipc_mutex_t *m)
{
bool rc = false;
if ((m == NULL) || (m->mutex == NULL))
goto end;
rc = oauth2_ipc_sema_post_config(log, m->mutex);
if (rc == false)
goto end;
rc = oauth2_ipc_sema_post(log, m->mutex);
end:
return rc;
}
bool oauth2_ipc_mutex_lock(oauth2_log_t *log, oauth2_ipc_mutex_t *m)
{
bool rc = false;
if ((m == NULL) || (m->mutex == NULL))
goto end;
rc = oauth2_ipc_sema_wait(log, m->mutex);
end:
return rc;
}
bool oauth2_ipc_mutex_unlock(oauth2_log_t *log, oauth2_ipc_mutex_t *m)
{
bool rc = false;
if ((m == NULL) || (m->mutex == NULL))
goto end;
rc = oauth2_ipc_sema_post(log, m->mutex);
end:
return rc;
}
/*
* shared memory
*/
typedef struct oauth2_ipc_shm_t {
oauth2_ipc_mutex_t *mutex;
oauth2_ipc_sema_t *num;
size_t size;
void *ptr;
} oauth2_ipc_shm_t;
oauth2_ipc_shm_t *oauth2_ipc_shm_init(oauth2_log_t *log, size_t size)
{
oauth2_ipc_shm_t *shm = oauth2_mem_alloc(sizeof(oauth2_ipc_shm_t));
shm->mutex = oauth2_ipc_mutex_init(log);
shm->num = oauth2_ipc_sema_init(log);
shm->ptr = NULL;
shm->size = size;
return shm;
}
void oauth2_ipc_shm_free(oauth2_log_t *log, oauth2_ipc_shm_t *shm)
{
if (shm == NULL)
goto end;
if (shm->mutex)
oauth2_ipc_mutex_free(log, shm->mutex);
shm->mutex = NULL;
if (shm->ptr) {
if (munmap(shm->ptr, shm->size) < 0)
oauth2_error(log, "munmap() failed: %s",
strerror(errno));
shm->ptr = NULL;
}
if (shm->num) {
oauth2_ipc_sema_free(log, shm->num);
shm->num = NULL;
}
oauth2_mem_free(shm);
end:
return;
}
bool oauth2_ipc_shm_post_config(oauth2_log_t *log, oauth2_ipc_shm_t *shm)
{
bool rc = false;
if (shm == NULL)
goto end;
rc = oauth2_ipc_sema_post_config(log, shm->num);
if (rc == false)
goto end;
rc = oauth2_ipc_mutex_post_config(log, shm->mutex);
if (rc == false)
goto end;
oauth2_debug(log, "creating anonymous shm");
shm->ptr = mmap(0, shm->size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (shm->ptr == MAP_FAILED) {
oauth2_error(log, "mmap() failed: %s", strerror(errno));
goto end;
}
rc = oauth2_ipc_sema_post(log, shm->num);
end:
return rc;
}
bool oauth2_ipc_shm_child_init(oauth2_log_t *log, oauth2_ipc_shm_t *shm)
{
return shm ? oauth2_ipc_sema_post(log, shm->num) : false;
}
void *oauth2_ipc_shm_get(oauth2_log_t *log, oauth2_ipc_shm_t *shm)
{
return shm ? shm->ptr : NULL;
}
liboauth2-1.4.4/src/jose.c 0000664 0000000 0000000 00000147226 14161030125 0015311 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/jose.h"
#include "oauth2/http.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "cjose/cjose.h"
#include "cache_int.h"
#include "jose_int.h"
#include "util_int.h"
#include
#include
#include
#include
#include
#define _OAUTH2_JOSE_OPENSSL_ERR_LOG(log, function) \
oauth2_error(log, "%s failed: %s", function, \
ERR_error_string(ERR_get_error(), NULL))
#define _OAUTH2_JOSE_JANSSON_ERR_LOG(log, msg, json_err) \
oauth2_error(log, "%s failed: %s", msg, json_err.text)
#if (OPENSSL_VERSION_NUMBER < 0x10100000) || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX *EVP_MD_CTX_new()
{
return oauth2_mem_alloc(sizeof(EVP_MD_CTX));
}
void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
{
if (ctx)
oauth2_mem_free(ctx);
}
#endif
static oauth2_jose_jwk_t *oauth2_jose_jwk_new()
{
oauth2_jose_jwk_t *jwk = oauth2_mem_alloc(sizeof(oauth2_jose_jwk_t));
jwk->jwk = NULL;
jwk->kid = NULL;
return jwk;
}
static oauth2_jose_jwk_t *oauth2_jose_jwk_oct_new(oauth2_log_t *log,
unsigned char *key,
unsigned int key_len)
{
oauth2_jose_jwk_t *rv = NULL;
cjose_err err;
cjose_jwk_t *c_jwk = NULL;
c_jwk = cjose_jwk_create_oct_spec(key, key_len, &err);
if (c_jwk == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jwk_create_oct_spec",
err);
goto end;
}
oauth2_trace1(log, "jwk oct spec created", key_len);
rv = oauth2_jose_jwk_new();
if (rv == NULL)
goto end;
rv->jwk = c_jwk;
end:
return rv;
}
void oauth2_jose_jwk_release(oauth2_jose_jwk_t *jwk)
{
if (jwk->jwk) {
cjose_jwk_release(jwk->jwk);
jwk->jwk = NULL;
}
if (jwk->kid) {
oauth2_mem_free(jwk->kid);
jwk->kid = NULL;
}
oauth2_mem_free(jwk);
}
bool oauth2_jose_hash_bytes(oauth2_log_t *log, const char *digest,
const unsigned char *src, unsigned int src_len,
unsigned char **dst, unsigned int *dst_len)
{
const EVP_MD *evp_digest = NULL;
EVP_MD_CTX *ctx = NULL;
unsigned char md_value[EVP_MAX_MD_SIZE];
bool rc = false;
oauth2_debug(log, "enter");
if ((dst == NULL) || (dst_len == NULL))
goto end;
if ((src == NULL) || (src_len == 0)) {
oauth2_warn(log, "cannot hash empty string");
goto end;
}
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
_OAUTH2_JOSE_OPENSSL_ERR_LOG(log, "EVP_MD_CTX_new");
goto end;
}
EVP_MD_CTX_init(ctx);
if ((evp_digest = EVP_get_digestbyname(digest)) == NULL) {
oauth2_error(
log,
"no OpenSSL digest algorithm found for algorithm \"%s\"",
digest);
goto end;
}
if (!EVP_DigestInit_ex(ctx, evp_digest, NULL))
goto end;
if (!EVP_DigestUpdate(ctx, src, src_len))
goto end;
if (!EVP_DigestFinal(ctx, md_value, dst_len))
goto end;
*dst = oauth2_mem_alloc((size_t)*dst_len);
if (*dst == NULL) {
*dst_len = 0;
goto end;
}
memcpy(*dst, md_value, *dst_len);
rc = true;
end:
if (ctx)
EVP_MD_CTX_free(ctx);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
bool oauth2_jose_jwk_create_symmetric(oauth2_log_t *log, const char *secret,
const char *hash_algo,
oauth2_jose_jwk_t **jwk)
{
unsigned char *key = NULL;
unsigned int key_len = 0;
bool rv = false, rc = false;
oauth2_debug(log, "enter");
if (jwk == NULL)
goto end;
oauth2_trace1(log, "secret: %s", secret);
if (hash_algo != NULL) {
/*
* hash the client_secret first, this is OpenID Connect specific
*/
rc = oauth2_jose_hash_bytes(
log, hash_algo, (const unsigned char *)secret,
secret ? strlen(secret) : 0, &key, &key_len);
if (rc == false) {
oauth2_error(log, "oauth2_jose_hash_bytes failed");
goto end;
}
} else if (secret != NULL) {
key_len = strlen(secret);
key = (unsigned char *)oauth2_strdup(secret);
}
oauth2_trace1(log, "key and key_len (%d) set", key_len);
*jwk = oauth2_jose_jwk_oct_new(log, key, key_len);
rv = (*jwk != NULL);
end:
if (key)
oauth2_mem_free(key);
oauth2_debug(log, "leave");
return rv;
}
bool oauth2_jose_encrypt(oauth2_log_t *log, const char *secret,
const char *s_sig_payload, char **cser)
{
bool rv = false, rc = false;
cjose_err err;
oauth2_jose_jwk_t *jwk = NULL;
cjose_jws_t *jwt = NULL;
cjose_jwe_t *jwe = NULL;
cjose_header_t *sig_hdr = NULL, *enc_hdr = NULL;
char *s_enc_payload = NULL;
oauth2_debug(log, "enter");
if (cser == NULL)
goto end;
if (oauth2_jose_jwk_create_symmetric(
log, secret, OAUTH2_JOSE_OPENSSL_ALG_SHA256, &jwk) == false) {
oauth2_error(log, "oauth2_jose_jwk_create_symmetric failed");
goto end;
}
oauth2_trace1(log, "hashed symmetric key created: %s",
OAUTH2_JOSE_OPENSSL_ALG_SHA256);
sig_hdr = cjose_header_new(&err);
if (sig_hdr == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_header_new for signature",
err);
goto end;
}
oauth2_trace1(log, "inner header created");
rc =
cjose_header_set(sig_hdr, CJOSE_HDR_ALG, CJOSE_HDR_ALG_HS256, &err);
if (rc == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(
log, "cjose_header_set for signature alg", err);
goto end;
}
oauth2_trace1(log, "inner header \"%s\" set: %s", CJOSE_HDR_ALG,
CJOSE_HDR_ALG_HS256);
jwt = cjose_jws_sign(jwk->jwk, sig_hdr, (const uint8_t *)s_sig_payload,
s_sig_payload ? strlen(s_sig_payload) : 0, &err);
if (jwt == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jws_sign", err);
goto end;
}
oauth2_trace1(log, "inner jwt signed");
rc = cjose_jws_export(jwt, (const char **)&s_enc_payload, &err);
if (rc == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jws_export", err);
goto end;
}
oauth2_trace1(log, "inner jwt exported: %s", s_enc_payload);
enc_hdr = cjose_header_new(&err);
if (enc_hdr == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(
log, "cjose_header_new for encryption", err);
goto end;
}
oauth2_trace1(log, "outer header created");
rc = cjose_header_set(enc_hdr, CJOSE_HDR_ALG, CJOSE_HDR_ALG_DIR, &err);
if (rc == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(
log, "cjose_header_set for encryption alg", err);
goto end;
}
oauth2_trace1(log, "outer header \"%s\" set: %s", CJOSE_HDR_ALG,
CJOSE_HDR_ALG_DIR);
rc = cjose_header_set(enc_hdr, CJOSE_HDR_ENC, CJOSE_HDR_ENC_A256GCM,
&err);
if (rc == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(
log, "cjose_header_set for encryption enc", err);
goto end;
}
oauth2_trace1(log, "outer header \"%s\" set: %s", CJOSE_HDR_ENC,
CJOSE_HDR_ENC_A256GCM);
jwe =
cjose_jwe_encrypt(jwk->jwk, enc_hdr, (const uint8_t *)s_enc_payload,
strlen(s_enc_payload), &err);
if (jwt == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jwe_encrypt", err);
goto end;
}
oauth2_trace1(log, "jwe created");
*cser = cjose_jwe_export(jwe, &err);
if (*cser == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jwe_export", err);
goto end;
}
oauth2_trace1(log, "jwe exported: %s", *cser);
rv = true;
end:
if (jwe)
cjose_jwe_release(jwe);
if (jwk)
oauth2_jose_jwk_release(jwk);
if (jwt)
cjose_jws_release(jwt);
if (sig_hdr)
cjose_header_release(sig_hdr);
if (enc_hdr)
cjose_header_release(enc_hdr);
oauth2_debug(log, "leave");
return rv;
}
bool oauth2_jose_jwt_encrypt(oauth2_log_t *log, const char *secret,
json_t *payload, char **cser)
{
bool rc = false;
char *s_sig_payload = NULL;
oauth2_debug(log, "enter");
if (cser == NULL)
goto end;
s_sig_payload =
payload ? json_dumps(payload, JSON_PRESERVE_ORDER | JSON_COMPACT)
: NULL;
oauth2_trace1(log, "JSON payload serialized: %s", s_sig_payload);
rc = oauth2_jose_encrypt(log, secret, s_sig_payload, cser);
end:
if (s_sig_payload)
oauth2_mem_free(s_sig_payload);
oauth2_debug(log, "leave");
return rc;
}
bool oauth2_jose_decrypt(oauth2_log_t *log, const char *secret,
const char *cser, char **result)
{
// oauth2_debug(log, "enter: JWT header=%s",
// oidc_proto_peek_jwt_header(log, cser, NULL));
bool rv = false, rc = false;
cjose_err err;
oauth2_jose_jwk_t *jwk = NULL;
cjose_jws_t *jwt = NULL;
cjose_jwe_t *jwe = NULL;
uint8_t *s_decrypted = NULL, *s_payload = NULL;
size_t dec_len, payload_len;
oauth2_debug(log, "enter");
if (result == NULL)
goto end;
if (oauth2_jose_jwk_create_symmetric(
log, secret, OAUTH2_JOSE_OPENSSL_ALG_SHA256, &jwk) == false) {
oauth2_error(log, "oauth2_jose_jwk_create_symmetric failed");
goto end;
}
oauth2_trace1(log, "symmetric key created");
jwe = cjose_jwe_import(cser, cser ? strlen(cser) : 0, &err);
if (jwe == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jwe_import", err);
goto end;
}
oauth2_trace1(log, "jwe imported");
s_decrypted = cjose_jwe_decrypt(jwe, jwk->jwk, &dec_len, &err);
if (s_decrypted == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jwe_decrypt", err);
goto end;
}
oauth2_trace1(log, "jwe decrypted");
jwt = cjose_jws_import((const char *)s_decrypted, dec_len, &err);
if (jwt == NULL) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jws_import", err);
goto end;
}
oauth2_trace1(log, "innner jws imported");
rc = cjose_jws_verify(jwt, jwk->jwk, &err);
if (rc == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jws_verify", err);
goto end;
}
oauth2_trace1(log, "inner jws verified");
rc = cjose_jws_get_plaintext(jwt, &s_payload, &payload_len, &err);
if (rc == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "cjose_jws_get_plaintext", err);
goto end;
}
oauth2_trace1(log, "plaintext retrieved");
*result = oauth2_mem_alloc(payload_len + 1);
strncpy(*result, (const char *)s_payload, payload_len);
(*result)[payload_len] = '\0';
oauth2_trace1(log, "plaintext copied");
rv = true;
end:
if (s_decrypted)
oauth2_mem_free(s_decrypted);
if (jwe)
cjose_jwe_release(jwe);
if (jwk)
oauth2_jose_jwk_release(jwk);
if (jwt)
cjose_jws_release(jwt);
oauth2_debug(log, "leave");
return rv;
}
bool oauth2_jose_jwt_decrypt(oauth2_log_t *log, const char *secret,
const char *cser, json_t **result)
{
bool rc = false;
char *payload = NULL;
json_error_t json_error;
oauth2_debug(log, "enter");
if ((secret == NULL) || (cser == NULL) || (result == NULL))
goto end;
if (oauth2_jose_decrypt(log, secret, cser, &payload) == false)
goto end;
*result = json_loads(payload, 0, &json_error);
if (*result == NULL) {
_OAUTH2_JOSE_JANSSON_ERR_LOG(log, "json_loads", json_error);
goto end;
}
oauth2_trace1(log, "payload parsed to JSON");
rc = true;
end:
if (payload)
oauth2_mem_free(payload);
oauth2_debug(log, "leave");
return rc;
}
#define OAUTH2_JTI_LENGTH 16
char *oauth2_jwt_create(oauth2_log_t *log, cjose_jwk_t *jwk, const char *alg,
const char *iss, const char *sub, const char *client_id,
const char *aud, oauth2_uint_t exp, bool include_iat,
bool include_jti)
{
char *rv = NULL;
char *payload = NULL;
json_t *assertion = NULL;
cjose_header_t *hdr = NULL;
cjose_jws_t *jws = NULL;
const char *jwt = NULL;
cjose_err err;
char *jti = NULL;
oauth2_debug(log, "enter");
if (jwk == NULL)
goto end;
assertion = json_object();
if (include_jti) {
jti = oauth2_rand_str(log, OAUTH2_JTI_LENGTH);
json_object_set_new(assertion, OAUTH2_CLAIM_JTI,
json_string(jti));
}
if (iss)
json_object_set_new(assertion, OAUTH2_CLAIM_ISS,
json_string(iss));
if (sub)
json_object_set_new(assertion, OAUTH2_CLAIM_SUB,
json_string(sub));
if (aud)
json_object_set_new(assertion, OAUTH2_CLAIM_AUD,
json_string(aud));
json_object_set_new(assertion, OAUTH2_CLAIM_EXP,
json_integer(oauth2_time_now_sec() + exp));
if (include_iat)
json_object_set_new(assertion, OAUTH2_CLAIM_IAT,
json_integer(oauth2_time_now_sec()));
payload = json_dumps(assertion, JSON_PRESERVE_ORDER | JSON_COMPACT);
hdr = cjose_header_new(&err);
if (hdr == NULL) {
oauth2_error(log, "cjose_header_new failed: %s", err.message);
goto end;
}
if (cjose_header_set(hdr, CJOSE_HDR_ALG, alg, &err) == false) {
oauth2_error(log, "cjose_header_set %s:%s failed: %s",
CJOSE_HDR_ALG, alg, err.message);
goto end;
}
if (cjose_header_set(hdr, OAUTH2_JOSE_HDR_TYP, OAUTH2_JOSE_HDR_TYP_JWT,
&err) == false) {
oauth2_error(log, "cjose_header_set %s:%s failed: %s",
OAUTH2_JOSE_HDR_TYP, OAUTH2_JOSE_HDR_TYP_JWT,
err.message);
goto end;
}
jws = cjose_jws_sign(jwk, hdr, (const uint8_t *)payload,
strlen(payload), &err);
if (jws == NULL) {
oauth2_error(log, "cjose_jws_sign failed: %s", err.message);
goto end;
}
if (cjose_jws_export(jws, &jwt, &err) == false) {
oauth2_error(log, "cjose_jws_export failed: %s", err.message);
goto end;
}
rv = oauth2_strndup(jwt, strlen(jwt));
end:
if (jti)
oauth2_mem_free(jti);
if (assertion)
json_decref(assertion);
if (payload)
free(payload);
if (hdr)
cjose_header_release(hdr);
if (jws)
cjose_jws_release(jws);
oauth2_debug(log, "leave");
return rv;
}
bool oauth2_jose_hash2s(oauth2_log_t *log, const char *digest, const char *src,
char **dst)
{
bool rc = false;
unsigned char *hash_bytes = NULL;
unsigned int hash_bytes_len = 0;
if (oauth2_jose_hash_bytes(log, digest, (const unsigned char *)src,
strlen(src), &hash_bytes,
&hash_bytes_len) == false)
goto end;
*dst = _oauth2_bytes2str(log, hash_bytes, hash_bytes_len);
rc = true;
end:
if (hash_bytes)
oauth2_mem_free(hash_bytes);
return rc;
}
_OAUTH2_CFG_CTX_INIT_START(oauth2_uri_ctx)
ctx->endpoint = NULL;
ctx->cache = NULL;
ctx->expiry_s = OAUTH2_CFG_UINT_UNSET;
_OAUTH2_CFG_CTX_INIT_END
_OAUTH2_CFG_CTX_CLONE_START(oauth2_uri_ctx)
dst->endpoint = oauth2_cfg_endpoint_clone(log, src->endpoint);
dst->cache = src->cache;
dst->expiry_s = src->expiry_s;
_OAUTH2_CFG_CTX_CLONE_END
_OAUTH2_CFG_CTX_FREE_START(oauth2_uri_ctx)
if (ctx->endpoint)
oauth2_cfg_endpoint_free(log, ctx->endpoint);
_OAUTH2_CFG_CTX_FREE_END
static oauth2_jose_jwk_list_t *oauth2_jose_jwk_list_init(oauth2_log_t *log)
{
oauth2_jose_jwk_list_t *list =
(oauth2_jose_jwk_list_t *)oauth2_mem_alloc(
sizeof(oauth2_jose_jwk_list_t));
list->jwk = oauth2_jose_jwk_new();
list->next = NULL;
return list;
}
static oauth2_jose_jwk_list_t *
oauth2_jose_jwk_list_clone(oauth2_log_t *log, oauth2_jose_jwk_list_t *src)
{
oauth2_jose_jwk_list_t *dst = NULL, *ptr = NULL, *last = NULL,
*elem = NULL;
cjose_err err;
ptr = src;
while (ptr) {
elem = oauth2_jose_jwk_list_init(log);
elem->jwk->kid = oauth2_strdup(ptr->jwk->kid);
err.code = CJOSE_ERR_NONE;
elem->jwk->jwk = cjose_jwk_retain(ptr->jwk->jwk, &err);
if ((elem->jwk->jwk == NULL) && (err.code != CJOSE_ERR_NONE)) {
oauth2_error(log, "cjose_jwk_retain failed: %s",
err.message);
oauth2_jose_jwk_list_free(log, elem);
continue;
}
if (dst == NULL) {
dst = elem;
last = dst;
} else {
last->next = elem;
last = last->next;
}
ptr = ptr->next;
}
return dst;
}
void oauth2_jose_jwk_list_free(oauth2_log_t *log, oauth2_jose_jwk_list_t *keys)
{
oauth2_jose_jwk_list_t *ptr = NULL;
ptr = keys;
while (ptr) {
keys = keys->next;
oauth2_jose_jwk_release(ptr->jwk);
oauth2_mem_free(ptr);
ptr = keys;
}
}
static oauth2_jose_jwk_list_t *
oauth2_jose_jwks_list_resolve(oauth2_log_t *, oauth2_jose_jwks_provider_t *,
bool *);
static oauth2_jose_jwk_list_t *
oauth2_jose_jwks_uri_resolve(oauth2_log_t *, oauth2_jose_jwks_provider_t *,
bool *);
static oauth2_jose_jwk_list_t *
oauth2_jose_jwks_eckey_url_resolve(oauth2_log_t *,
oauth2_jose_jwks_provider_t *, bool *);
static oauth2_jose_jwks_provider_t *
_oauth2_jose_jwks_provider_init(oauth2_log_t *log,
oauth2_jose_jwks_provider_type_t type)
{
oauth2_jose_jwks_provider_t *provider =
(oauth2_jose_jwks_provider_t *)oauth2_mem_alloc(
sizeof(oauth2_jose_jwks_provider_t));
provider->type = type;
switch (type) {
case OAUTH2_JOSE_JWKS_PROVIDER_LIST:
provider->resolve = oauth2_jose_jwks_list_resolve;
provider->jwks = NULL;
break;
case OAUTH2_JOSE_JWKS_PROVIDER_JWKS_URI:
provider->jwks_uri = oauth2_uri_ctx_init(log);
provider->resolve = oauth2_jose_jwks_uri_resolve;
break;
case OAUTH2_JOSE_JWKS_PROVIDER_ECKEY_URI:
provider->jwks_uri = oauth2_uri_ctx_init(log);
provider->resolve = oauth2_jose_jwks_eckey_url_resolve;
break;
}
return provider;
}
static oauth2_jose_jwks_provider_t *
_oauth2_jose_jwks_provider_clone(oauth2_log_t *log,
oauth2_jose_jwks_provider_t *src)
{
oauth2_jose_jwks_provider_t *dst = NULL;
if (src == NULL)
goto end;
dst = (oauth2_jose_jwks_provider_t *)oauth2_mem_alloc(
sizeof(oauth2_jose_jwks_provider_t));
dst->type = src->type;
dst->resolve = src->resolve;
switch (src->type) {
case OAUTH2_JOSE_JWKS_PROVIDER_LIST:
dst->jwks = oauth2_jose_jwk_list_clone(log, src->jwks);
break;
case OAUTH2_JOSE_JWKS_PROVIDER_JWKS_URI:
case OAUTH2_JOSE_JWKS_PROVIDER_ECKEY_URI:
dst->jwks_uri = oauth2_uri_ctx_clone(log, src->jwks_uri);
break;
}
end:
return dst;
}
static void
_oauth2_jose_jwks_provider_free(oauth2_log_t *log,
oauth2_jose_jwks_provider_t *provider)
{
if (provider == NULL)
goto end;
switch (provider->type) {
case OAUTH2_JOSE_JWKS_PROVIDER_LIST:
if (provider->jwks)
oauth2_jose_jwk_list_free(log, provider->jwks);
break;
case OAUTH2_JOSE_JWKS_PROVIDER_JWKS_URI:
case OAUTH2_JOSE_JWKS_PROVIDER_ECKEY_URI:
if (provider->jwks_uri)
oauth2_uri_ctx_free(log, provider->jwks_uri);
break;
}
oauth2_mem_free(provider);
end:
return;
}
oauth2_jose_jwt_validate_claim_t oauth2_parse_validate_claim_option(
oauth2_log_t *log, const char *value,
oauth2_jose_jwt_validate_claim_t default_value)
{
oauth2_jose_jwt_validate_claim_t result = default_value;
if (value == NULL)
goto end;
if (strcasecmp(value, "optional") == 0) {
result = OAUTH2_JOSE_JWT_VALIDATE_CLAIM_OPTIONAL;
goto end;
}
if (strcasecmp(value, "skip") == 0) {
result = OAUTH2_JOSE_JWT_VALIDATE_CLAIM_SKIP;
goto end;
}
if (strcasecmp(value, "required") == 0) {
result = OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED;
goto end;
}
end:
return result;
}
static const char *
_oauth2_validate_claim_option2s(oauth2_jose_jwt_validate_claim_t value)
{
const char *result = "";
if (value == OAUTH2_JOSE_JWT_VALIDATE_CLAIM_OPTIONAL) {
result = "optional";
goto end;
}
if (value == OAUTH2_JOSE_JWT_VALIDATE_CLAIM_SKIP) {
result = "skip";
goto end;
}
if (value == OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED) {
result = "required";
goto end;
}
end:
return result;
}
#define OAUTH2_JOSE_JWT_IAT_SLACK_DEFAULT (oauth2_uint_t)10
#define OAUTH2_JOSE_JWT_IAT_SLACK_BEFORE "verify.iat.slack_before"
#define OAUTH2_JOSE_JWT_IAT_SLACK_AFTER "verify.iat.slack_after"
#define OAUTH2_JOSE_JWT_ISS_VALIDATE "verify.iss"
#define OAUTH2_JOSE_JWT_EXP_VALIDATE "verify.exp"
#define OAUTH2_JOSE_JWT_IAT_VALIDATE "verify.iat"
_OAUTH2_CFG_CTX_INIT_START(oauth2_jose_jwt_verify_ctx)
ctx->exp_validate = OAUTH2_CFG_UINT_UNSET;
ctx->iat_validate = OAUTH2_CFG_UINT_UNSET;
ctx->iss_validate = OAUTH2_CFG_UINT_UNSET;
ctx->iat_slack_after = OAUTH2_CFG_UINT_UNSET;
ctx->iat_slack_before = OAUTH2_CFG_UINT_UNSET;
ctx->jwks_provider = NULL;
_OAUTH2_CFG_CTX_INIT_END
_OAUTH2_CFG_CTX_CLONE_START(oauth2_jose_jwt_verify_ctx)
dst->exp_validate = src->exp_validate;
dst->iat_slack_after = src->iat_slack_after;
dst->iat_slack_before = src->iat_slack_before;
dst->iat_validate = src->iat_validate;
dst->iss_validate = src->iss_validate;
dst->jwks_provider = _oauth2_jose_jwks_provider_clone(log, src->jwks_provider);
_OAUTH2_CFG_CTX_CLONE_END
_OAUTH2_CFG_CTX_FREE_START(oauth2_jose_jwt_verify_ctx)
if (ctx->jwks_provider)
_oauth2_jose_jwks_provider_free(log, ctx->jwks_provider);
_OAUTH2_CFG_CTX_FREE_END
_OAUTH2_CFG_CTX_FUNCS(oauth2_jose_jwt_verify_ctx)
bool oauth2_jose_jwt_verify_set_options(
oauth2_log_t *log, oauth2_jose_jwt_verify_ctx_t *jwt_verify,
oauth2_jose_jwks_provider_type_t type, const oauth2_nv_list_t *params)
{
jwt_verify->jwks_provider = _oauth2_jose_jwks_provider_init(log, type);
jwt_verify->iss_validate = oauth2_parse_validate_claim_option(
log, oauth2_nv_list_get(log, params, OAUTH2_JOSE_JWT_ISS_VALIDATE),
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_OPTIONAL);
jwt_verify->exp_validate = oauth2_parse_validate_claim_option(
log, oauth2_nv_list_get(log, params, OAUTH2_JOSE_JWT_EXP_VALIDATE),
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_OPTIONAL);
jwt_verify->iat_validate = oauth2_parse_validate_claim_option(
log, oauth2_nv_list_get(log, params, OAUTH2_JOSE_JWT_IAT_VALIDATE),
OAUTH2_JOSE_JWT_VALIDATE_CLAIM_OPTIONAL);
// TODO: this is probably different (default -1) for id_token's
// would we need to pass all flags explicitly in init?
jwt_verify->iat_slack_before = oauth2_parse_uint(
log,
oauth2_nv_list_get(log, params, OAUTH2_JOSE_JWT_IAT_SLACK_BEFORE),
OAUTH2_CFG_UINT_UNSET);
jwt_verify->iat_slack_after = oauth2_parse_uint(
log,
oauth2_nv_list_get(log, params, OAUTH2_JOSE_JWT_IAT_SLACK_AFTER),
OAUTH2_JOSE_JWT_IAT_SLACK_DEFAULT);
// TODO: calculate rc based on previous calls
return true;
}
typedef struct oauth2_jose_jwt_verify_jwk_ctx_t {
cjose_jws_t *jws;
const char *kid;
bool verified;
} oauth2_jose_jwt_verify_jwk_ctx_t;
static bool _oauth2_jose_jwt_verify_jwk(oauth2_log_t *log, void *rec,
const char *kid,
const oauth2_jose_jwk_t *jwk)
{
bool rc = true;
cjose_err err;
oauth2_jose_jwt_verify_jwk_ctx_t *ctx =
(oauth2_jose_jwt_verify_jwk_ctx_t *)rec;
oauth2_debug(log, "enter: jws kid=%s, jwk kid=%s", ctx->kid, kid);
if ((ctx == NULL) || (jwk == NULL))
goto end;
// NB: kid can be ""
if ((ctx->kid != NULL) && (kid != NULL) && (strcmp(kid, "") != 0) &&
(strcmp(ctx->kid, kid) != 0))
goto end;
if (cjose_jws_verify(ctx->jws, jwk->jwk, &err) == true) {
oauth2_debug(log, "cjose_jws_verify returned true");
ctx->verified = true;
// break the loop
rc = false;
}
end:
oauth2_debug(log, "leave: rc=%d", rc == false);
return rc;
}
char *oauth2_jose_jwt_header_peek(oauth2_log_t *log,
const char *compact_encoded_jwt,
const char **alg)
{
char *input = NULL, *result = NULL;
json_t *json = NULL;
char *p = NULL;
size_t result_len;
char *rv = NULL;
if (compact_encoded_jwt == NULL)
goto end;
p = strstr(compact_encoded_jwt, ".");
if (p == NULL)
goto end;
input = oauth2_strndup(compact_encoded_jwt,
strlen(compact_encoded_jwt) - strlen(p));
oauth2_debug(log, "decoding: %s (%d-%d=%d)", input,
strlen(compact_encoded_jwt), strlen(p),
strlen(compact_encoded_jwt) - strlen(p));
if (oauth2_base64url_decode(log, input, (uint8_t **)&result,
&result_len) == false)
goto end;
rv = oauth2_strndup(result, result_len);
oauth2_debug(log, "decoded: %s", rv);
if (oauth2_json_decode_object(log, rv, &json) == false) {
oauth2_mem_free(rv);
rv = NULL;
goto end;
}
if ((json == NULL) || (alg == NULL))
goto end;
*alg = json_string_value(json_object_get(json, CJOSE_HDR_ALG));
end:
if (input)
oauth2_mem_free(input);
if (result)
oauth2_mem_free(result);
if (json)
json_decref(json);
return rv;
}
typedef bool(oauth2_jose_verification_keys_loop_cb_t)(
oauth2_log_t *log, void *rec, const char *kid,
const oauth2_jose_jwk_t *jwk);
static void _oauth2_jose_verification_keys_loop(
oauth2_log_t *log, const oauth2_jose_jwk_list_t *list,
oauth2_jose_verification_keys_loop_cb_t *callback, void *rec)
{
const oauth2_jose_jwk_list_t *ptr = NULL;
if ((list == NULL) || (callback == NULL))
goto end;
for (ptr = list; ptr; ptr = ptr->next) {
if (callback(log, rec, ptr->jwk->kid, ptr->jwk) == false)
break;
}
end:
return;
}
static bool
_oauth2_jose_jwt_validate_iss(oauth2_log_t *log, const json_t *json_payload,
const char *iss,
oauth2_jose_jwt_validate_claim_t validate)
{
bool rc = false;
char *value = NULL;
oauth2_debug(log, "enter: iss=%s, validate=%s", iss,
_oauth2_validate_claim_option2s(validate));
if (validate == OAUTH2_JOSE_JWT_VALIDATE_CLAIM_SKIP) {
rc = true;
goto end;
}
if (iss == NULL) {
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
if (oauth2_json_string_get(log, json_payload, OAUTH2_JOSE_JWT_ISS,
&value, NULL) == false) {
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
if (value == NULL) {
oauth2_error(log,
"JWT did not contain an \"%s\" string (requested "
"value: %s)",
OAUTH2_JOSE_JWT_ISS, iss);
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
if (strcmp(iss, value) != 0) {
oauth2_error(log,
"requested issuer (%s) does not match received "
"\"%s\" value in id_token (%s)",
iss, OAUTH2_JOSE_JWT_ISS, value);
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool
_oauth2_jose_jwt_validate_exp(oauth2_log_t *log, const json_t *json_payload,
oauth2_jose_jwt_validate_claim_t validate)
{
bool rc = false;
json_int_t exp = -1;
oauth2_time_t now;
oauth2_debug(log, "enter: validate=%s",
_oauth2_validate_claim_option2s(validate));
if (validate == OAUTH2_JOSE_JWT_VALIDATE_CLAIM_SKIP) {
rc = true;
goto end;
}
if (oauth2_json_number_get(log, json_payload, OAUTH2_JOSE_JWT_EXP, &exp,
-1) == false) {
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
if (exp == -1) {
oauth2_warn(log, "JWT did not contain a \"%s\" number",
OAUTH2_JOSE_JWT_EXP);
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
now = oauth2_time_now_sec();
oauth2_debug(log,
"\"%s\"=%" JSON_INTEGER_FORMAT ", %ld seconds from now",
OAUTH2_JOSE_JWT_EXP, exp, (long)(exp - now));
if (now > exp) {
oauth2_error(log,
"\"%s\" validation failure (%ld): JWT expired %ld "
"seconds ago",
OAUTH2_JOSE_JWT_EXP, (long)exp, (long)(now - exp));
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
bool oauth2_jose_jwt_validate_iat(oauth2_log_t *log, const json_t *json_payload,
oauth2_jose_jwt_validate_claim_t validate,
oauth2_uint_t slack_before,
oauth2_uint_t slack_after)
{
bool rc = false;
json_int_t iat = -1;
oauth2_time_t now;
oauth2_debug(log,
"enter: validate=%s, slack_before=" OAUTH2_UINT_FORMAT
", slack_after=" OAUTH2_UINT_FORMAT,
_oauth2_validate_claim_option2s(validate), slack_before,
slack_after);
if (validate == OAUTH2_JOSE_JWT_VALIDATE_CLAIM_SKIP) {
rc = true;
goto end;
}
if (oauth2_json_number_get(log, json_payload, OAUTH2_JOSE_JWT_IAT, &iat,
-1) == false) {
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
if (iat == -1) {
oauth2_warn(log, "JWT did not contain a \"%s\" number",
OAUTH2_JOSE_JWT_IAT);
rc = (validate != OAUTH2_JOSE_JWT_VALIDATE_CLAIM_REQUIRED);
goto end;
}
now = oauth2_time_now_sec();
if ((slack_before != OAUTH2_CFG_UINT_UNSET) &&
((now - slack_before) > iat)) {
oauth2_error(log,
"\"%s\" validation failure (%ld): JWT was issued "
"more than %d seconds ago",
OAUTH2_JOSE_JWT_IAT, (long)iat, slack_before);
goto end;
}
if ((slack_after != OAUTH2_CFG_UINT_UNSET) &&
((now + slack_after) < iat)) {
oauth2_error(log,
"\"%s\" validation failure (%ld): JWT was issued "
"more than %d seconds in the future",
OAUTH2_JOSE_JWT_IAT, (long)iat, slack_after);
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool
_oauth2_jose_jwt_payload_validate(oauth2_log_t *log,
oauth2_jose_jwt_verify_ctx_t *jwt_verify_ctx,
const json_t *json_payload, const char *iss)
{
bool rc = false;
oauth2_debug(log, "enter");
if (_oauth2_jose_jwt_validate_iss(
log, json_payload, iss, jwt_verify_ctx->iss_validate) == false)
goto end;
if (_oauth2_jose_jwt_validate_exp(
log, json_payload, jwt_verify_ctx->exp_validate) == false)
goto end;
if (oauth2_jose_jwt_validate_iat(
log, json_payload, jwt_verify_ctx->iat_validate,
jwt_verify_ctx->iat_slack_before,
jwt_verify_ctx->iat_slack_after) == false)
goto end;
// TODO: token_binding_policy
// if (oauth2_jose_jwt_validate_cnf(r, jwt->payload.value.json,
// token_binding_policy) == false)
// goto end;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
bool oauth2_jose_jwt_verify(oauth2_log_t *log,
oauth2_jose_jwt_verify_ctx_t *jwt_verify_ctx,
const char *token, json_t **json_payload,
char **s_payload)
{
bool rc = false;
char *peek = NULL;
cjose_jws_t *jws = NULL;
cjose_header_t *hdr = NULL;
cjose_err err;
oauth2_jose_jwk_list_t *keys = NULL;
oauth2_jose_jwt_verify_jwk_ctx_t ctx;
uint8_t *plaintext = NULL;
size_t plaintext_len = 0;
bool refresh = false;
peek = oauth2_jose_jwt_header_peek(log, token, NULL);
oauth2_debug(log, "enter: JWT token header=%s", peek);
if (token == NULL)
goto end;
/*
* TODO: resolve the shared secret(s) and the private key(s) for
* decryption
*/
// TODO: this is not optimized anymore across different JWK verify
// configs
jws = cjose_jws_import(token, strlen(token), &err);
if (jws == NULL) {
oauth2_error(log, "cjose_jws_import failed: %s", err.message);
goto end;
}
hdr = cjose_jws_get_protected(jws);
if (hdr == NULL)
goto end;
if (jwt_verify_ctx) {
keys = jwt_verify_ctx->jwks_provider->resolve(
log, jwt_verify_ctx->jwks_provider, &refresh);
ctx.jws = jws;
ctx.kid = cjose_header_get(hdr, "kid", &err);
ctx.verified = false;
_oauth2_jose_verification_keys_loop(
log, keys, _oauth2_jose_jwt_verify_jwk, &ctx);
if (ctx.verified == false) {
if (refresh == false)
goto end;
if (keys)
oauth2_jose_jwk_list_free(log, keys);
keys = jwt_verify_ctx->jwks_provider->resolve(
log, jwt_verify_ctx->jwks_provider, &refresh);
_oauth2_jose_verification_keys_loop(
log, keys, _oauth2_jose_jwt_verify_jwk, &ctx);
if (ctx.verified == false)
goto end;
}
}
if (cjose_jws_get_plaintext(jws, &plaintext, &plaintext_len, &err) ==
false) {
oauth2_error(log, "cjose_jws_get_plaintext failed: %s",
err.message);
goto end;
}
if ((s_payload == NULL) || (json_payload == NULL))
goto end;
*s_payload = oauth2_strndup((const char *)plaintext, plaintext_len);
oauth2_debug(log, "got plaintext (len=%lu): %s", plaintext_len,
*s_payload);
if (oauth2_json_decode_object(log, *s_payload, json_payload) == false) {
oauth2_mem_free(*s_payload);
*s_payload = NULL;
goto end;
}
if (jwt_verify_ctx) {
if (_oauth2_jose_jwt_payload_validate(
log, jwt_verify_ctx, *json_payload, NULL) == false) {
json_decref(*json_payload);
*json_payload = NULL;
oauth2_mem_free(*s_payload);
*s_payload = NULL;
goto end;
}
}
rc = true;
end:
if (peek)
oauth2_mem_free(peek);
if (jws)
cjose_jws_release(jws);
if (keys)
oauth2_jose_jwk_list_free(log, keys);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool _oauth2_jose_jwt_verify_callback(oauth2_log_t *log,
oauth2_cfg_token_verify_t *verify,
const char *token,
json_t **json_payload,
char **s_payload)
{
bool rc = false;
oauth2_jose_jwt_verify_ctx_t *ctx = NULL;
oauth2_debug(log, "enter");
if ((verify == NULL) || (verify->ctx == NULL) ||
(verify->ctx->ptr == NULL))
goto end;
ctx = (oauth2_jose_jwt_verify_ctx_t *)verify->ctx->ptr;
rc = oauth2_jose_jwt_verify(log, ctx, token, json_payload, s_payload);
if (rc == false)
goto end;
end:
return rc;
}
static char *
_oauth2_jose_verify_options_jwk_add_jwk(oauth2_log_t *log, cjose_jwk_t *jwk,
const oauth2_nv_list_t *params,
oauth2_cfg_token_verify_t *verify)
{
char *rv = NULL;
oauth2_jose_jwt_verify_ctx_t *ptr = NULL;
const char *kid = NULL;
cjose_err err;
verify->callback = _oauth2_jose_jwt_verify_callback;
verify->ctx->callbacks = &oauth2_jose_jwt_verify_ctx_funcs;
verify->ctx->ptr = verify->ctx->callbacks->init(log);
ptr = (oauth2_jose_jwt_verify_ctx_t *)verify->ctx->ptr;
if (oauth2_jose_jwt_verify_set_options(
log, ptr, OAUTH2_JOSE_JWKS_PROVIDER_LIST, params) == false) {
rv = oauth2_strdup("oauth2_jose_jwt_verify_set_options failed");
goto end;
}
// set or possibly override kid in JWK
kid = oauth2_nv_list_get(log, params, "kid");
if (kid) {
if (cjose_jwk_set_kid(jwk, kid, strlen(kid), &err) == false) {
rv = oauth2_stradd(NULL, "cjose_jwk_set_kid failed",
": ", err.message);
goto end;
}
} else {
err.code = CJOSE_ERR_NONE;
kid = cjose_jwk_get_kid(jwk, &err);
if ((kid == NULL) && (err.code != CJOSE_ERR_NONE)) {
rv = oauth2_stradd(NULL, "cjose_jwk_get_kid failed",
": ", err.message);
goto end;
}
}
// list of one
ptr->jwks_provider->jwks = oauth2_jose_jwk_list_init(log);
ptr->jwks_provider->jwks->jwk->jwk = jwk;
ptr->jwks_provider->jwks->jwk->kid = kid ? oauth2_strdup(kid) : NULL;
ptr->jwks_provider->jwks->next = NULL;
end:
return rv;
}
static char *_oauth2_jose_verify_options_jwk_set_symmetric_key(
oauth2_log_t *log, const uint8_t *key, size_t key_len,
const oauth2_nv_list_t *params, oauth2_cfg_token_verify_t *verify)
{
char *rv = NULL;
cjose_err err;
cjose_jwk_t *jwk = NULL;
jwk = cjose_jwk_create_oct_spec(key, key_len, &err);
if (jwk == NULL) {
rv = oauth2_stradd(NULL, "cjose_jwk_create_oct_spec failed",
": ", err.message);
goto end;
}
rv = _oauth2_jose_verify_options_jwk_add_jwk(log, jwk, params, verify);
end:
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_plain)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
const uint8_t *key = NULL;
size_t key_len = 0;
if (value == NULL) {
rv = oauth2_strdup("no plain symmetric key value provided");
goto end;
}
key = (const uint8_t *)value;
key_len = strlen(value);
rv = _oauth2_jose_verify_options_jwk_set_symmetric_key(
log, key, key_len, params, verify);
end:
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_base64)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
uint8_t *key = NULL;
size_t key_len = 0;
if (value == NULL) {
rv = oauth2_strdup(
"no base64 encoded symmetric key value provided");
goto end;
}
if (oauth2_base64_decode(log, value, &key, &key_len) == false) {
rv = oauth2_strdup("oauth2_base64_decode failed");
goto end;
}
rv = _oauth2_jose_verify_options_jwk_set_symmetric_key(
log, key, key_len, params, verify);
end:
if (key)
oauth2_mem_free(key);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_base64url)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
uint8_t *key = NULL;
size_t key_len = 0;
if (value == NULL) {
rv = oauth2_strdup(
"no base64url encoded symmetric key value provided");
goto end;
}
if (oauth2_base64url_decode(log, value, &key, &key_len) == false) {
rv = oauth2_strdup("oauth2_base64url_decode failed");
goto end;
}
rv = _oauth2_jose_verify_options_jwk_set_symmetric_key(
log, key, key_len, params, verify);
end:
if (key)
oauth2_mem_free(key);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_hex)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
unsigned char *key = NULL;
size_t key_len = 0;
const char *ptr = NULL;
size_t n = 0;
if (value == NULL) {
rv = oauth2_strdup("no hex symmetric key value provided");
goto end;
}
key_len = strlen(value) / 2;
ptr = value;
key = oauth2_mem_alloc(key_len);
for (n = 0; n < key_len / sizeof(unsigned char); n++) {
if (sscanf(ptr, "%2hhx", &key[n]) != 1) {
rv = oauth2_strdup("sscanf failed");
goto end;
}
ptr += 2;
}
rv = _oauth2_jose_verify_options_jwk_set_symmetric_key(
log, (const uint8_t *)key, key_len, params, verify);
end:
if (key)
oauth2_mem_free(key);
return rv;
}
static BIO *_oauth2_jose_str2bio(oauth2_log_t *log, const char *value)
{
BIO *input = NULL;
if ((input = BIO_new(BIO_s_mem())) == NULL) {
oauth2_error(log, "BIO allocation failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
if (BIO_puts(input, value) <= 0) {
oauth2_error(log, "BIO_puts failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
end:
return input;
}
static char *
_oauth2_jose_options_jwk_set_rsa_key(oauth2_log_t *log, EVP_PKEY *pkey,
const oauth2_nv_list_t *params,
oauth2_cfg_token_verify_t *verify)
{
char *rv = NULL;
cjose_jwk_rsa_keyspec key_spec;
cjose_err err;
cjose_jwk_t *jwk = NULL;
RSA *rsa = NULL;
const BIGNUM *rsa_n, *rsa_e;
memset(&key_spec, 0, sizeof(cjose_jwk_rsa_keyspec));
rsa = EVP_PKEY_get1_RSA(pkey);
if (rsa == NULL) {
rv = oauth2_stradd(NULL, "EVP_PKEY_get1_RSA failed", ": ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
#if OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER)
RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
#else
rsa_n = rsa->n;
rsa_e = rsa->e;
#endif
RSA_free(rsa);
key_spec.nlen = BN_num_bytes(rsa_n);
key_spec.n = oauth2_mem_alloc(key_spec.nlen);
BN_bn2bin(rsa_n, key_spec.n);
key_spec.elen = BN_num_bytes(rsa_e);
key_spec.e = oauth2_mem_alloc(key_spec.elen);
BN_bn2bin(rsa_e, key_spec.e);
jwk = cjose_jwk_create_RSA_spec(&key_spec, &err);
if (jwk == NULL) {
rv = oauth2_stradd(NULL, "cjose_jwk_create_RSA_spec failed",
": ", err.message);
goto end;
}
rv = _oauth2_jose_verify_options_jwk_add_jwk(log, jwk, params, verify);
end:
if (key_spec.n)
oauth2_mem_free(key_spec.n);
if (key_spec.e)
oauth2_mem_free(key_spec.e);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_pem)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
BIO *input = NULL;
X509 *x509 = NULL;
EVP_PKEY *pkey = NULL;
input = _oauth2_jose_str2bio(log, value);
if (input == NULL) {
rv = oauth2_stradd(NULL, "_oauth2_jose_str2bio failed", ": ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
x509 = PEM_read_bio_X509_AUX(input, NULL, NULL, NULL);
if (x509 == NULL) {
rv = oauth2_stradd(NULL, "PEM_read_bio_X509_AUX failed", ": ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
pkey = X509_get_pubkey(x509);
if (pkey == NULL) {
rv = oauth2_stradd(NULL, "X509_get_pubkey failed", ": ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
rv = _oauth2_jose_options_jwk_set_rsa_key(log, pkey, params, verify);
end:
if (x509)
X509_free(x509);
if (pkey)
EVP_PKEY_free(pkey);
if (input)
BIO_free(input);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_pubkey)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
BIO *input = NULL;
EVP_PKEY *pkey = NULL;
input = _oauth2_jose_str2bio(log, value);
if (input == NULL) {
rv = oauth2_stradd(NULL, "_oauth2_jose_str2bio failed", ": ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
pkey = PEM_read_bio_PUBKEY(input, &pkey, NULL, NULL);
if (pkey == NULL) {
rv = oauth2_stradd(NULL, "PEM_read_bio_PUBKEY failed", ": ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
rv = _oauth2_jose_options_jwk_set_rsa_key(log, pkey, params, verify);
end:
if (pkey)
EVP_PKEY_free(pkey);
if (input)
BIO_free(input);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_jwk)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
cjose_jwk_t *jwk = NULL;
cjose_err err;
jwk = cjose_jwk_import(value, strlen(value), &err);
if (jwk == NULL) {
rv = oauth2_stradd(NULL, "cjose_jwk_import failed", ": ",
err.message);
goto end;
}
rv = _oauth2_jose_verify_options_jwk_add_jwk(log, jwk, params, verify);
end:
return rv;
}
#define OAUTH2_JOSE_URI_REFRESH_DEFAULT 60 * 60 * 24
char *oauth2_jose_options_uri_ctx(oauth2_log_t *log, const char *value,
const oauth2_nv_list_t *params,
oauth2_uri_ctx_t *ctx, const char *prefix)
{
char *rv = NULL;
char *key = NULL;
ctx->endpoint = oauth2_cfg_endpoint_init(log);
rv = oauth2_cfg_set_endpoint(log, ctx->endpoint, value, params, prefix);
key = oauth2_stradd(NULL, prefix, ".", "cache");
ctx->cache =
oauth2_cache_obtain(log, oauth2_nv_list_get(log, params, key));
oauth2_mem_free(key);
if (ctx->cache == NULL)
rv = oauth2_strdup("cache could not be found");
key = oauth2_stradd(NULL, prefix, ".", "expiry");
ctx->expiry_s =
oauth2_parse_uint(log, oauth2_nv_list_get(log, params, key),
OAUTH2_JOSE_URI_REFRESH_DEFAULT);
oauth2_mem_free(key);
return rv;
}
static char *_oauth2_jose_verify_options_jwk_set_url(
oauth2_log_t *log, const char *value, const oauth2_nv_list_t *params,
oauth2_cfg_token_verify_t *verify, oauth2_jose_jwks_provider_type_t type,
const char *prefix)
{
char *rv = NULL;
oauth2_jose_jwt_verify_ctx_t *ptr = NULL;
oauth2_debug(log, "enter");
verify->callback = _oauth2_jose_jwt_verify_callback;
verify->ctx->callbacks = &oauth2_jose_jwt_verify_ctx_funcs;
verify->ctx->ptr = verify->ctx->callbacks->init(log);
ptr = (oauth2_jose_jwt_verify_ctx_t *)verify->ctx->ptr;
if (oauth2_jose_jwt_verify_set_options(log, ptr, type, params) ==
false) {
rv = oauth2_strdup("oauth2_jose_jwt_verify_set_options failed");
goto end;
}
rv = oauth2_jose_options_uri_ctx(log, value, params,
ptr->jwks_provider->jwks_uri, prefix);
end:
oauth2_debug(log, "leave: %s", rv);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_jwks_uri)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
return _oauth2_jose_verify_options_jwk_set_url(
log, value, params, verify, OAUTH2_JOSE_JWKS_PROVIDER_JWKS_URI,
"jwks_uri");
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_eckey_uri)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
return _oauth2_jose_verify_options_jwk_set_url(
log, value, params, verify, OAUTH2_JOSE_JWKS_PROVIDER_ECKEY_URI,
"eckey_uri");
}
static oauth2_jose_jwk_list_t *oauth2_jose_jwks_list_resolve(
oauth2_log_t *log, oauth2_jose_jwks_provider_t *provider, bool *refresh)
{
*refresh = false;
return oauth2_jose_jwk_list_clone(log, provider->jwks);
}
typedef oauth2_jose_jwk_list_t *(oauth2_jose_jwks_url_resolve_response_cb_t)(
oauth2_log_t *log, char *response);
// cater for the (Amazon ALB) use case that only a single EC(!) key is served
// from the URL
static oauth2_jose_jwk_list_t *
_oauth2_jose_jwks_eckey_url_resolve_response_callback(oauth2_log_t *log,
char *response)
{
oauth2_jose_jwk_list_t *keys = NULL;
BIO *input = NULL;
EC_KEY *eckey = NULL;
const EC_GROUP *ecgroup = NULL;
const EC_POINT *ecpoint = NULL;
BIGNUM *x = NULL, *y = NULL;
cjose_jwk_ec_keyspec spec;
cjose_jwk_t *jwk = NULL;
cjose_err err;
input = _oauth2_jose_str2bio(log, response);
if (input == NULL)
goto end;
eckey = PEM_read_bio_EC_PUBKEY(input, NULL, 0, 0);
if (eckey == NULL) {
oauth2_error(log, "PEM_read_bio_EC_PUBKEY failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
ecgroup = EC_KEY_get0_group(eckey);
if (ecgroup == NULL) {
oauth2_error(log, "EC_KEY_get0_group failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
spec.crv = EC_GROUP_get_curve_name(ecgroup);
if (spec.crv == 0) {
oauth2_error(log, "EC_GROUP_get_curve_name failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
ecpoint = EC_KEY_get0_public_key(eckey);
if (ecpoint == 0) {
oauth2_error(log, "EC_KEY_get0_public_key failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
x = BN_new();
y = BN_new();
if ((x == NULL) || (y == NULL)) {
oauth2_error(log, "BN_new failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint, x, y, NULL) !=
1) {
oauth2_error(log,
"EC_POINT_get_affine_coordinates_GFp failed: ",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
spec.xlen = BN_num_bytes(x);
spec.x = oauth2_mem_alloc(spec.xlen);
BN_bn2bin(x, spec.x);
spec.ylen = BN_num_bytes(y);
spec.y = oauth2_mem_alloc(spec.ylen);
BN_bn2bin(y, spec.y);
spec.dlen = 0;
spec.d = NULL;
err.code = CJOSE_ERR_NONE;
jwk = cjose_jwk_create_EC_spec(&spec, &err);
if ((jwk == NULL) || (err.code != CJOSE_ERR_NONE)) {
oauth2_error(log,
"cjose_jwk_create_EC_spec failed: ", err.message);
goto end;
}
keys = oauth2_jose_jwk_list_init(log);
keys->jwk->jwk = jwk;
keys->jwk->kid = NULL;
end:
if (spec.x)
oauth2_mem_free(spec.x);
if (spec.y)
oauth2_mem_free(spec.y);
if (x)
BN_free(x);
if (y)
BN_free(y);
if (eckey)
EC_KEY_free(eckey);
if (input)
BIO_free(input);
return keys;
}
static oauth2_jose_jwk_list_t *
_oauth2_jose_jwks_uri_resolve_response_callback(oauth2_log_t *log,
char *response)
{
json_t *json_result = NULL, *json_keys = NULL, *json_key = NULL;
oauth2_jose_jwk_list_t *result = NULL, *elem = NULL, *last = NULL;
int i = 0;
cjose_err err;
if (oauth2_json_decode_object(log, response, &json_result) == false)
goto end;
// TODO: #define
json_keys = json_object_get(json_result, "keys");
if ((json_keys == NULL) || !(json_is_array(json_keys))) {
oauth2_error(log, "\"keys\" array element is not a JSON array");
goto end;
}
for (i = 0; i < json_array_size(json_keys); i++) {
json_key = json_array_get(json_keys, i);
// TODO: #define
const char *use =
json_string_value(json_object_get(json_key, "use"));
if ((use != NULL) && (strcmp(use, "sig") != 0)) {
oauth2_debug(log,
"skipping key because of "
"non-matching \"%s\": \"%s\"",
"use", use);
continue;
}
// TODO: search/skip based on key type (?)
elem = oauth2_jose_jwk_list_init(log);
err.code = CJOSE_ERR_NONE;
elem->jwk->jwk = cjose_jwk_import_json(json_key, &err);
if ((elem->jwk->jwk == NULL) || (err.code != CJOSE_ERR_NONE)) {
oauth2_warn(log, "cjose_jwk_import_json failed", ": ",
err.message);
oauth2_jose_jwk_list_free(log, elem);
continue;
}
elem->jwk->kid =
oauth2_strdup(cjose_jwk_get_kid(elem->jwk->jwk, &err));
if (err.code != CJOSE_ERR_NONE) {
oauth2_warn(log, "cjose_jwk_get_kid failed", ": ",
err.message);
oauth2_jose_jwk_list_free(log, elem);
continue;
}
if (result == NULL) {
result = elem;
last = result;
} else {
last->next = elem;
last = last->next;
}
}
end:
if (json_result)
json_decref(json_result);
return result;
}
char *oauth2_jose_resolve_from_uri(oauth2_log_t *log, oauth2_uri_ctx_t *uri_ctx,
bool *refresh)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
char *response = NULL;
oauth2_uint_t status_code = 0;
oauth2_debug(log, "enter");
if (uri_ctx == NULL)
goto end;
if (*refresh == false) {
oauth2_cache_get(log, uri_ctx->cache,
oauth2_cfg_endpoint_get_url(uri_ctx->endpoint),
&response);
}
if (response == NULL) {
*refresh = false;
ctx = oauth2_http_call_ctx_init(log);
oauth2_http_call_ctx_ssl_verify_set(
log, ctx,
oauth2_cfg_endpoint_get_ssl_verify(uri_ctx->endpoint));
oauth2_http_call_ctx_outgoing_proxy_set(
log, ctx,
oauth2_cfg_endpoint_get_outgoing_proxy(uri_ctx->endpoint));
rc = oauth2_http_get(
log, oauth2_cfg_endpoint_get_url(uri_ctx->endpoint), NULL,
ctx, &response, &status_code);
if (rc == false)
goto end;
if ((status_code < 200) || (status_code >= 300)) {
rc = false;
goto end;
}
oauth2_cache_set(log, uri_ctx->cache,
oauth2_cfg_endpoint_get_url(uri_ctx->endpoint),
response, uri_ctx->expiry_s);
}
end:
if (ctx)
oauth2_http_call_ctx_free(log, ctx);
oauth2_debug(log, "leave: %s", response);
return response;
}
static oauth2_jose_jwk_list_t *_oauth2_jose_jwks_resolve_from_uri(
oauth2_log_t *log, oauth2_jose_jwks_provider_t *provider, bool *refresh,
oauth2_jose_jwks_url_resolve_response_cb_t *resolve_response_cb)
{
oauth2_jose_jwk_list_t *dst = NULL;
char *response = NULL;
response =
oauth2_jose_resolve_from_uri(log, provider->jwks_uri, refresh);
if (response == NULL)
goto end;
dst = resolve_response_cb(log, response);
end:
if (response)
oauth2_mem_free(response);
return dst;
}
static oauth2_jose_jwk_list_t *oauth2_jose_jwks_uri_resolve(
oauth2_log_t *log, oauth2_jose_jwks_provider_t *provider, bool *refresh)
{
return _oauth2_jose_jwks_resolve_from_uri(
log, provider, refresh,
_oauth2_jose_jwks_uri_resolve_response_callback);
}
static oauth2_jose_jwk_list_t *oauth2_jose_jwks_eckey_url_resolve(
oauth2_log_t *log, oauth2_jose_jwks_provider_t *provider, bool *refresh)
{
return _oauth2_jose_jwks_resolve_from_uri(
log, provider, refresh,
_oauth2_jose_jwks_eckey_url_resolve_response_callback);
}
/*
oauth2_jose_jwk_list_t *
oauth2_jose_jwks_resolve(oauth2_log_t *log, oauth2_cfg_token_verify_t *verify,
bool *refresh)
{
// oauth2_jose_jwk_list_t *list = NULL, *last = NULL;
oauth2_jose_jwt_verify_ctx_t *ptr = NULL;
oauth2_jose_jwk_list_t *jwks = NULL;
oauth2_debug(log, "enter");
if ((verify == NULL) || (verify->ctx == NULL))
goto end;
ptr = (oauth2_jose_jwt_verify_ctx_t *)verify->ctx->ptr;
jwks = ptr->jwks_provider->resolve(log, ptr->jwks_provider, refresh);
end:
oauth2_debug(log, "leave: %p", jwks);
return jwks;
}
*/
bool oauth2_jose_jwk_thumbprint(oauth2_log_t *log, const cjose_jwk_t *jwk,
unsigned char **hash_bytes,
unsigned int *hash_bytes_len)
{
bool rc = false;
char *s_payload = NULL, *s_compact = NULL;
json_t *json_payload = NULL, *json_comp = NULL;
cjose_err err;
s_payload = cjose_jwk_to_json(jwk, false, &err);
if (s_payload == NULL) {
oauth2_error(log, "cjose_jwk_to_json failed: %s", err.message);
goto end;
}
if (oauth2_json_decode_object(log, s_payload, &json_payload) == false) {
oauth2_error(log, "decoding JWK JSON failed");
goto end;
}
json_comp = json_object();
switch (cjose_jwk_get_kty(jwk, &err)) {
case CJOSE_JWK_KTY_EC:
json_object_set(json_comp, "crv",
json_object_get(json_payload, "crv"));
json_object_set(json_comp, "kty",
json_object_get(json_payload, "kty"));
json_object_set(json_comp, "x",
json_object_get(json_payload, "x"));
json_object_set(json_comp, "y",
json_object_get(json_payload, "y"));
break;
case CJOSE_JWK_KTY_RSA:
json_object_set(json_comp, "e",
json_object_get(json_payload, "e"));
json_object_set(json_comp, "kty",
json_object_get(json_payload, "kty"));
json_object_set(json_comp, "n",
json_object_get(json_payload, "n"));
break;
case CJOSE_JWK_KTY_OCT:
json_object_set(json_comp, "k",
json_object_get(json_payload, "k"));
json_object_set(json_comp, "kty",
json_object_get(json_payload, "kty"));
break;
default:
oauth2_error(log, "unknown kty");
goto end;
}
s_compact = oauth2_json_encode(log, json_comp,
JSON_PRESERVE_ORDER | JSON_COMPACT);
if (oauth2_jose_hash_bytes(log, OAUTH2_JOSE_OPENSSL_ALG_SHA256,
(const unsigned char *)s_compact,
strlen(s_compact), hash_bytes,
hash_bytes_len) == false) {
oauth2_error(log, "oauth2_jose_hash_bytes failed");
goto end;
}
rc = true;
end:
if (s_compact)
oauth2_mem_free(s_compact);
if (s_payload)
cjose_get_dealloc()(s_payload);
if (json_payload)
json_decref(json_payload);
if (json_comp)
json_decref(json_comp);
return rc;
}
liboauth2-1.4.4/src/jose_int.h 0000664 0000000 0000000 00000010570 14161030125 0016157 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_JOSE_INT_H_
#define _OAUTH2_JOSE_INT_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/log.h"
#include "oauth2/util.h"
#include "cfg_int.h"
typedef struct oauth2_jose_jwk_t {
cjose_jwk_t *jwk;
char *kid;
} oauth2_jose_jwk_t;
typedef struct oauth2_jose_jwk_list_t {
oauth2_jose_jwk_t *jwk;
struct oauth2_jose_jwk_list_t *next;
} oauth2_jose_jwk_list_t;
typedef struct oauth2_uri_ctx_t {
oauth2_cfg_endpoint_t *endpoint;
oauth2_cache_t *cache;
oauth2_time_t expiry_s;
} oauth2_uri_ctx_t;
typedef enum oauth2_jose_jwks_provider_type_t {
OAUTH2_JOSE_JWKS_PROVIDER_LIST,
OAUTH2_JOSE_JWKS_PROVIDER_JWKS_URI,
OAUTH2_JOSE_JWKS_PROVIDER_ECKEY_URI
} oauth2_jose_jwks_provider_type_t;
typedef struct oauth2_jose_jwks_provider_t oauth2_jose_jwks_provider_t;
typedef oauth2_jose_jwk_list_t *(oauth2_jose_jwks_resolve_cb_t)(
oauth2_log_t *, oauth2_jose_jwks_provider_t *, bool *);
typedef struct oauth2_jose_jwks_provider_t {
oauth2_jose_jwks_provider_type_t type;
oauth2_jose_jwks_resolve_cb_t *resolve;
union {
oauth2_uri_ctx_t *jwks_uri;
oauth2_jose_jwk_list_t *jwks;
};
// struct oauth2_jose_jwks_provider_t *next;
} oauth2_jose_jwks_provider_t;
_OAUTH2_CFG_CTX_TYPE_START(oauth2_jose_jwt_verify_ctx)
oauth2_jose_jwks_provider_t *jwks_provider;
oauth2_jose_jwt_validate_claim_t iss_validate;
oauth2_jose_jwt_validate_claim_t exp_validate;
oauth2_jose_jwt_validate_claim_t iat_validate;
oauth2_uint_t iat_slack_before;
oauth2_uint_t iat_slack_after;
_OAUTH2_CFG_CTX_TYPE_END(oauth2_jose_jwt_verify_ctx)
void *oauth2_uri_ctx_init(oauth2_log_t *log);
void *oauth2_uri_ctx_clone(oauth2_log_t *log, void *c);
void oauth2_uri_ctx_free(oauth2_log_t *log, void *c);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_plain);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_base64);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_base64url);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_hex);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_pem);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_pubkey);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_jwk);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_jwks_uri);
_OAUTH_CFG_CTX_CALLBACK(oauth2_jose_verify_options_jwk_set_eckey_uri);
char *oauth2_jose_resolve_from_uri(oauth2_log_t *log, oauth2_uri_ctx_t *uri_ctx,
bool *refresh);
char *oauth2_jose_options_uri_ctx(oauth2_log_t *log, const char *value,
const oauth2_nv_list_t *params,
oauth2_uri_ctx_t *ctx, const char *prefix);
void *oauth2_jose_jwt_verify_ctx_init(oauth2_log_t *log);
void *oauth2_jose_jwt_verify_ctx_clone(oauth2_log_t *log, void *s);
void oauth2_jose_jwt_verify_ctx_free(oauth2_log_t *log, void *c);
bool oauth2_jose_jwt_verify_set_options(
oauth2_log_t *log, oauth2_jose_jwt_verify_ctx_t *jwt_verify,
oauth2_jose_jwks_provider_type_t type, const oauth2_nv_list_t *params);
char *oauth2_jose_jwt_header_peek(oauth2_log_t *log,
const char *compact_encoded_jwt,
const char **alg);
bool oauth2_jose_jwt_validate_iat(oauth2_log_t *log, const json_t *json_payload,
oauth2_jose_jwt_validate_claim_t validate,
oauth2_uint_t slack_before,
oauth2_uint_t slack_after);
char *oauth2_jwt_create(oauth2_log_t *log, cjose_jwk_t *jwk, const char *alg,
const char *iss, const char *sub, const char *client_id,
const char *aud, oauth2_uint_t exp, bool include_iat,
bool include_jti);
#endif /* _OAUTH2_JOSE_INT_H_ */
liboauth2-1.4.4/src/log.c 0000664 0000000 0000000 00000014730 14161030125 0015123 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
// need this at the top for vasprintf
#include "oauth2/log.h"
#include "oauth2/mem.h"
#include "util_int.h"
#include
#include
typedef struct oauth2_log_sink_t {
oauth2_log_level_t level;
oauth2_log_function_t callback;
void *ctx;
} oauth2_log_sink_t;
// note this is fastest, but must maintain the order of the enum...
static const char *_oauth2_log_level2str[] = {"ERR", "WRN", "NOT", "INF",
"DBG", "TR1", "TR2"};
typedef struct oauth2_log_sink_list_elem_t {
oauth2_log_sink_t *sink;
struct oauth2_log_sink_list_elem_t *next;
} oauth2_log_sink_list_elem_t;
typedef struct oauth2_log_sink_list_t {
oauth2_log_sink_list_elem_t *first;
oauth2_log_sink_list_elem_t *last;
} oauth2_log_sink_list_t;
typedef struct oauth2_log_t {
oauth2_log_sink_list_t sinks;
} oauth2_log_t;
oauth2_log_sink_t *oauth2_log_sink_create(oauth2_log_level_t level,
oauth2_log_function_t callback,
void *ctx)
{
oauth2_log_sink_t *sink = oauth2_mem_alloc(sizeof(oauth2_log_sink_t));
sink->callback = callback;
sink->level = level;
sink->ctx = ctx;
return sink;
}
void *oauth2_log_sink_ctx_get(oauth2_log_sink_t *sink)
{
return sink->ctx;
}
oauth2_log_function_t oauth2_log_sink_callback_get(oauth2_log_sink_t *sink)
{
return sink->callback;
}
void oauth2_log_sink_add(oauth2_log_t *log, oauth2_log_sink_t *add)
{
oauth2_log_sink_list_elem_t *ptr =
(oauth2_log_sink_list_elem_t *)oauth2_mem_alloc(
sizeof(oauth2_log_sink_list_elem_t));
;
ptr->sink = add;
ptr->next = NULL;
if (log->sinks.first == NULL) {
log->sinks.first = ptr;
log->sinks.last = ptr;
} else {
log->sinks.last->next = ptr;
}
}
void oauth2_log_sink_level_set(oauth2_log_sink_t *sink,
oauth2_log_level_t level)
{
sink->level = level;
}
static void oauth2_log_std(FILE *std, oauth2_log_sink_t *sink,
const char *filename, unsigned long line,
const char *function, oauth2_log_level_t level,
const char *msg)
{
// TODO: make a print-to-string function for this generic prefix?
fprintf(std, "[%s:%lu:%s:%s] %s\n", filename, line, function,
_oauth2_log_level2str[level], msg);
}
static void oauth2_log_std_err(oauth2_log_sink_t *sink, const char *filename,
unsigned long line, const char *function,
oauth2_log_level_t level, const char *msg)
{
oauth2_log_std(stderr, sink, filename, line, function, level, msg);
}
static void oauth2_log_std_out(oauth2_log_sink_t *sink, const char *filename,
unsigned long line, const char *function,
oauth2_log_level_t level, const char *msg)
{
oauth2_log_std(stdout, sink, filename, line, function, level, msg);
}
oauth2_log_sink_t oauth2_log_sink_stderr = {OAUTH2_LOG_INFO, oauth2_log_std_err,
NULL};
oauth2_log_sink_t oauth2_log_sink_stdout = {OAUTH2_LOG_INFO, oauth2_log_std_out,
NULL};
// API
#ifdef _MSC_VER
int vasprintf(char **strp, const char *fmt, va_list ap)
{
// _vscprintf tells you how big the buffer needs to be
int len = _vscprintf(fmt, ap);
if (len == -1) {
return -1;
}
size_t size = (size_t)len + 1;
char *str = malloc(size);
if (!str) {
return -1;
}
// _vsprintf_s is the "secure" version of vsprintf
int r = vsprintf_s(str, len + 1, fmt, ap);
if (r == -1) {
free(str);
return -1;
}
*strp = str;
return r;
}
#endif
void oauth2_log(oauth2_log_t *log, const char *filename, unsigned long line,
const char *function, oauth2_log_level_t level, const char *fmt,
...)
{
va_list ap;
oauth2_log_sink_list_elem_t *ptr;
char *msg = NULL;
int rc = 0;
if ((log == NULL) || (log->sinks.first == NULL) || (fmt == NULL))
goto end;
va_start(ap, fmt);
rc = vasprintf(&msg, fmt, ap);
// TODO: can't get this to work...?
// rc = oauth2_sprintf(&msg, fmt, ap);
(void)rc;
va_end(ap);
if (msg) {
for (ptr = log->sinks.first; ptr != NULL; ptr = ptr->next) {
if (level > ptr->sink->level)
continue;
ptr->sink->callback(ptr->sink, filename, line, function,
level, msg);
}
// TODO: can't get this to work...?
// oauth2_mem_free(msg);
free(msg);
}
end:
return;
}
oauth2_log_t *oauth2_log_init(oauth2_log_level_t level, oauth2_log_sink_t *sink)
{
oauth2_log_t *log =
(oauth2_log_t *)oauth2_mem_alloc(sizeof(oauth2_log_t));
if (log == NULL)
goto end;
log->sinks.first = NULL;
log->sinks.last = NULL;
oauth2_log_sink_add(log,
(sink != NULL) ? sink : &oauth2_log_sink_stderr);
log->sinks.first->sink->level = level;
end:
return log;
}
void oauth2_log_free(oauth2_log_t *log)
{
oauth2_log_sink_list_elem_t *ptr = NULL;
if (log == NULL)
goto end;
while ((ptr = log->sinks.first)) {
log->sinks.first = log->sinks.first->next;
if ((ptr->sink != &oauth2_log_sink_stderr) &&
(ptr->sink != &oauth2_log_sink_stdout))
oauth2_mem_free(ptr->sink);
oauth2_mem_free(ptr);
}
log->sinks.last = NULL;
oauth2_mem_free(log);
end:
return;
}
/*
static int oauth2_log_level2aplog[] = {
APLOG_ERR,
APLOG_WARNING,
APLOG_NOTICE,
APLOG_INFO,
APLOG_DEBUG,
APLOG_TRACE1
};
void oauth2_log_backend_ap_log_rerror(void *log_log, const char *filename,
unsigned long line, const char *function, oauth2_log_level_t level, const char
*fmt, ...) {
request_rec *r = (request_rec *)log_log;
ap_log_rerror(filename, line, APLOG_MODULE_INDEX,
oauth2_log_level2aplog[level], 0, r,"%s: %s", function, apr_psprintf(r->pool,
fmt, ##__VA_ARGS__))
}
*/
liboauth2-1.4.4/src/mem.c 0000664 0000000 0000000 00000005047 14161030125 0015121 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/mem.h"
#include "cjose/util.h"
#include "oauth2/util.h"
#include "util_int.h"
#include
#include
#include "curl/curl.h"
static void *oauth2_mem_calloc_callback(size_t nmemb, size_t size)
{
return oauth2_mem_alloc(nmemb * size);
}
void oauth2_mem_set_alloc_funcs(oauth2_mem_alloc_fn_t alloc,
oauth2_mem_realloc_fn_t realloc,
oauth2_mem_dealloc_fn_t dealloc)
{
cjose_set_alloc_funcs(alloc, realloc, dealloc);
curl_global_init_mem(CURL_GLOBAL_ALL, alloc, dealloc, realloc,
oauth2_strdup, oauth2_mem_calloc_callback);
}
void oauth2_mem_set_alloc_ex_funcs(oauth2_mem_alloc3_fn_t alloc3,
oauth2_mem_realloc3_fn_t realloc3,
oauth2_mem_dealloc3_fn_t dealloc3)
{
cjose_set_alloc_ex_funcs(alloc3, realloc3, dealloc3);
curl_global_init_mem(CURL_GLOBAL_ALL, cjose_get_alloc(),
cjose_get_dealloc(), cjose_get_realloc(),
oauth2_strdup, oauth2_mem_calloc_callback);
}
oauth2_mem_alloc_fn_t oauth2_mem_get_alloc()
{
return cjose_get_alloc();
}
oauth2_mem_alloc3_fn_t oauth2_mem_get_alloc3()
{
return cjose_get_alloc3();
}
oauth2_mem_realloc_fn_t oauth2_mem_get_realloc()
{
return cjose_get_realloc();
}
oauth2_mem_realloc3_fn_t oauth2_mem_get_realloc3()
{
return cjose_get_realloc3();
}
oauth2_mem_dealloc_fn_t oauth2_mem_get_dealloc()
{
return cjose_get_dealloc();
}
oauth2_mem_dealloc3_fn_t oauth2_mem_get_dealloc3()
{
return cjose_get_dealloc3();
}
void *oauth2_mem_alloc(size_t size)
{
void *ptr = oauth2_mem_get_alloc()(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void oauth2_mem_free(void *ptr)
{
oauth2_mem_get_dealloc()(ptr);
}
liboauth2-1.4.4/src/oauth2.c 0000664 0000000 0000000 00000054030 14161030125 0015541 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/oauth2.h"
#include "cfg_int.h"
#include "oauth2/cfg.h"
#include "oauth2/http.h"
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "jose_int.h"
#include "oauth2_int.h"
#include "util_int.h"
#include
#include
#include
/*
* auth
*/
#define OAUTH2_CLIENT_ASSERTION "client_assertion"
#define OAUTH2_CLIENT_ASSERTION_TYPE "client_assertion_type"
#define OAUTH2_CLIENT_ASSERTION_TYPE_JWT_BEARER \
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
static bool _oauth2_add_signed_jwt(oauth2_log_t *log, cjose_jwk_t *jwk,
const char *alg, const char *client_id,
const char *aud, oauth2_nv_list_t *params)
{
bool rc = false;
char *jwt = NULL;
oauth2_debug(log, "enter");
jwt = oauth2_jwt_create(log, jwk, alg, client_id, client_id, client_id,
aud, 60, true, true);
if (jwt == NULL)
goto end;
oauth2_nv_list_set(log, params, OAUTH2_CLIENT_ASSERTION_TYPE,
OAUTH2_CLIENT_ASSERTION_TYPE_JWT_BEARER);
oauth2_nv_list_set(log, params, OAUTH2_CLIENT_ASSERTION, jwt);
rc = true;
end:
if (jwt)
oauth2_mem_free(jwt);
oauth2_debug(log, "leave");
return rc;
}
static bool
oauth2_auth_client_secret_jwt(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
oauth2_debug(log, "enter");
if ((auth->client_secret_jwt.client_id == NULL) ||
(auth->client_secret_jwt.jwk == NULL) ||
(auth->client_secret_jwt.aud == NULL))
goto end;
rc = _oauth2_add_signed_jwt(log, auth->client_secret_jwt.jwk,
CJOSE_HDR_ALG_HS256,
auth->client_secret_jwt.client_id,
auth->client_secret_jwt.aud, params);
end:
oauth2_debug(log, "leave");
return rc;
}
static bool oauth2_auth_private_key_jwt(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
cjose_err err;
// cjose_jwk_t *jwk = NULL;
oauth2_debug(log, "enter");
if ((auth->private_key_jwt.client_id == NULL) ||
(auth->private_key_jwt.jwk == NULL) ||
(auth->private_key_jwt.aud == NULL))
goto end;
// jwk = cjose_jwk_import(cser_jwk, strlen(cser_jwk), &err);
// if (jwk == NULL) {
// oauth2_error(log, "cjose_jwk_import failed: %s",
// err.message); goto end;
// }
if (cjose_jwk_get_kty(auth->private_key_jwt.jwk, &err) !=
CJOSE_JWK_KTY_RSA) {
oauth2_error(log, "jwk is not an RSA key: %s", err.message);
goto end;
}
rc = _oauth2_add_signed_jwt(
log, auth->private_key_jwt.jwk, CJOSE_HDR_ALG_RS256,
auth->private_key_jwt.client_id, auth->private_key_jwt.aud, params);
end:
oauth2_debug(log, "leave");
return rc;
}
static bool
oauth2_auth_client_secret_basic(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
if ((auth->client_secret_basic.client_id == NULL) ||
(auth->client_secret_basic.client_secret == NULL))
goto end;
rc = oauth2_http_call_ctx_basic_auth_set(
log, ctx, auth->client_secret_basic.client_id,
auth->client_secret_basic.client_secret, true);
end:
return rc;
}
static bool
oauth2_auth_client_secret_post(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
if ((auth->client_secret_post.client_id == NULL) ||
(auth->client_secret_post.client_secret == NULL))
goto end;
rc = oauth2_nv_list_add(log, params, OAUTH2_CLIENT_ID,
auth->client_secret_post.client_id);
if (rc == false)
goto end;
rc = oauth2_nv_list_add(log, params, OAUTH2_CLIENT_SECRET,
auth->client_secret_post.client_secret);
end:
return rc;
}
static bool oauth2_auth_client_cert(oauth2_log_t *log,
oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
if ((auth->client_cert.certfile == NULL) ||
(auth->client_cert.keyfile == NULL))
goto end;
rc = oauth2_http_auth_client_cert(log, auth->client_cert.certfile,
auth->client_cert.keyfile, ctx);
end:
return rc;
}
bool oauth2_auth_basic(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
rc = oauth2_http_auth_basic(log, auth->basic.username,
auth->basic.password, ctx);
return rc;
}
typedef bool(oauth2_http_ctx_add_auth_cb_t)(
oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth, oauth2_nv_list_t *params);
typedef struct oauth2_http_ctx_auth_cb_ctx_t {
oauth2_cfg_endpoint_auth_type_t type;
oauth2_http_ctx_add_auth_cb_t *add_callback;
} oauth2_http_ctx_auth_cb_ctx_t;
// clang-format off
static oauth2_http_ctx_auth_cb_ctx_t oauth2_http_ctx_auth_cb[] = {
{ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC, oauth2_auth_client_secret_basic },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST, oauth2_auth_client_secret_post },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT, oauth2_auth_client_secret_jwt },
{ OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT, oauth2_auth_private_key_jwt },
{ OAUTH2_ENDPOINT_AUTH_CLIENT_CERT, oauth2_auth_client_cert },
{ OAUTH2_ENDPOINT_AUTH_BASIC, oauth2_auth_basic },
// must be last
{ OAUTH2_ENDPOINT_AUTH_NONE, NULL },
};
// clang-format on
bool oauth2_http_ctx_auth_add(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params)
{
bool rc = false;
int i = 0;
if ((ctx == NULL) || (auth == NULL))
goto end;
if (auth->type == OAUTH2_ENDPOINT_AUTH_NONE) {
rc = true;
goto end;
}
i = 0;
while (oauth2_http_ctx_auth_cb[i].type != OAUTH2_ENDPOINT_AUTH_NONE) {
if (auth->type == oauth2_http_ctx_auth_cb[i].type) {
rc = oauth2_http_ctx_auth_cb[i].add_callback(
log, ctx, auth, params);
goto end;
}
i++;
}
end:
return rc;
}
/*
* introspect
*/
_OAUTH2_CFG_CTX_TYPE_START(oauth2_introspect_ctx)
oauth2_cfg_endpoint_t *endpoint;
_OAUTH2_CFG_CTX_TYPE_END(oauth2_introspect_ctx)
_OAUTH2_CFG_CTX_INIT_START(oauth2_introspect_ctx)
ctx->endpoint = NULL;
_OAUTH2_CFG_CTX_INIT_END
_OAUTH2_CFG_CTX_CLONE_START(oauth2_introspect_ctx)
dst->endpoint = oauth2_cfg_endpoint_clone(log, src->endpoint);
_OAUTH2_CFG_CTX_CLONE_END
_OAUTH2_CFG_CTX_FREE_START(oauth2_introspect_ctx)
if (ctx->endpoint)
oauth2_cfg_endpoint_free(log, ctx->endpoint);
_OAUTH2_CFG_CTX_FREE_END
_OAUTH2_CFG_CTX_FUNCS(oauth2_introspect_ctx)
#define OAUTH2_INTROSPECT_TOKEN "token"
#define OAUTH2_INTROSPECT_TOKEN_TYPE_HINT "token_type_hint"
#define OAUTH2_INTROSPECT_TOKEN_TYPE_HINT_ACCESS_TOKEN "access_token"
#define OAUTH2_INTROSPECT_CLAIM_ACTIVE "active"
static bool _oauth2_introspect_verify(oauth2_log_t *log,
oauth2_introspect_ctx_t *ctx,
const char *token, json_t **json_payload,
char **s_payload)
{
bool rc = false;
oauth2_nv_list_t *params = NULL;
oauth2_http_call_ctx_t *http_ctx = NULL;
json_t *active = NULL;
oauth2_uint_t status_code = 0;
oauth2_debug(log, "enter");
http_ctx = oauth2_http_call_ctx_init(log);
if (http_ctx == NULL)
goto end;
if (oauth2_http_call_ctx_ssl_verify_set(
log, http_ctx,
oauth2_cfg_endpoint_get_ssl_verify(ctx->endpoint)) == false)
goto end;
oauth2_http_call_ctx_outgoing_proxy_set(
log, http_ctx,
oauth2_cfg_endpoint_get_outgoing_proxy(ctx->endpoint));
params = oauth2_nv_list_init(log);
if (params == NULL)
goto end;
oauth2_nv_list_add(log, params, OAUTH2_INTROSPECT_TOKEN, token);
oauth2_nv_list_add(log, params, OAUTH2_INTROSPECT_TOKEN_TYPE_HINT,
OAUTH2_INTROSPECT_TOKEN_TYPE_HINT_ACCESS_TOKEN);
// TODO: add configurable extra POST params
if (oauth2_http_ctx_auth_add(
log, http_ctx, oauth2_cfg_endpoint_get_auth(ctx->endpoint),
params) == false)
goto end;
if (oauth2_http_post_form(
log, oauth2_cfg_endpoint_get_url(ctx->endpoint), params,
http_ctx, s_payload, &status_code) == false)
goto end;
if ((status_code < 200) || (status_code >= 300)) {
rc = false;
goto end;
}
if (oauth2_json_decode_check_error(log, *s_payload, json_payload) ==
false)
goto end;
active = json_object_get(*json_payload, OAUTH2_INTROSPECT_CLAIM_ACTIVE);
if (active == NULL)
goto end;
if (json_is_boolean(active) == false)
goto end;
if (json_is_true(active) == false) {
oauth2_error(
log,
"\"%s\" boolean object with value \"false\" found in "
"response JSON object",
OAUTH2_INTROSPECT_CLAIM_ACTIVE);
goto end;
}
rc = true;
// TODO: verify if returned content is JWT? how to call into existing
// jwks_uri etc. code?
end:
if (rc == false) {
if ((json_payload) && (*json_payload)) {
json_decref(*json_payload);
*json_payload = NULL;
}
if ((s_payload) && (*s_payload)) {
oauth2_mem_free(*s_payload);
*s_payload = NULL;
}
}
if (params)
oauth2_nv_list_free(log, params);
if (http_ctx)
oauth2_http_call_ctx_free(log, http_ctx);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool _oauth2_introspect_verify_callback(
oauth2_log_t *log, oauth2_cfg_token_verify_t *verify, const char *token,
json_t **json_payload, char **s_payload)
{
bool rc = false;
oauth2_introspect_ctx_t *ctx = NULL;
ctx = (oauth2_introspect_ctx_t *)verify->ctx->ptr;
if ((verify == NULL) || (verify->ctx == NULL) ||
(verify->ctx->ptr == NULL))
goto end;
rc =
_oauth2_introspect_verify(log, ctx, token, json_payload, s_payload);
end:
return rc;
}
static char *_oauth2_verify_options_set_introspect_url_ctx(
oauth2_log_t *log, const char *url, const oauth2_nv_list_t *params,
oauth2_introspect_ctx_t *ctx)
{
char *rv = NULL;
oauth2_debug(log, "enter");
ctx->endpoint = oauth2_cfg_endpoint_init(log);
rv = oauth2_cfg_set_endpoint(log, ctx->endpoint, url, params,
"introspect");
oauth2_debug(log, "leave: %s", rv);
return rv;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_verify_options_set_introspect_url)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
oauth2_debug(log, "enter");
verify->callback = _oauth2_introspect_verify_callback;
verify->ctx->callbacks = &oauth2_introspect_ctx_funcs;
verify->ctx->ptr = verify->ctx->callbacks->init(log);
rv = _oauth2_verify_options_set_introspect_url_ctx(
log, value, params, (oauth2_introspect_ctx_t *)verify->ctx->ptr);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
_OAUTH2_CFG_CTX_TYPE_START(oauth2_metadata_ctx)
oauth2_introspect_ctx_t *introspect;
oauth2_jose_jwt_verify_ctx_t *jwks_uri_verify;
oauth2_uri_ctx_t *metadata_uri;
_OAUTH2_CFG_CTX_TYPE_END(oauth2_metadata_ctx)
_OAUTH2_CFG_CTX_INIT_START(oauth2_metadata_ctx)
ctx->introspect = oauth2_introspect_ctx_init(log);
ctx->jwks_uri_verify =
(oauth2_jose_jwt_verify_ctx_t *)oauth2_jose_jwt_verify_ctx_init(log);
ctx->metadata_uri = oauth2_uri_ctx_init(log);
_OAUTH2_CFG_CTX_INIT_END
_OAUTH2_CFG_CTX_CLONE_START(oauth2_metadata_ctx)
dst->introspect = oauth2_introspect_ctx_clone(log, src->introspect);
dst->jwks_uri_verify =
oauth2_jose_jwt_verify_ctx_clone(log, src->jwks_uri_verify);
dst->metadata_uri = oauth2_uri_ctx_clone(log, src->metadata_uri);
_OAUTH2_CFG_CTX_CLONE_END
_OAUTH2_CFG_CTX_FREE_START(oauth2_metadata_ctx)
if (ctx->introspect)
oauth2_introspect_ctx_free(log, ctx->introspect);
if (ctx->jwks_uri_verify)
oauth2_jose_jwt_verify_ctx_free(log, ctx->jwks_uri_verify);
if (ctx->metadata_uri)
oauth2_uri_ctx_free(log, ctx->metadata_uri);
_OAUTH2_CFG_CTX_FREE_END
_OAUTH2_CFG_CTX_FUNCS(oauth2_metadata_ctx)
static bool _oauth2_metadata_verify_callback(oauth2_log_t *log,
oauth2_cfg_token_verify_t *verify,
const char *token,
json_t **json_payload,
char **s_payload)
{
bool rc = false;
oauth2_metadata_ctx_t *ptr = NULL;
bool refresh = false;
char *response = NULL;
json_t *json_metadata = NULL, *json_jwks_uri = NULL,
*json_introspection_endpoint;
const char *jwks_uri = NULL, *introspection_endpoint = NULL;
char *peek = NULL;
if ((verify == NULL) || (verify->ctx == NULL) ||
(verify->ctx->ptr == NULL))
goto end;
ptr = (oauth2_metadata_ctx_t *)verify->ctx->ptr;
response =
oauth2_jose_resolve_from_uri(log, ptr->metadata_uri, &refresh);
if (response == NULL)
goto end;
if (oauth2_json_decode_object(log, response, &json_metadata) == false)
goto end;
peek = oauth2_jose_jwt_header_peek(log, token, NULL);
if (peek) {
oauth2_debug(log, "JWT token: header=%s", peek);
goto jwks_uri;
} else {
oauth2_debug(log, "no JWT token: introspect it");
goto introspect;
}
jwks_uri:
json_jwks_uri = json_object_get(json_metadata, "jwks_uri");
if (json_jwks_uri) {
if (json_is_string(json_jwks_uri)) {
jwks_uri = json_string_value(json_jwks_uri);
} else {
oauth2_warn(log, "\"jwks_uri\" value is not a string");
}
}
if (jwks_uri) {
oauth2_cfg_endpoint_set_url(
ptr->jwks_uri_verify->jwks_provider->jwks_uri->endpoint,
jwks_uri);
rc = oauth2_jose_jwt_verify(log, ptr->jwks_uri_verify, token,
json_payload, s_payload);
if (rc == true)
goto end;
}
introspect:
json_introspection_endpoint =
json_object_get(json_metadata, "introspection_endpoint");
if (json_introspection_endpoint) {
if (json_is_string(json_introspection_endpoint)) {
introspection_endpoint =
json_string_value(json_introspection_endpoint);
} else {
oauth2_warn(
log,
"\"introspection_endpoint\" value is not a string");
}
}
if (introspection_endpoint) {
oauth2_cfg_endpoint_set_url(ptr->introspect->endpoint,
introspection_endpoint);
rc = _oauth2_introspect_verify(log, ptr->introspect, token,
json_payload, s_payload);
if (rc == true)
goto end;
}
end:
if (peek)
oauth2_mem_free(peek);
if (json_metadata)
json_decref(json_metadata);
if (response)
oauth2_mem_free(response);
return rc;
}
_OAUTH_CFG_CTX_CALLBACK(oauth2_verify_options_set_metadata_url)
{
oauth2_cfg_token_verify_t *verify = (oauth2_cfg_token_verify_t *)ctx;
char *rv = NULL;
oauth2_metadata_ctx_t *ptr = NULL;
oauth2_debug(log, "enter");
verify->callback = _oauth2_metadata_verify_callback;
verify->ctx->callbacks = &oauth2_metadata_ctx_funcs;
verify->ctx->ptr = verify->ctx->callbacks->init(log);
ptr = (oauth2_metadata_ctx_t *)verify->ctx->ptr;
rv = _oauth2_verify_options_set_introspect_url_ctx(log, value, params,
ptr->introspect);
if (rv != NULL)
goto end;
// TODO: should we not combine these next 2 calls in a single function?
if (oauth2_jose_jwt_verify_set_options(
log, ptr->jwks_uri_verify, OAUTH2_JOSE_JWKS_PROVIDER_JWKS_URI,
params) == false) {
rv = oauth2_strdup("oauth2_jose_jwt_verify_set_options failed");
goto end;
}
rv = oauth2_jose_options_uri_ctx(
log, value, params, ptr->jwks_uri_verify->jwks_provider->jwks_uri,
"jwks_uri");
if (rv != NULL) {
rv = oauth2_strdup(
"oauth2_jose_options_uri_ctx failed for jwks_uri");
goto end;
}
rv = oauth2_jose_options_uri_ctx(log, value, params, ptr->metadata_uri,
"metadata");
if (rv != NULL) {
rv = oauth2_strdup(
"oauth2_jose_options_uri_ctx failed for metadata");
goto end;
}
end:
oauth2_debug(log, "leave: %s", rv);
return rv;
}
static const char *
oauth2_mtls_verify_policy2str(const oauth2_cfg_mtls_verify_policy_t policy)
{
if (policy == OAUTH2_MTLS_VERIFY_POLICY_OPTIONAL)
return "optional";
if (policy == OAUTH2_MTLS_VERIFY_POLICY_REQUIRED)
return "required";
return "unset";
}
static char *
oauth2_mtls_client_cert_fingerprint(oauth2_log_t *log,
oauth2_cfg_mtls_verify_t *mtls_verify,
const oauth2_http_request_t *request)
{
BIO *input = NULL;
X509 *x509 = NULL;
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int md_len;
char *fingerprint = NULL;
const char *cert_pem = NULL;
if (mtls_verify == NULL)
goto end;
cert_pem = oauth2_http_request_context_get(log, request,
OAUTH2_TLS_CERT_VAR_NAME);
oauth2_debug(log, "request context variable: %s=%s",
OAUTH2_TLS_CERT_VAR_NAME, cert_pem);
if (cert_pem == NULL)
goto end;
input = BIO_new(BIO_s_mem());
if (input == NULL) {
oauth2_error(log, "memory allocation BIO_new/BIO_s_mem");
goto end;
}
if (BIO_puts(input, cert_pem) <= 0) {
oauth2_error(log, "memory allocation BIO_new/BIO_s_mem");
goto end;
}
x509 = PEM_read_bio_X509_AUX(input, NULL, NULL, NULL);
if (x509 == NULL) {
oauth2_error(log, "could not decode x509 cert from presumably "
"PEM encoded env var value");
goto end;
}
if (!X509_digest(x509, EVP_sha256(), md, &md_len)) {
oauth2_error(log, "X509_digest failed");
goto end;
}
oauth2_base64url_encode(log, md, md_len, &fingerprint);
end:
if (input)
BIO_free(input);
if (x509)
X509_free(x509);
return fingerprint;
}
oauth2_cfg_mtls_verify_policy_t
oauth2_mtls_verify_policy_get(const oauth2_cfg_mtls_verify_t *mtls_verify)
{
if ((mtls_verify == NULL) ||
(mtls_verify->policy == OAUTH2_CFG_UINT_UNSET))
return OAUTH2_MTLS_VERIFY_POLICY_REQUIRED;
return mtls_verify->policy;
}
static bool oauth2_mtls_validate_cnf_x5t_s256(
oauth2_log_t *log, oauth2_cfg_mtls_verify_t *mtls_verify,
const oauth2_http_request_t *request, const char *x5t_256_str)
{
bool rc = false;
char *fingerprint = NULL;
fingerprint =
oauth2_mtls_client_cert_fingerprint(log, mtls_verify, request);
if (fingerprint == NULL) {
oauth2_debug(log, "no certificate (fingerprint) provided");
goto end;
}
if (strcmp(fingerprint, x5t_256_str) != 0) {
oauth2_warn(log,
"fingerprint of provided cert (%s) does not match "
"cnf[\"x5t#S256\"] (%s)",
fingerprint, x5t_256_str);
goto end;
}
oauth2_debug(
log, "fingerprint of provided cert (%s) matches cnf[\"x5t#S256\"]",
fingerprint);
rc = true;
end:
if (fingerprint)
oauth2_mem_free(fingerprint);
return rc;
}
#define OAUTH2_CLAIM_CNF "cnf"
#define OAUTH2_CLAIM_CNF_X5T_S256 "x5t#S256"
static bool oauth2_mtls_token_verify(oauth2_log_t *log,
oauth2_cfg_mtls_verify_t *mtls_verify,
const oauth2_http_request_t *request,
json_t *jwt)
{
bool rc = false;
char *cnf_x5t_s256_str = NULL;
oauth2_debug(log, "enter: policy=%s",
oauth2_mtls_verify_policy2str(mtls_verify->policy));
json_t *cnf = json_object_get(jwt, OAUTH2_CLAIM_CNF);
// cnf = json_object();
// json_object_set(cnf, "x5t#S256",
// json_string("3rM_CLStQF3e-N2_9dZcR-BMw45XmRR0jbj6MSHU2bM"));
if (cnf == NULL) {
oauth2_debug(log, "no \"%s\" claim found in the token",
OAUTH2_CLAIM_CNF);
goto err;
}
oauth2_json_string_get(log, cnf, OAUTH2_CLAIM_CNF_X5T_S256,
&cnf_x5t_s256_str, NULL);
if (cnf_x5t_s256_str == NULL) {
oauth2_debug(log,
" \"%s\" claim found in the token but no \"%s\" "
"key found inside",
OAUTH2_CLAIM_CNF, OAUTH2_CLAIM_CNF_X5T_S256);
goto err;
}
rc = oauth2_mtls_validate_cnf_x5t_s256(log, mtls_verify, request,
cnf_x5t_s256_str);
goto end;
err:
if ((cnf_x5t_s256_str == NULL) &&
(oauth2_mtls_verify_policy_get(mtls_verify) !=
OAUTH2_MTLS_VERIFY_POLICY_REQUIRED))
rc = true;
end:
if (cnf_x5t_s256_str != NULL)
oauth2_mem_free(cnf_x5t_s256_str);
return rc;
}
bool oauth2_token_verify(oauth2_log_t *log, oauth2_http_request_t *request,
oauth2_cfg_token_verify_t *verify, const char *token,
json_t **json_payload)
{
bool rc = false;
oauth2_cfg_token_verify_t *ptr = NULL;
char *s_payload = NULL;
oauth2_debug(log, "enter");
if ((verify == NULL) || (token == NULL))
goto end;
ptr = verify;
while (ptr && ptr->callback) {
oauth2_cache_get(log, ptr->cache, token, &s_payload);
if ((s_payload) &&
(oauth2_json_decode_object(log, s_payload, json_payload))) {
rc = true;
break;
}
if (ptr->callback(log, ptr, token, json_payload, &s_payload)) {
oauth2_cache_set(log, ptr->cache, token, s_payload,
ptr->expiry_s);
rc = true;
break;
}
ptr = ptr->next;
}
if (rc == true) {
if (ptr->type == OAUTH2_TOKEN_VERIFY_DPOP) {
rc = oauth2_dpop_token_verify(log, &verify->dpop,
request, *json_payload);
} else if (ptr->type == OAUTH2_TOKEN_VERIFY_MTLS) {
rc = oauth2_mtls_token_verify(log, &verify->mtls,
request, *json_payload);
}
}
end:
if (s_payload)
oauth2_mem_free(s_payload);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
/*
void oauth2_scrub_headers(request_rec *r) {
oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
&auth_openidc_module);
if (cfg->scrub_request_headers != 0) {
const char *prefix = oidc_cfg_claim_prefix(r);
apr_hash_t *hdrs = apr_hash_make(r->pool);
if (apr_strnatcmp(prefix, "") == 0) {
if ((cfg->white_listed_claims != NULL)
&&
(apr_hash_count(cfg->white_listed_claims) > 0)) hdrs = apr_hash_overlay(r->pool,
cfg->white_listed_claims, hdrs); else oidc_warn(r, "both " OIDCClaimPrefix " and
" OIDCWhiteListedClaims " are empty: this renders an insecure setup!");
}
char *authn_hdr = oidc_cfg_dir_authn_header(r);
if (authn_hdr != NULL)
apr_hash_set(hdrs, authn_hdr, APR_HASH_KEY_STRING,
authn_hdr);
oidc_scrub_request_headers(r, OIDC_DEFAULT_HEADER_PREFIX, hdrs);
if ((strstr(prefix, OIDC_DEFAULT_HEADER_PREFIX) != prefix)) {
oidc_scrub_request_headers(r, prefix, NULL);
}
}
}
*/
liboauth2-1.4.4/src/oauth2_int.h 0000664 0000000 0000000 00000003112 14161030125 0016413 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_INT_H_
#define _OAUTH2_INT_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/cfg.h"
#include "oauth2/http.h"
#include "oauth2/log.h"
#include "oauth2/util.h"
#include "cfg_int.h"
_OAUTH_CFG_CTX_CALLBACK(oauth2_verify_options_set_introspect_url);
_OAUTH_CFG_CTX_CALLBACK(oauth2_verify_options_set_metadata_url);
bool oauth2_auth_basic(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx,
const oauth2_cfg_endpoint_auth_t *auth,
oauth2_nv_list_t *params);
bool oauth2_dpop_token_verify(oauth2_log_t *log,
oauth2_cfg_dpop_verify_t *verify,
oauth2_http_request_t *request,
json_t *json_payload);
#endif /* _OAUTH2_INT_H_ */
liboauth2-1.4.4/src/openidc/ 0000775 0000000 0000000 00000000000 14161030125 0015612 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/src/openidc/.gitignore 0000664 0000000 0000000 00000000056 14161030125 0017603 0 ustar 00root root 0000000 0000000 /.dirstamp
/.deps/
/.libs/
/*.lo
/*.o
/*.gcno
liboauth2-1.4.4/src/openidc/client.c 0000664 0000000 0000000 00000022767 14161030125 0017252 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include "cfg_int.h"
#include "openidc_int.h"
#include "util_int.h"
oauth2_openidc_client_t *oauth2_openidc_client_init(oauth2_log_t *log)
{
oauth2_openidc_client_t *c = NULL;
c = oauth2_mem_alloc(sizeof(oauth2_openidc_client_t));
if (c == NULL)
goto end;
c->client_id = NULL;
c->client_secret = NULL;
c->scope = NULL;
c->token_endpoint_auth = NULL;
c->ssl_verify = OAUTH2_CFG_FLAG_UNSET;
c->http_timeout = OAUTH2_CFG_UINT_UNSET;
end:
return c;
}
void oauth2_openidc_client_free(oauth2_log_t *log, oauth2_openidc_client_t *c)
{
if (c == NULL)
goto end;
if (c->client_id)
oauth2_mem_free(c->client_id);
if (c->client_secret)
oauth2_mem_free(c->client_secret);
if (c->scope)
oauth2_mem_free(c->scope);
if (c->token_endpoint_auth)
oauth2_cfg_endpoint_auth_free(log, c->token_endpoint_auth);
oauth2_mem_free(c);
end:
return;
}
oauth2_openidc_client_t *
oauth2_openidc_client_clone(oauth2_log_t *log,
const oauth2_openidc_client_t *src)
{
oauth2_openidc_client_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_openidc_client_init(log);
if (dst == NULL)
goto end;
dst->client_id = oauth2_strdup(src->client_id);
dst->client_secret = oauth2_strdup(src->client_secret);
dst->scope = oauth2_strdup(src->scope);
dst->token_endpoint_auth =
oauth2_cfg_endpoint_auth_clone(log, src->token_endpoint_auth);
dst->ssl_verify = src->ssl_verify;
dst->http_timeout = src->http_timeout;
end:
return dst;
}
static char *_oauth2_openidc_client_metadata_parse(
oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, const char *s_json,
const oauth2_nv_list_t *options_params)
{
char *rv = NULL;
json_t *json = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
char *value = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_debug(log, "enter");
if ((cfg == NULL) || (cfg->client == NULL) || (s_json == NULL)) {
rv = oauth2_strdup(
"internal error: struct, client or json is NULL");
goto end;
}
if (oauth2_json_decode_object(log, s_json, &json) == false) {
rv = oauth2_strdup("could not parse json object");
goto end;
}
params = options_params ? oauth2_nv_list_clone(log, options_params)
: oauth2_nv_list_init(log);
if ((oauth2_json_string_get(log, json, "client_id", &value, NULL) ==
false) ||
(value == NULL)) {
rv = oauth2_strdup("could not parse client_id");
goto end;
}
if (value) {
// TODO: better merging?
oauth2_nv_list_add(log, params, "client_id", value);
oauth2_openidc_client_client_id_set(log, cfg->client, value);
oauth2_mem_free(value);
value = NULL;
}
if (oauth2_json_string_get(log, json, "client_secret", &value, NULL) ==
false) {
rv = oauth2_strdup("could not parse client_secret");
goto end;
}
if (value) {
// TODO: better merging?
oauth2_nv_list_add(log, params, "client_secret", value);
oauth2_openidc_client_client_secret_set(log, cfg->client,
value);
oauth2_mem_free(value);
value = NULL;
}
if (oauth2_json_string_get(log, json, "scope", &value, NULL) == false) {
rv = oauth2_strdup("could not parse scope");
goto end;
}
if (value) {
oauth2_openidc_client_scope_set(log, cfg->client, value);
oauth2_mem_free(value);
value = NULL;
}
auth = oauth2_cfg_endpoint_auth_init(log);
value = NULL;
if (oauth2_json_string_get(log, json, "token_endpoint_auth_method",
&value, NULL) == false) {
rv =
oauth2_strdup("could not parse token_endpoint_auth_method");
oauth2_cfg_endpoint_auth_free(log, auth);
goto end;
}
if (value == NULL) {
oauth2_cfg_endpoint_auth_free(log, auth);
goto end;
}
rv = oauth2_cfg_set_endpoint_auth(log, auth, value, params, NULL);
if (rv != NULL) {
oauth2_cfg_endpoint_auth_free(log, auth);
goto end;
}
oauth2_cfg_endpoint_auth_free(
log,
oauth2_openidc_client_token_endpoint_auth_get(log, cfg->client));
oauth2_openidc_client_token_endpoint_auth_set(log, cfg->client, auth);
end:
if ((rv != NULL) && (cfg->client)) {
oauth2_openidc_client_free(log, cfg->client);
cfg->client = NULL;
}
if (value)
oauth2_mem_free(value);
if (params)
oauth2_nv_list_free(log, params);
if (json)
json_decref(json);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
static char *
_oauth2_openidc_client_set_options_file(oauth2_log_t *log, const char *filename,
const oauth2_nv_list_t *params, void *c)
{
oauth2_cfg_openidc_t *cfg = (oauth2_cfg_openidc_t *)c;
char *s_json = NULL;
char *rv = NULL;
s_json = oauth_read_file(log, filename);
if (s_json == NULL)
goto end;
rv = _oauth2_openidc_client_metadata_parse(log, cfg, s_json, params);
end:
if (s_json)
oauth2_mem_free(s_json);
return rv;
}
static char *
_oauth2_openidc_client_set_options_json(oauth2_log_t *log, const char *value,
const oauth2_nv_list_t *params, void *c)
{
oauth2_cfg_openidc_t *cfg = (oauth2_cfg_openidc_t *)c;
return _oauth2_openidc_client_metadata_parse(log, cfg, value, params);
}
static char *
_oauth2_openidc_client_set_options_string(oauth2_log_t *log, const char *value,
const oauth2_nv_list_t *params,
void *c)
{
oauth2_cfg_openidc_t *cfg = (oauth2_cfg_openidc_t *)c;
char *rv = NULL;
oauth2_nv_list_t *client_params = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_debug(log, "enter");
if (oauth2_parse_form_encoded_params(log, value, &client_params) ==
false) {
rv = oauth2_strdup("could not parse parameters");
goto end;
}
oauth2_openidc_client_client_id_set(
log, cfg->client,
oauth2_nv_list_get(log, client_params, "client_id"));
oauth2_openidc_client_client_secret_set(
log, cfg->client,
oauth2_nv_list_get(log, client_params, "client_secret"));
oauth2_openidc_client_scope_set(
log, cfg->client, oauth2_nv_list_get(log, client_params, "scope"));
auth = oauth2_cfg_endpoint_auth_init(log);
// TODO: merge client_params and params?
rv = oauth2_cfg_set_endpoint_auth(
log, auth,
oauth2_nv_list_get(log, client_params,
"token_endpoint_auth_method"),
client_params, NULL);
if (rv != NULL) {
oauth2_cfg_endpoint_auth_free(log, auth);
goto end;
}
oauth2_cfg_endpoint_auth_free(
log,
oauth2_openidc_client_token_endpoint_auth_get(log, cfg->client));
oauth2_openidc_client_token_endpoint_auth_set(log, cfg->client, auth);
end:
if (client_params)
oauth2_nv_list_free(log, client_params);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, client, scope, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, client, client_id, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, client, client_secret, char *,
str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, client, token_endpoint_auth,
oauth2_cfg_endpoint_auth_t *, ptr)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, client, ssl_verify,
oauth2_flag_t, bln)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, client, http_timeout,
oauth2_uint_t, uint)
#define OAUTH2_OPENIDC_RESOLVER_STRING_STR "string"
#define OAUTH2_OPENIDC_RESOLVER_JSON_STR "json"
#define OAUTH2_OPENIDC_RESOLVER_FILE_STR "file"
// clang-format off
static oauth2_cfg_set_options_ctx_t _oauth2_cfg_client_resolver_options_set[] = {
{ OAUTH2_OPENIDC_RESOLVER_STRING_STR, _oauth2_openidc_client_set_options_string },
{ OAUTH2_OPENIDC_RESOLVER_JSON_STR, _oauth2_openidc_client_set_options_json },
{ OAUTH2_OPENIDC_RESOLVER_FILE_STR, _oauth2_openidc_client_set_options_file },
{ NULL, NULL }
};
// clang-format on
char *oauth2_openidc_client_set_options(oauth2_log_t *log,
oauth2_cfg_openidc_t *cfg,
const char *type, const char *value,
const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_debug(log, "type=%s value=%s options=%s", type, value, options);
if (cfg->client == NULL) {
cfg->client = oauth2_openidc_client_init(log);
if (cfg->client == NULL) {
rv = oauth2_strdup("could not create client");
goto end;
}
}
if (oauth2_parse_form_encoded_params(log, options, ¶ms) == false) {
rv = oauth2_strdup("could not parse parameters");
goto end;
}
cfg->session = _oauth2_cfg_session_obtain(
log, oauth2_nv_list_get(log, params, "session"));
if (cfg->session == NULL) {
rv = oauth2_strdup("could not obtain session");
goto end;
}
rv = oauth2_strdup(oauth2_cfg_set_flag_slot(
cfg->client, offsetof(oauth2_openidc_client_t, ssl_verify),
oauth2_nv_list_get(log, params, "ssl_verify")));
if (rv != NULL)
goto end;
rv = oauth2_cfg_set_options(log, cfg, type, value, options,
_oauth2_cfg_client_resolver_options_set);
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
liboauth2-1.4.4/src/openidc/openidc.c 0000664 0000000 0000000 00000042667 14161030125 0017416 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/openidc.h"
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "oauth2/oauth2.h"
#include "oauth2/session.h"
#include "cfg_int.h"
#include "openidc_int.h"
#define OAUTH2_STATE_LENGTH 16
#define OAUTH2_NONCE_LENGTH 16
#define OAUTH2_PKCE_LENGTH 48
static bool _oauth2_openidc_authenticate(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t **response)
{
bool rc = false;
oauth2_openidc_provider_t *provider = NULL;
char *nonce = NULL, *state = NULL, *redirect_uri = NULL,
*location = NULL, *pkce = NULL, *code_challenge = NULL;
oauth2_nv_list_t *params = oauth2_nv_list_init(log);
char *client_id = NULL, *scope = NULL;
unsigned char *dst = NULL;
unsigned int dst_len = 0;
oauth2_debug(log, "enter");
if ((cfg == NULL) || (request == NULL) || (response == NULL))
goto end;
if (_oauth2_openidc_provider_resolve(log, cfg, request, NULL,
&provider) == false)
goto end;
oauth2_nv_list_add(log, params, OAUTH2_RESPONSE_TYPE,
OAUTH2_RESPONSE_TYPE_CODE);
client_id = oauth2_openidc_client_client_id_get(log, cfg->client);
if (client_id)
oauth2_nv_list_add(log, params, OAUTH2_CLIENT_ID, client_id);
// redirect_uri = oauth2_openidc_cfg_redirect_uri_get_iss(log, cfg,
// request, provider);
redirect_uri = oauth2_cfg_openidc_redirect_uri_get(log, cfg, request);
if (redirect_uri)
oauth2_nv_list_add(log, params, OAUTH2_REDIRECT_URI,
redirect_uri);
scope = oauth2_openidc_client_scope_get(log, cfg->client);
if (scope)
oauth2_nv_list_add(log, params, OAUTH2_SCOPE, scope);
nonce = oauth2_rand_str(log, OAUTH2_NONCE_LENGTH);
oauth2_nv_list_add(log, params, OAUTH2_NONCE, nonce);
state = oauth2_rand_str(log, OAUTH2_STATE_LENGTH);
oauth2_nv_list_add(log, params, OAUTH2_STATE, state);
pkce = oauth2_rand_str(log, OAUTH2_PKCE_LENGTH);
oauth2_jose_hash_bytes(log, "sha256", (const unsigned char *)pkce,
strlen(pkce), &dst, &dst_len);
oauth2_base64url_encode(log, dst, dst_len, &code_challenge);
oauth2_nv_list_add(log, params, OAUTH2_CODE_CHALLENGE, code_challenge);
oauth2_nv_list_add(log, params, OAUTH2_CODE_CHALLENGE_METHOD, "S256");
// TODO: handle POST binding as well
if (*response == NULL)
goto end;
if (_oauth2_openidc_state_cookie_set(log, cfg, provider, request,
*response, state, pkce) == false)
goto end;
location = oauth2_http_url_query_encode(
log, provider->authorization_endpoint, params);
if (oauth2_http_response_header_set(
log, *response, OAUTH2_HTTP_HDR_LOCATION, location) == false)
goto end;
rc = oauth2_http_response_status_code_set(log, *response, 302);
end:
if (provider)
oauth2_openidc_provider_free(log, provider);
if (redirect_uri)
oauth2_mem_free(redirect_uri);
if (nonce)
oauth2_mem_free(nonce);
if (state)
oauth2_mem_free(state);
if (location)
oauth2_mem_free(location);
if (params)
oauth2_nv_list_free(log, params);
if (code_challenge)
oauth2_mem_free(code_challenge);
if (pkce)
oauth2_mem_free(pkce);
if (dst)
oauth2_mem_free(dst);
oauth2_debug(log, "return: %d", rc);
return rc;
}
static bool _oauth2_openidc_unauthenticated_request(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request, oauth2_session_rec_t *session,
oauth2_http_response_t **response)
{
bool rc = false;
oauth2_debug(log, "enter");
if (response == NULL)
goto end;
*response = oauth2_http_response_init(log);
switch (oauth2_cfg_openidc_unauth_action_get(log, cfg)) {
case OAUTH2_UNAUTH_ACTION_PASS:
// r->user = "";
// oidc_scrub_headers(r);
rc = true;
goto end;
break;
case OAUTH2_UNAUTH_ACTION_HTTP_401:
oauth2_http_response_status_code_set(log, *response, 401);
rc = true;
goto end;
break;
case OAUTH2_UNAUTH_ACTION_HTTP_410:
oauth2_http_response_status_code_set(log, *response, 410);
rc = true;
goto end;
break;
case OAUTH2_UNAUTH_ACTION_AUTHENTICATE:
case OAUTH2_UNAUTH_ACTION_UNDEFINED:
default:
if (oauth2_http_request_is_xml_http_request(log, request)) {
oauth2_http_response_status_code_set(log, *response,
410);
rc = true;
goto end;
}
break;
}
rc = _oauth2_openidc_authenticate(log, cfg, request, response);
end:
oauth2_debug(log, "return: %d", rc);
return rc;
}
static bool _oauth2_openidc_existing_session(oauth2_log_t *log,
const oauth2_cfg_openidc_t *c,
const oauth2_http_request_t *r,
oauth2_session_rec_t *session,
oauth2_http_response_t **response,
json_t **claims)
{
bool rc = false;
json_t *id_token_claims = NULL, *userinfo_claims = NULL;
const char *key = NULL;
json_t *value = NULL;
oauth2_debug(log, "enter");
*response = oauth2_http_response_init(log);
if (oauth2_session_handle(log, c->session, r, *response, session) ==
false)
goto end;
id_token_claims = oauth2_session_rec_id_token_claims_get(log, session);
userinfo_claims = oauth2_session_rec_userinfo_claims_get(log, session);
*claims = json_object();
if (id_token_claims) {
json_object_foreach(id_token_claims, key, value)
json_object_set_new(*claims, key, json_deep_copy(value));
}
if (userinfo_claims) {
json_object_foreach(userinfo_claims, key, value)
json_object_set_new(*claims, key, json_deep_copy(value));
}
rc = true;
end:
oauth2_debug(log, "return: %d (%p, %p)", rc, *response, *claims);
return rc;
}
static bool _oauth2_openidc_id_token_verify(oauth2_log_t *log,
oauth2_openidc_provider_t *provider,
const char *s_id_token,
json_t **id_token, bool ssl_verify)
{
bool rc = false;
char *rv = NULL;
char *options = NULL;
oauth2_cfg_token_verify_t *verify = NULL;
if (ssl_verify == false)
options =
oauth2_stradd(NULL, "jwks_uri.ssl_verify", "=", "false");
rv = oauth2_cfg_token_verify_add_options(log, &verify, "jwks_uri",
provider->jwks_uri, options);
if (rv != NULL) {
oauth2_error(
log, "oauth2_cfg_token_verify_add_options failed: %s", rv);
goto end;
}
if (oauth2_token_verify(log, NULL, verify, s_id_token, id_token) ==
false) {
oauth2_error(log, "id_token verification failed");
goto end;
}
rc = true;
end:
if (rv)
oauth2_mem_free(rv);
if (options)
oauth2_mem_free(options);
if (verify)
oauth2_cfg_token_verify_free(log, verify);
return rc;
}
static bool
_oauth2_openidc_token_endpoint_call(oauth2_log_t *log,
oauth2_openidc_client_t *client,
oauth2_openidc_provider_t *provider,
oauth2_nv_list_t *params, json_t **json)
{
bool rc = false;
oauth2_http_call_ctx_t *http_ctx = NULL;
char *s_response = NULL;
oauth2_uint_t status_code = 0;
oauth2_debug(log, "enter");
http_ctx = oauth2_http_call_ctx_init(log);
if (http_ctx == NULL)
goto end;
if (oauth2_http_call_ctx_ssl_verify_set(
log, http_ctx,
oauth2_openidc_client_ssl_verify_get(log, client)) == false)
goto end;
if (oauth2_http_call_ctx_timeout_set(
log, http_ctx,
oauth2_openidc_client_http_timeout_get(log, client)) == false)
goto end;
// TODO: add configurable extra POST params
if (oauth2_http_ctx_auth_add(
log, http_ctx,
oauth2_openidc_client_token_endpoint_auth_get(log, client),
params) == false)
goto end;
if (oauth2_http_post_form(
log, oauth2_openidc_provider_token_endpoint_get(log, provider),
params, http_ctx, &s_response, &status_code) == false)
goto end;
if ((status_code < 200) || (status_code >= 300)) {
rc = false;
goto end;
}
if (oauth2_json_decode_check_error(log, s_response, json) == false)
goto end;
rc = true;
end:
if (s_response)
oauth2_mem_free(s_response);
if (http_ctx)
oauth2_http_call_ctx_free(log, http_ctx);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool _oauth2_openidc_token_request(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request,
oauth2_openidc_provider_t *provider,
const char *code, const char *pkce,
char **s_id_token,
char **s_access_token)
{
bool rc = false;
oauth2_nv_list_t *params = NULL;
char *redirect_uri = NULL;
json_t *json = NULL;
oauth2_debug(log, "enter");
redirect_uri = oauth2_cfg_openidc_redirect_uri_get(log, cfg, request);
if (redirect_uri == NULL)
goto end;
params = oauth2_nv_list_init(log);
if (params == NULL)
goto end;
oauth2_nv_list_add(log, params, OAUTH2_GRANT_TYPE,
OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE);
oauth2_nv_list_add(log, params, OAUTH2_CODE, code);
oauth2_nv_list_add(log, params, OAUTH2_REDIRECT_URI, redirect_uri);
oauth2_nv_list_add(log, params, OAUTH2_CODE_VERIFIER, pkce);
if (_oauth2_openidc_token_endpoint_call(log, cfg->client, provider,
params, &json) == false)
goto end;
if (oauth2_json_string_get(log, json, OAUTH2_OPENIDC_ID_TOKEN,
s_id_token, NULL) == false) {
oauth2_error(log, "no %s found in token response",
OAUTH2_OPENIDC_ID_TOKEN);
goto end;
}
if (oauth2_json_string_get(log, json, OAUTH2_OPENIDC_ACCESS_TOKEN,
s_access_token, NULL) == false) {
oauth2_error(log, "no %s found in token response",
OAUTH2_OPENIDC_ACCESS_TOKEN);
goto end;
}
rc = true;
end:
if (redirect_uri)
oauth2_mem_free(redirect_uri);
if (params)
oauth2_nv_list_free(log, params);
if (json)
json_decref(json);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static bool _oauth2_openidc_userinfo_request(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request, oauth2_openidc_provider_t *provider,
const char *s_access_token, json_t **userinfo_claims)
{
bool rc = false;
oauth2_http_call_ctx_t *http_ctx = NULL;
char *s_response = NULL;
oauth2_uint_t status_code = 0;
if (oauth2_openidc_provider_userinfo_endpoint_get(log, provider) ==
NULL) {
rc = true;
goto end;
}
http_ctx = oauth2_http_call_ctx_init(log);
if (http_ctx == NULL)
goto end;
if (oauth2_http_call_ctx_ssl_verify_set(
log, http_ctx,
oauth2_openidc_client_ssl_verify_get(log, cfg->client)) ==
false)
goto end;
if (oauth2_http_call_ctx_timeout_set(
log, http_ctx,
oauth2_openidc_client_http_timeout_get(log, cfg->client)) ==
false)
goto end;
if (oauth2_http_call_ctx_bearer_token_set(log, http_ctx,
s_access_token) == false)
goto end;
if (oauth2_http_get(
log,
oauth2_openidc_provider_userinfo_endpoint_get(log, provider),
NULL, http_ctx, &s_response, &status_code) == false)
goto end;
if ((status_code < 200) || (status_code >= 300)) {
rc = false;
goto end;
}
if (oauth2_json_decode_check_error(log, s_response, userinfo_claims) ==
false)
goto end;
rc = true;
end:
if (s_response)
oauth2_mem_free(s_response);
if (http_ctx)
oauth2_http_call_ctx_free(log, http_ctx);
return rc;
}
static bool _oauth2_openidc_redirect_uri_handler(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request, oauth2_session_rec_t *session,
oauth2_http_response_t **response)
{
bool rc = false;
oauth2_openidc_provider_t *provider = NULL;
const char *code = NULL, *state = NULL;
char *location = NULL, *s_id_token = NULL, *s_access_token = NULL,
*pkce = NULL;
json_t *id_token = NULL, *userinfo_claims = NULL;
oauth2_openidc_proto_state_t *proto_state = NULL;
oauth2_debug(log, "enter");
// at this point we know there's a request to the redirect uri
// errors set in the HTTP response
code = oauth2_http_request_query_param_get(log, request, OAUTH2_CODE);
if (code == NULL) {
oauth2_error(log,
"invalid request to the redirect_uri: %s "
"parameter could not be found [%s]",
OAUTH2_CODE,
oauth2_http_request_query_get(log, request));
goto end;
}
state = oauth2_http_request_query_param_get(log, request, OAUTH2_STATE);
if (state == NULL) {
oauth2_error(log,
"invalid request to the redirect_uri: %s "
"parameter could not be found [%s]",
OAUTH2_STATE,
oauth2_http_request_query_get(log, request));
goto end;
}
*response = oauth2_http_response_init(log);
if (_oauth2_openidc_state_cookie_get(log, cfg, request, *response,
state, &proto_state) == false)
goto end;
if (_oauth2_openidc_state_validate(log, cfg, request, proto_state,
&provider) == false)
goto end;
if (oauth2_openidc_proto_state_pkce_get(log, proto_state, &pkce) ==
false)
goto end;
if (_oauth2_openidc_token_request(log, cfg, request, provider, code,
pkce, &s_id_token,
&s_access_token) == false)
goto end;
if (_oauth2_openidc_id_token_verify(
log, provider, s_id_token, &id_token,
oauth2_openidc_client_ssl_verify_get(log, cfg->client)) ==
false)
goto end;
if (_oauth2_openidc_userinfo_request(log, cfg, request, provider,
s_access_token,
&userinfo_claims) == false)
goto end;
// TODO: evaluate and set configurable r->user claim
oauth2_session_rec_user_set(
log, session, json_string_value(json_object_get(id_token, "sub")));
oauth2_session_rec_id_token_claims_set(log, session, id_token);
oauth2_session_rec_userinfo_claims_set(log, session, userinfo_claims);
oauth2_session_save(log, cfg->session, request, *response, session);
// redirect to where we wanted to go originally
if (oauth2_openidc_proto_state_target_link_uri_get(log, proto_state,
&location) == false)
goto end;
if (oauth2_http_response_header_set(
log, *response, OAUTH2_HTTP_HDR_LOCATION, location) == false)
goto end;
if (oauth2_http_response_status_code_set(log, *response, 302) == false)
goto end;
rc = true;
end:
if (pkce)
oauth2_mem_free(pkce);
if (s_id_token)
oauth2_mem_free(s_id_token);
if (s_access_token)
oauth2_mem_free(s_access_token);
if (location)
oauth2_mem_free(location);
if (proto_state)
oauth2_openidc_proto_state_free(log, proto_state);
if (provider)
oauth2_openidc_provider_free(log, provider);
if (id_token)
json_decref(id_token);
if (userinfo_claims)
json_decref(userinfo_claims);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
bool oauth2_openidc_is_request_to_redirect_uri(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request)
{
bool rc = false;
char *redirect_uri = NULL, *request_url;
request_url = oauth2_http_request_url_path_get(log, request);
if (request_url == NULL)
goto end;
// redirect_uri = oauth2_openidc_cfg_redirect_uri_get_iss(log, cfg,
// request, provider);
redirect_uri = oauth2_cfg_openidc_redirect_uri_get(log, cfg, request);
if (redirect_uri == NULL)
goto end;
oauth2_debug(log, "comparing: \"%s\"=\"%s\"", request_url,
redirect_uri);
if (strcmp(redirect_uri, request_url) != 0)
goto end;
rc = true;
end:
if (request_url)
oauth2_mem_free(request_url);
if (redirect_uri)
oauth2_mem_free(redirect_uri);
return rc;
}
static bool _oauth2_openidc_internal_requests(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request,
oauth2_session_rec_t *session,
oauth2_http_response_t **response,
bool *processed)
{
bool rc = true;
oauth2_debug(log, "enter");
if (oauth2_openidc_is_request_to_redirect_uri(log, cfg, request) ==
true) {
rc = _oauth2_openidc_redirect_uri_handler(log, cfg, request,
session, response);
*processed = true;
goto end;
}
// TODO:
// - session info
// - key materials
// - 3rd-party init SSO
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
bool oauth2_openidc_handle(oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request,
oauth2_http_response_t **response, json_t **claims)
{
bool rc = false, processed = false;
oauth2_session_rec_t *session = NULL;
oauth2_debug(log, "incoming request: %s%s%s",
oauth2_http_request_path_get(log, request),
oauth2_http_request_query_get(log, request) ? "?" : "",
oauth2_http_request_query_get(log, request)
? oauth2_http_request_query_get(log, request)
: "");
if (oauth2_session_load(log, cfg->session, request, &session) == false)
goto end;
rc = _oauth2_openidc_internal_requests(log, cfg, request, session,
response, &processed);
if ((processed == true) || (rc == false))
goto end;
if (oauth2_session_rec_user_get(log, session) != NULL) {
oauth2_debug(log, "user found in session: %s",
oauth2_session_rec_user_get(log, session));
rc = _oauth2_openidc_existing_session(
log, cfg, request, session, response, claims);
goto end;
}
oauth2_debug(log, "no user found in session");
rc = _oauth2_openidc_unauthenticated_request(log, cfg, request, session,
response);
end:
oauth2_session_rec_free(log, session);
oauth2_debug(log, "return: %d", rc);
return rc;
}
liboauth2-1.4.4/src/openidc/provider.c 0000664 0000000 0000000 00000004405 14161030125 0017613 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include "openidc_int.h"
#include "util_int.h"
oauth2_openidc_provider_t *oauth2_openidc_provider_init(oauth2_log_t *log)
{
oauth2_openidc_provider_t *p = NULL;
p = oauth2_mem_alloc(sizeof(oauth2_openidc_provider_t));
if (p == NULL)
goto end;
p->issuer = NULL;
p->authorization_endpoint = NULL;
p->token_endpoint = NULL;
p->userinfo_endpoint = NULL;
p->jwks_uri = NULL;
end:
return p;
}
void oauth2_openidc_provider_free(oauth2_log_t *log,
oauth2_openidc_provider_t *p)
{
if (p == NULL)
goto end;
if (p->issuer)
oauth2_mem_free(p->issuer);
if (p->authorization_endpoint)
oauth2_mem_free(p->authorization_endpoint);
if (p->token_endpoint)
oauth2_mem_free(p->token_endpoint);
if (p->jwks_uri)
oauth2_mem_free(p->jwks_uri);
if (p->userinfo_endpoint)
oauth2_mem_free(p->userinfo_endpoint);
oauth2_mem_free(p);
end:
return;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, provider, issuer, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, provider, authorization_endpoint,
char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, provider, token_endpoint, char *,
str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, provider, userinfo_endpoint,
char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(openidc, provider, jwks_uri, char *, str)
liboauth2-1.4.4/src/openidc/resolver.c 0000664 0000000 0000000 00000022015 14161030125 0017617 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include "cache_int.h"
#include "cfg_int.h"
#include "openidc_int.h"
static bool
_oauth2_openidc_provider_metadata_parse(oauth2_log_t *log, const char *s_json,
oauth2_openidc_provider_t **provider)
{
bool rc = false;
json_t *json = NULL;
oauth2_openidc_provider_t *p = NULL;
oauth2_debug(log, "enter");
if (oauth2_json_decode_object(log, s_json, &json) == false) {
oauth2_error(log, "could not parse json object");
goto end;
}
*provider = oauth2_openidc_provider_init(log);
p = *provider;
if (p == NULL)
goto end;
if ((oauth2_json_string_get(log, json, "issuer", &p->issuer, NULL) ==
false) ||
(p->issuer == NULL)) {
oauth2_error(log, "could not parse issuer");
goto end;
}
if (oauth2_json_string_get(log, json, "authorization_endpoint",
&p->authorization_endpoint, NULL) == false) {
oauth2_error(log, "could not parse authorization_endpoint");
goto end;
}
if (oauth2_json_string_get(log, json, "token_endpoint",
&p->token_endpoint, NULL) == false) {
oauth2_error(log, "could not parse token_endpoint");
goto end;
}
if (oauth2_json_string_get(log, json, "userinfo_endpoint",
&p->userinfo_endpoint, NULL) == false) {
oauth2_error(log, "could not parse userinfo_endpoint");
goto end;
}
if (oauth2_json_string_get(log, json, "jwks_uri", &p->jwks_uri, NULL) ==
false) {
oauth2_error(log, "could not parse jwks_uri");
goto end;
}
rc = true;
end:
if ((rc == false) && (*provider)) {
oauth2_openidc_provider_free(log, *provider);
*provider = NULL;
}
if (json)
json_decref(json);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
#define OAUTH_OPENIDC_PROVIDER_CACHE_EXPIRY_DEFAULT 60 * 60 * 24
bool _oauth2_openidc_provider_resolve(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request,
const char *issuer,
oauth2_openidc_provider_t **provider)
{
bool rc = false;
char *s_json = NULL;
if ((cfg->provider_resolver == NULL) ||
(cfg->provider_resolver->callback == NULL)) {
oauth2_error(
log,
"configuration error: provider_resolver is not configured");
goto end;
}
if ((issuer) && (cfg->provider_resolver->cache)) {
oauth2_cache_get(log, cfg->provider_resolver->cache, issuer,
&s_json);
}
if (s_json == NULL) {
if (cfg->provider_resolver->callback(log, cfg, request,
&s_json) == false) {
oauth2_error(log, "resolver callback returned false");
goto end;
}
if (s_json == NULL) {
oauth2_error(
log, "no provider was returned by the provider "
"resolver; probably a configuration error");
goto end;
}
}
if (_oauth2_openidc_provider_metadata_parse(log, s_json, provider) ==
false)
goto end;
// TODO: cache expiry configuration option
if (cfg->provider_resolver->cache) {
oauth2_cache_set(
log, cfg->provider_resolver->cache,
oauth2_openidc_provider_issuer_get(log, *provider), s_json,
OAUTH_OPENIDC_PROVIDER_CACHE_EXPIRY_DEFAULT);
}
rc = true;
end:
if (s_json)
oauth2_mem_free(s_json);
return rc;
}
_OAUTH2_CFG_CTX_TYPE_SINGLE_STRING(oauth2_openidc_provider_resolver_file_ctx,
filename)
#define OAUTH2_OPENIDC_PROVIDER_RESOLVE_FILENAME_DEFAULT "conf/provider.json"
static bool _oauth2_openidc_provider_resolve_file(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request, char **s_json)
{
bool rc = false;
oauth2_openidc_provider_resolver_file_ctx_t *ctx = NULL;
char *filename = NULL;
oauth2_debug(log, "enter");
ctx = (oauth2_openidc_provider_resolver_file_ctx_t *)
cfg->provider_resolver->ctx->ptr;
filename = ctx->filename
? ctx->filename
: OAUTH2_OPENIDC_PROVIDER_RESOLVE_FILENAME_DEFAULT;
*s_json = oauth_read_file(log, filename);
if (*s_json == NULL)
goto end;
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
// TODO: must explicitly (re-)populate cache on startup!
#define OAUTH2_CFG_OPENIDC_PROVIDER_CACHE_DEFAULT 60 * 60 * 24
static char *_oauth2_cfg_openidc_provider_resolver_file_set_options(
oauth2_log_t *log, const char *value, const oauth2_nv_list_t *params,
void *c)
{
oauth2_cfg_openidc_t *cfg = (oauth2_cfg_openidc_t *)c;
// TODO: macroize?
cfg->provider_resolver = oauth2_cfg_openidc_provider_resolver_init(log);
cfg->provider_resolver->callback =
_oauth2_openidc_provider_resolve_file;
cfg->provider_resolver->ctx->callbacks =
&oauth2_openidc_provider_resolver_file_ctx_funcs;
cfg->provider_resolver->ctx->ptr =
cfg->provider_resolver->ctx->callbacks->init(log);
// TODO: factor out?
oauth2_openidc_provider_resolver_file_ctx_t *ctx =
(oauth2_openidc_provider_resolver_file_ctx_t *)
cfg->provider_resolver->ctx->ptr;
ctx->filename = oauth2_strdup(value);
cfg->provider_resolver->cache =
oauth2_cache_obtain(log, oauth2_nv_list_get(log, params, "cache"));
return NULL;
}
// DIR
static char *_oauth2_cfg_openidc_provider_resolver_dir_set_options(
oauth2_log_t *log, const char *value, const oauth2_nv_list_t *params,
void *c)
{
// oauth2_cfg_cache_set_options(
// log, resolver->cache, "resolver",
// params, OAUTH2_CFG_OPENIDC_PROVIDER_CACHE_DEFAULT);
return NULL;
}
// STRING
_OAUTH2_CFG_CTX_TYPE_SINGLE_STRING(oauth2_openidc_provider_resolver_str_ctx,
metadata)
static bool _oauth2_openidc_provider_resolve_string(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request, char **s_json)
{
bool rc = false;
oauth2_openidc_provider_resolver_str_ctx_t *ctx = NULL;
oauth2_debug(log, "enter");
ctx = (oauth2_openidc_provider_resolver_str_ctx_t *)
cfg->provider_resolver->ctx->ptr;
if (ctx->metadata == NULL) {
oauth2_error(log, "metadata not configured");
goto end;
}
*s_json = oauth2_strdup(ctx->metadata);
rc = true;
end:
oauth2_debug(log, "leave: %d", rc);
return rc;
}
static char *_oauth2_cfg_openidc_provider_resolver_string_set_options(
oauth2_log_t *log, const char *value, const oauth2_nv_list_t *params,
void *c)
{
oauth2_cfg_openidc_t *cfg = (oauth2_cfg_openidc_t *)c;
oauth2_openidc_provider_resolver_str_ctx_t *ctx = NULL;
cfg->provider_resolver = oauth2_cfg_openidc_provider_resolver_init(log);
cfg->provider_resolver->callback =
_oauth2_openidc_provider_resolve_string;
cfg->provider_resolver->ctx->callbacks =
&oauth2_openidc_provider_resolver_str_ctx_funcs;
cfg->provider_resolver->ctx->ptr =
cfg->provider_resolver->ctx->callbacks->init(log);
ctx = (oauth2_openidc_provider_resolver_str_ctx_t *)
cfg->provider_resolver->ctx->ptr;
ctx->metadata = oauth2_strdup(value);
return NULL;
}
#define OAUTH2_OPENIDC_PROVIDER_RESOLVER_STR_STR "string"
#define OAUTH2_OPENIDC_PROVIDER_RESOLVER_FILE_STR "file"
#define OAUTH2_OPENIDC_PROVIDER_RESOLVER_DIR_STR "dir"
// clang-format off
static oauth2_cfg_set_options_ctx_t _oauth2_cfg_resolver_options_set[] = {
{ OAUTH2_OPENIDC_PROVIDER_RESOLVER_STR_STR, _oauth2_cfg_openidc_provider_resolver_string_set_options },
{ OAUTH2_OPENIDC_PROVIDER_RESOLVER_FILE_STR, _oauth2_cfg_openidc_provider_resolver_file_set_options },
{ OAUTH2_OPENIDC_PROVIDER_RESOLVER_DIR_STR, _oauth2_cfg_openidc_provider_resolver_dir_set_options },
{ NULL, NULL }
};
// clang-format on
char *oauth2_cfg_openidc_provider_resolver_set_options(
oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, const char *type,
const char *value, const char *options)
{
char *rv = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_debug(log, "type=%s value=%s options=%s", type, value, options);
if (cfg->provider_resolver) {
oauth2_cfg_openidc_provider_resolver_free(
log, cfg->provider_resolver);
cfg->provider_resolver = NULL;
}
oauth2_parse_form_encoded_params(log, options, ¶ms);
cfg->session = _oauth2_cfg_session_obtain(
log, oauth2_nv_list_get(log, params, "session"));
if (cfg->session == NULL) {
rv = oauth2_strdup("could not configure session");
goto end;
}
rv = oauth2_cfg_set_options(log, cfg, type, value, options,
_oauth2_cfg_resolver_options_set);
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", rv);
return rv;
}
liboauth2-1.4.4/src/openidc/state.c 0000664 0000000 0000000 00000034564 14161030125 0017112 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include "cfg_int.h"
#include "openidc_int.h"
typedef struct oauth2_openidc_proto_state_t {
json_t *state;
} oauth2_openidc_proto_state_t;
oauth2_openidc_proto_state_t *oauth2_openidc_proto_state_init(oauth2_log_t *log)
{
oauth2_openidc_proto_state_t *p =
oauth2_mem_alloc(sizeof(oauth2_openidc_proto_state_t));
p->state = json_object();
return p;
}
oauth2_openidc_proto_state_t *
oauth2_openidc_proto_state_clone(oauth2_log_t *log,
const oauth2_openidc_proto_state_t *src)
{
oauth2_openidc_proto_state_t *dst =
oauth2_openidc_proto_state_init(log);
if (dst->state)
json_decref(dst->state);
dst->state = json_deep_copy(src->state);
return dst;
}
void oauth2_openidc_proto_state_free(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p)
{
if (p->state)
json_decref(p->state);
oauth2_mem_free(p);
}
bool oauth2_openidc_proto_state_set(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
const char *name, const char *value)
{
json_object_set_new(p->state, name, json_string(value));
return true;
}
bool oauth2_openidc_proto_state_set_int(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
const char *name,
const json_int_t value)
{
json_object_set_new(p->state, name, json_integer(value));
return true;
}
bool oauth2_openidc_proto_state_json_set(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
json_t *json)
{
if (p->state)
json_decref(p->state);
p->state = json;
return true;
}
json_t *
oauth2_openidc_proto_state_json_get(const oauth2_openidc_proto_state_t *p)
{
return p->state;
}
bool oauth2_openidc_proto_state_target_link_uri_get(
oauth2_log_t *log, oauth2_openidc_proto_state_t *p, char **value)
{
return oauth2_json_string_get(
log, oauth2_openidc_proto_state_json_get(p),
_OAUTH2_OPENIDC_PROTO_STATE_KEY_TARGET_LINK_URI, value, NULL);
}
bool oauth2_openidc_proto_state_pkce_get(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
char **value)
{
return oauth2_json_string_get(
log, oauth2_openidc_proto_state_json_get(p),
_OAUTH2_OPENIDC_PROTO_STATE_KEY_PKCE, value, NULL);
}
static oauth2_openidc_proto_state_t *_oauth2_openidc_proto_state_create(
oauth2_log_t *log, oauth2_openidc_provider_t *provider,
const char *target_link_uri, const char *pkce,
const oauth2_http_request_t *request)
{
oauth2_openidc_proto_state_t *p = oauth2_openidc_proto_state_init(log);
oauth2_openidc_proto_state_set(
log, p, _OAUTH2_OPENIDC_PROTO_STATE_KEY_ISSUER,
oauth2_openidc_provider_issuer_get(log, provider));
oauth2_openidc_proto_state_set(
log, p, _OAUTH2_OPENIDC_PROTO_STATE_KEY_TARGET_LINK_URI,
target_link_uri);
oauth2_openidc_proto_state_set(
log, p, _OAUTH2_OPENIDC_PROTO_STATE_KEY_PKCE, pkce);
oauth2_openidc_proto_state_set_int(
log, p, _OAUTH2_OPENIDC_PROTO_STATE_KEY_REQUEST_METHOD,
oauth2_http_request_method_get(log, request));
oauth2_openidc_proto_state_set_int(
log, p, _OAUTH2_OPENIDC_PROTO_STATE_KEY_TIMESTAMP,
oauth2_time_now_sec());
// TODO: response mode _OAUTH2_OPENIDC_PROTO_STATE_KEY_RESPONSE_MODE
// TODO: response type _OAUTH2_OPENIDC_PROTO_STATE_KEY_RESPONSE_TYPE
return p;
}
typedef struct oidc_state_cookies_t {
char *name;
oauth2_time_t timestamp;
char *target_uri;
struct oidc_state_cookies_t *next;
} oidc_state_cookies_t;
static bool _oauth2_openidc_cookie_clear(oauth2_log_t *log,
oauth2_http_response_t *response,
const char *name, const char *path,
const bool is_secure)
{
return oauth2_http_response_cookie_set(
log, response, name, NULL, path, is_secure, OAUTH2_CFG_TIME_UNSET);
}
static int _oauth2_openidc_delete_oldest_state_cookies(
oauth2_log_t *log, oauth2_http_response_t *response, const char *path,
int number_of_valid_state_cookies, int max_number_of_state_cookies,
oidc_state_cookies_t **first, const bool is_secure)
{
oidc_state_cookies_t *cur = NULL, *prev = NULL, *prev_oldest = NULL,
*oldest = NULL;
while (number_of_valid_state_cookies >= max_number_of_state_cookies) {
oldest = *first;
prev_oldest = NULL;
prev = *first;
cur = (*first)->next;
while (cur) {
if ((cur->timestamp < oldest->timestamp)) {
oldest = cur;
prev_oldest = prev;
}
prev = cur;
cur = cur->next;
}
oauth2_warn(
log,
"deleting oldest state cookie: %s ; time until "
"expiry " OAUTH2_TIME_T_FORMAT " seconds [target_uri=%s]",
oldest->name, oldest->timestamp - oauth2_time_now_sec(),
oldest->target_uri);
_oauth2_openidc_cookie_clear(log, response, oldest->name, path,
is_secure);
if (prev_oldest)
prev_oldest->next = oldest->next;
else
*first = (*first)->next;
number_of_valid_state_cookies--;
oauth2_mem_free(oldest->name);
oauth2_mem_free(oldest->target_uri);
oauth2_mem_free(oldest);
}
return number_of_valid_state_cookies;
}
static bool _oauth2_openidc_state_expired(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_openidc_proto_state_t *proto_state, oauth2_time_t *tsr)
{
bool rc = true;
oauth2_time_t now, exp;
oauth2_time_t ts;
now = oauth2_time_now_sec();
ts = json_integer_value(
json_object_get(oauth2_openidc_proto_state_json_get(proto_state),
_OAUTH2_OPENIDC_PROTO_STATE_KEY_TIMESTAMP));
exp = oauth2_cfg_openidc_state_cookie_timeout_get(log, cfg);
if (now > ts + exp) {
oauth2_error(log, "state expired: now: %d, then: %d, ttl: %d",
now, ts, exp);
goto end;
}
rc = false;
end:
if (tsr)
*tsr = ts;
return rc;
}
static bool _oauth2_openidc_get_state_from_cookie(
oauth2_log_t *log, const char *value,
oauth2_openidc_proto_state_t **proto_state)
{
bool rc = false;
json_t *json = NULL;
if (oauth2_jose_jwt_decrypt(log, oauth2_crypto_passphrase_get(log),
value, &json) == false)
goto end;
*proto_state = oauth2_openidc_proto_state_init(log);
oauth2_openidc_proto_state_json_set(log, *proto_state, json);
rc = true;
end:
return rc;
}
static oidc_state_cookies_t *
_oauth2_openidc_cookie_valid(oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response, char *cookie,
const char *path)
{
oidc_state_cookies_t *entry = NULL;
oauth2_openidc_proto_state_t *proto_state = NULL;
char *cookieStart = NULL, *cookieName = NULL, *cookieValue = NULL;
oauth2_time_t ts;
char *target_uri = NULL;
cookieStart = cookie;
while (cookie != NULL && *cookie != '=')
cookie++;
if (*cookie != '=')
goto end;
*cookie = '\0';
cookie++;
cookieName = oauth2_url_decode(log, cookieStart);
cookieValue = oauth2_url_decode(log, cookie);
if ((_oauth2_openidc_get_state_from_cookie(log, cookieValue,
&proto_state) == false) ||
(proto_state == NULL)) {
oauth2_warn(
log,
"state cookie could not be retrieved/decoded, deleting: %s",
cookieName);
_oauth2_openidc_cookie_clear(
log, response, cookieName, path,
oauth2_http_request_is_secure(log, request));
goto end;
}
oauth2_openidc_proto_state_target_link_uri_get(log, proto_state,
&target_uri);
if (_oauth2_openidc_state_expired(log, cfg, proto_state, &ts)) {
oauth2_warn(log, "state (%s) has expired [target_uri=%s]",
cookieName, target_uri);
_oauth2_openidc_cookie_clear(
log, response, cookieName, path,
oauth2_http_request_is_secure(log, request));
goto end;
}
entry = oauth2_mem_alloc(sizeof(oidc_state_cookies_t));
entry->name = oauth2_strdup(cookieName);
entry->timestamp = ts;
entry->target_uri = oauth2_strdup(target_uri);
entry->next = NULL;
end:
if (cookieName)
oauth2_mem_free(cookieName);
if (cookieValue)
oauth2_mem_free(cookieValue);
if (target_uri)
oauth2_mem_free(target_uri);
if (proto_state)
oauth2_openidc_proto_state_free(log, proto_state);
return entry;
}
static bool _oauth2_openidc_clean_expired_state_cookies(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request, oauth2_http_response_t *response)
{
bool rc = false;
char *cookies = NULL, *save_ptr = NULL;
oidc_state_cookies_t *first = NULL, *last = NULL, *entry = NULL;
const char delim[2] = ";";
int number_of_valid_state_cookies = 0;
char *cookieStr = NULL;
// TODO: session reference...?
const char *path = oauth2_cfg_session_cookie_path_get(
log, oauth2_cfg_openidc_session_get(log, cfg));
cookies =
oauth2_strdup(oauth2_http_request_header_cookie_get(log, request));
if (cookies == NULL) {
rc = true;
goto end;
}
cookieStr = strtok_r(cookies, delim, &save_ptr);
while (cookieStr != NULL) {
while (*cookieStr == ' ')
cookieStr++;
if (strstr(cookieStr,
oauth2_cfg_openidc_state_cookie_name_prefix_get(
log, cfg)) != cookieStr)
goto cont;
entry = _oauth2_openidc_cookie_valid(log, cfg, request,
response, cookieStr, path);
if (entry == NULL)
goto cont;
if (first == NULL) {
first = entry;
last = first;
} else {
last->next = entry;
last = last->next;
}
number_of_valid_state_cookies++;
cont:
cookieStr = strtok_r(NULL, delim, &save_ptr);
}
if ((number_of_valid_state_cookies >=
oauth2_cfg_openidc_state_cookie_max_get(log, cfg)) &&
(oauth2_cfg_openidc_state_cookie_delete_oldest_get(log, cfg) ==
false)) {
oauth2_debug(log,
"max number of state cookies has been reached");
goto end;
}
_oauth2_openidc_delete_oldest_state_cookies(
log, response, path, number_of_valid_state_cookies,
oauth2_cfg_openidc_state_cookie_max_get(log, cfg), &first,
oauth2_http_request_is_secure(log, request));
rc = true;
end:
while (first) {
entry = first;
first = first->next;
oauth2_mem_free(entry->name);
oauth2_mem_free(entry->target_uri);
oauth2_mem_free(entry);
}
if (cookies)
oauth2_mem_free(cookies);
return rc;
}
/*
* state cookie handling
*/
bool _oauth2_openidc_state_cookie_set(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_openidc_provider_t *provider,
const oauth2_http_request_t *request,
oauth2_http_response_t *response,
const char *state, const char *pkce)
{
bool rc = false;
char *name = NULL, *value = NULL, *target_link_uri = NULL;
oauth2_openidc_proto_state_t *proto_state = NULL;
const char *path = NULL;
name = oauth2_stradd(
name, oauth2_cfg_openidc_state_cookie_name_prefix_get(log, cfg),
state, NULL);
if (name == NULL)
goto end;
if (_oauth2_openidc_clean_expired_state_cookies(log, cfg, request,
response) == false)
goto end;
target_link_uri = oauth2_http_request_url_get(log, request);
// TODO: add different state policy that keeps track in the
// shared cache of outstanding parallel requests from the same
// client (ip/user-agent) against a configurable maximum and
// uses only a single shared cookie across those requests
// (accepting consecutive responses, or take the last one)
proto_state = _oauth2_openidc_proto_state_create(
log, provider, target_link_uri, pkce, request);
if (oauth2_jose_jwt_encrypt(
log, oauth2_crypto_passphrase_get(log),
oauth2_openidc_proto_state_json_get(proto_state),
&value) == false)
goto end;
path = oauth2_cfg_session_cookie_path_get(
log, oauth2_cfg_openidc_session_get(log, cfg));
rc = oauth2_http_response_cookie_set(
log, response, name, value, path,
oauth2_http_request_is_secure(log, request),
oauth2_cfg_openidc_state_cookie_timeout_get(log, cfg));
end:
if (proto_state)
oauth2_openidc_proto_state_free(log, proto_state);
if (name)
oauth2_mem_free(name);
if (value)
oauth2_mem_free(value);
if (target_link_uri)
oauth2_mem_free(target_link_uri);
return rc;
}
bool _oauth2_openidc_state_cookie_get(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request, oauth2_http_response_t *response,
const char *state, oauth2_openidc_proto_state_t **proto_state)
{
bool rc = false;
char *name = NULL, *value = NULL;
const char *path = NULL;
name = oauth2_stradd(
name, oauth2_cfg_openidc_state_cookie_name_prefix_get(log, cfg),
state, NULL);
if (name == NULL)
goto end;
value = oauth2_http_request_cookie_get(log, request, name, true);
if (value == NULL) {
oauth2_warn(log, "no state cookie found");
goto end;
}
path = oauth2_cfg_session_cookie_path_get(
log, oauth2_cfg_openidc_session_get(log, cfg));
rc = _oauth2_openidc_cookie_clear(
log, response, name, path,
oauth2_http_request_is_secure(log, request));
if (rc == false)
goto end;
rc = _oauth2_openidc_get_state_from_cookie(log, value, proto_state);
end:
if (name)
oauth2_mem_free(name);
if (value)
oauth2_mem_free(value);
return rc;
}
bool _oauth2_openidc_state_validate(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request,
oauth2_openidc_proto_state_t *proto_state,
oauth2_openidc_provider_t **provider)
{
bool rc = false;
const char *iss = NULL;
iss = json_string_value(
json_object_get(oauth2_openidc_proto_state_json_get(proto_state),
_OAUTH2_OPENIDC_PROTO_STATE_KEY_ISSUER));
if (iss == NULL) {
oauth2_error(log, "no issuer (key=%s) found in state",
_OAUTH2_OPENIDC_PROTO_STATE_KEY_ISSUER);
goto end;
}
if (_oauth2_openidc_provider_resolve(log, cfg, request, iss,
provider) == false) {
oauth2_error(log,
"_oauth2_openidc_provider_resolve returned false");
goto end;
}
if (_oauth2_openidc_state_expired(log, cfg, proto_state, NULL))
goto end;
rc = true;
end:
return rc;
}
liboauth2-1.4.4/src/openidc_int.h 0000664 0000000 0000000 00000006123 14161030125 0016637 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_OPENIDC_INT_H_
#define _OAUTH2_OPENIDC_INT_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/openidc.h"
typedef struct oauth2_openidc_provider_t {
char *issuer;
char *authorization_endpoint;
char *token_endpoint;
char *jwks_uri;
char *userinfo_endpoint;
} oauth2_openidc_provider_t;
typedef struct oauth2_openidc_client_t {
char *client_id;
char *client_secret;
char *scope;
oauth2_cfg_endpoint_auth_t *token_endpoint_auth;
oauth2_flag_t ssl_verify;
oauth2_uint_t http_timeout;
oauth2_openidc_provider_t *provider;
} oauth2_openidc_client_t;
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_ISSUER "i"
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_TARGET_LINK_URI "l"
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_REQUEST_METHOD "m"
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_RESPONSE_MODE "r"
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_RESPONSE_TYPE "y"
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_TIMESTAMP "t"
#define _OAUTH2_OPENIDC_PROTO_STATE_KEY_PKCE "p"
bool _oauth2_openidc_state_cookie_get(
oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request, oauth2_http_response_t *response,
const char *state, oauth2_openidc_proto_state_t **proto_state);
bool _oauth2_openidc_state_cookie_set(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_openidc_provider_t *provider,
const oauth2_http_request_t *request,
oauth2_http_response_t *response,
const char *state, const char *pkce);
bool _oauth2_openidc_state_validate(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
oauth2_http_request_t *request,
oauth2_openidc_proto_state_t *proto_state,
oauth2_openidc_provider_t **provider);
bool oauth2_openidc_proto_state_target_link_uri_get(
oauth2_log_t *log, oauth2_openidc_proto_state_t *p, char **value);
bool oauth2_openidc_proto_state_pkce_get(oauth2_log_t *log,
oauth2_openidc_proto_state_t *p,
char **value);
bool _oauth2_openidc_provider_resolve(oauth2_log_t *log,
const oauth2_cfg_openidc_t *cfg,
const oauth2_http_request_t *request,
const char *issuer,
oauth2_openidc_provider_t **provider);
#endif /* _OAUTH2_OPENIDC_INT_H_ */
liboauth2-1.4.4/src/proto.c 0000664 0000000 0000000 00000032206 14161030125 0015503 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/proto.h"
#include "oauth2/mem.h"
#include "cfg_int.h"
#include "util_int.h"
#include
#define OAUTH2_CFG_SOURCE_TOKEN_HEADER_NAME_DEFAULT \
OAUTH2_HTTP_HDR_AUTHORIZATION
#define OAUTH2_CFG_SOURCE_TOKEN_HEADER_TYPE_DEFAULT OAUTH2_HTTP_HDR_BEARER
static char *
_oauth2_get_source_token_from_header(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request)
{
char *source_token = NULL;
char *scheme = NULL;
const char *auth_line = NULL;
char *name = NULL;
char *type = NULL;
oauth2_debug(log, "enter");
name = cfg->accept_in.header.name
? cfg->accept_in.header.name
: OAUTH2_CFG_SOURCE_TOKEN_HEADER_NAME_DEFAULT;
type = cfg->accept_in.header.type
? cfg->accept_in.header.type
: OAUTH2_CFG_SOURCE_TOKEN_HEADER_TYPE_DEFAULT;
auth_line = oauth2_http_request_header_get(log, request, name);
if (auth_line == NULL)
goto end;
oauth2_debug(log, "%s header found", name);
if ((type != NULL) && (strcmp(type, "") != 0)) {
scheme = oauth2_getword(&auth_line, ' ');
if (strcasecmp(scheme, type) != 0) {
oauth2_warn(
log,
"client used unsupported authentication scheme: %s",
scheme);
goto end;
}
}
while (isspace(*auth_line))
auth_line++;
source_token = oauth2_strdup(auth_line);
if (source_token != NULL)
if (oauth2_cfg_source_token_get_strip(cfg) != 0)
oauth2_http_request_header_unset(log, request, name);
end:
if (scheme)
oauth2_mem_free(scheme);
oauth2_debug(log, "leave: %s", source_token);
return source_token;
}
#define OAUTH2_CFG_SOURCE_TOKEN_QUERY_PARAMNAME_DEFAULT "access_token"
static char *_oauth2_get_source_token_from_query(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request)
{
char *source_token = NULL;
char *name = NULL;
oauth2_debug(log, "enter");
name = cfg->accept_in.query.param_name
? cfg->accept_in.query.param_name
: OAUTH2_CFG_SOURCE_TOKEN_QUERY_PARAMNAME_DEFAULT;
source_token = oauth2_strdup(
oauth2_http_request_query_param_get(log, request, name));
if (source_token == NULL) {
oauth2_debug(
log, "no source token found in query parameter: %s", name);
} else if (oauth2_cfg_source_token_get_strip(cfg) != 0) {
oauth2_debug(log,
"stripping query param %s from outgoing request",
name);
oauth2_http_request_query_param_unset(log, request, name);
}
return source_token;
}
#define OAUTH2_CFG_SOURCE_TOKEN_POST_PARAMNAME_DEFAULT "access_token"
static char *_oauth2_get_source_token_from_post(
oauth2_log_t *log, oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request, oauth2_cfg_server_callback_funcs_t *srv_cb,
void *srv_cb_ctx)
{
char *source_token = NULL;
oauth2_nv_list_t *params = NULL;
const char *content_type = NULL;
char *name = NULL;
oauth2_debug(log, "enter");
name = cfg->accept_in.post.param_name
? cfg->accept_in.post.param_name
: OAUTH2_CFG_SOURCE_TOKEN_POST_PARAMNAME_DEFAULT;
content_type =
oauth2_http_request_header_content_type_get(log, request);
if ((oauth2_http_request_method_get(log, request) !=
OAUTH2_HTTP_METHOD_POST) ||
(strcasecmp(content_type, OAUTH2_CONTENT_TYPE_FORM_ENCODED) != 0)) {
oauth2_debug(log, "no form-encoded HTTP POST");
goto end;
}
if (srv_cb->form_post(log, srv_cb_ctx, ¶ms) == false) {
oauth2_error(log, "HTTP POST read callback failed");
goto end;
}
source_token = oauth2_strdup(oauth2_nv_list_get(log, params, name));
if (source_token == NULL) {
oauth2_debug(log, "no source token found in POST parameter: %s",
name);
} else if (oauth2_cfg_source_token_get_strip(cfg) != 0) {
// TBD: would work if we can remove stuff across
// brigades/buckets in the
// input filter...
// sts_userdata_set_post_param(r, post_param_name,
// NULL);
oauth2_warn(log,
"stripping post param %s from outgoing request "
"is not supported!",
name);
}
end:
if (params)
oauth2_nv_list_free(log, params);
oauth2_debug(log, "leave: %s", source_token);
return source_token;
}
#define OAUTH2_CFG_SOURCE_TOKEN_COOKIE_NAME_DEFAULT "access_token"
static char *
_oauth2_get_source_token_from_cookie(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request)
{
char *source_token = NULL;
char *name = NULL;
oauth2_debug(log, "enter");
name = cfg->accept_in.cookie.name
? cfg->accept_in.cookie.name
: OAUTH2_CFG_SOURCE_TOKEN_COOKIE_NAME_DEFAULT;
source_token = oauth2_http_request_cookie_get(
log, request, name, oauth2_cfg_source_token_get_strip(cfg));
if (source_token == NULL)
oauth2_debug(log, "no source token found in cookie: %s", name);
return source_token;
}
#define OAUTH2_CFG_SOURCE_TOKEN_ENVVAR_NAME_DEFAULT "access_token"
static char *_oauth2_get_source_token_from_envvar(
oauth2_log_t *log, oauth2_cfg_source_token_t *cfg,
oauth2_cfg_server_callback_funcs_t *srv_cb, void *srv_cb_ctx)
{
char *source_token = NULL;
char *name = NULL;
oauth2_debug(log, "enter");
name = cfg->accept_in.envvar.name
? cfg->accept_in.envvar.name
: OAUTH2_CFG_SOURCE_TOKEN_ENVVAR_NAME_DEFAULT;
if (srv_cb->get(log, srv_cb_ctx, name, &source_token) == false) {
oauth2_error(log, "environment variable get callback failed");
goto end;
}
if (source_token == NULL) {
oauth2_debug(log,
"no source token found in %s environment variable",
name);
goto end;
};
if (oauth2_cfg_source_token_get_strip(cfg))
srv_cb->set(log, srv_cb_ctx, name, NULL);
end:
return source_token;
}
static char *_oauth2_get_source_token_from_basic(oauth2_log_t *log,
oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request)
{
char *source_token = NULL;
char *decoded_line = NULL;
size_t decoded_len = 0;
const char *auth_line = NULL;
char *ptr = NULL;
char *scheme = NULL;
oauth2_debug(log, "enter");
auth_line = oauth2_http_request_header_get(
log, request, OAUTH2_HTTP_HDR_AUTHORIZATION);
if (auth_line == NULL)
goto end;
oauth2_debug(log, "%s header found", OAUTH2_HTTP_HDR_AUTHORIZATION);
scheme = oauth2_getword(&auth_line, ' ');
if ((scheme == NULL) || (strcasecmp(scheme, "Basic") != 0)) {
oauth2_warn(log,
"client used unsupported "
"authentication scheme: %s",
scheme);
goto end;
}
while (isspace(*auth_line))
auth_line++;
if (oauth2_base64_decode(log, auth_line, (uint8_t **)&decoded_line,
&decoded_len) == false)
goto end;
decoded_line[decoded_len] = '\0';
ptr = decoded_line;
if (strchr(ptr, ':') != NULL) {
oauth2_mem_free(oauth2_getword((const char **)&ptr, ':'));
source_token = oauth2_strdup(ptr);
}
if ((source_token != NULL) &&
(oauth2_cfg_source_token_get_strip(cfg) != 0))
oauth2_http_request_header_unset(log, request,
OAUTH2_HTTP_HDR_AUTHORIZATION);
end:
if (scheme)
oauth2_mem_free(scheme);
if (decoded_line)
oauth2_mem_free(decoded_line);
return source_token;
}
char *oauth2_get_source_token(oauth2_log_t *log, oauth2_cfg_source_token_t *cfg,
oauth2_http_request_t *request,
oauth2_cfg_server_callback_funcs_t *srv_cb,
void *srv_cb_ctx)
{
char *source_token = NULL;
char accept_source_token_in =
oauth2_cfg_source_token_get_accept_in(cfg);
if ((source_token == NULL) &&
(accept_source_token_in & OAUTH2_CFG_TOKEN_IN_ENVVAR))
source_token = _oauth2_get_source_token_from_envvar(
log, cfg, srv_cb, srv_cb_ctx);
if ((source_token == NULL) &&
(accept_source_token_in & OAUTH2_CFG_TOKEN_IN_HEADER))
source_token =
_oauth2_get_source_token_from_header(log, cfg, request);
if ((source_token == NULL) &&
(accept_source_token_in & OAUTH2_CFG_TOKEN_IN_QUERY)) {
source_token =
_oauth2_get_source_token_from_query(log, cfg, request);
}
if ((source_token == NULL) &&
(accept_source_token_in & OAUTH2_CFG_TOKEN_IN_POST)) {
source_token = _oauth2_get_source_token_from_post(
log, cfg, request, srv_cb, srv_cb_ctx);
}
if ((source_token == NULL) &&
(accept_source_token_in & OAUTH2_CFG_TOKEN_IN_COOKIE))
source_token =
_oauth2_get_source_token_from_cookie(log, cfg, request);
if ((source_token == NULL) &&
(accept_source_token_in & OAUTH2_CFG_TOKEN_IN_BASIC))
source_token =
_oauth2_get_source_token_from_basic(log, cfg, request);
if (source_token == NULL) {
oauth2_debug(log,
"no source token found in any of the configured "
"methods: %x",
accept_source_token_in);
}
return source_token;
}
bool oauth2_proto_request(oauth2_log_t *log,
const oauth2_cfg_endpoint_t *token_endpoint,
oauth2_http_call_ctx_t *ctx,
const oauth2_nv_list_t *params, char **rtoken,
oauth2_uint_t *status_code)
{
bool rc = false;
char *response = NULL;
json_t *result = NULL;
char *tkn = NULL;
oauth2_http_call_ctx_ssl_verify_set(
log, ctx, oauth2_cfg_endpoint_get_ssl_verify(token_endpoint));
oauth2_http_call_ctx_timeout_set(
log, ctx, oauth2_cfg_endpoint_get_http_timeout(token_endpoint));
// oauth2_http_call_ctx_outgoing_proxy_set(log, ctx, outgoing_proxy);
if (oauth2_http_post_form(log,
oauth2_cfg_endpoint_get_url(token_endpoint),
params, ctx, &response, status_code) == false)
goto end;
if ((*status_code < 200) || (*status_code >= 300))
goto end;
if (oauth2_json_decode_check_error(log, response, &result) == false)
goto end;
if (oauth2_json_string_get(log, result, OAUTH2_ACCESS_TOKEN, &tkn,
NULL) == false)
goto end;
if (tkn == NULL) {
oauth2_error(log, "no access token found in result");
goto end;
}
*rtoken = oauth2_strdup(tkn);
rc = true;
/*
char **token_type = NULL;
sts_util_json_object_get_string(r->pool, result, "token_type",
token_type,
NULL);
if (token_type != NULL) {
if (oidc_proto_validate_token_type(r, provider, *token_type) == FALSE)
{
oidc_warn(r, "access token type did not validate, dropping it");
*access_token = NULL;
}
}
sts_util_json_object_get_int(r->pool, result, OIDC_PROTO_EXPIRES_IN,
expires_in,
-1);
sts_util_json_object_get_string(r->pool, result,
OIDC_PROTO_REFRESH_TOKEN,
refresh_token,
NULL);
*/
end:
if (response)
oauth2_mem_free(response);
if (tkn)
oauth2_mem_free(tkn);
if (result)
json_decref(result);
return rc;
}
#define OAUTH2_PROTO_ROPC_GRANT_TYPE_VALUE "password"
#define OAUTH2_PROTO_ROPC_USERNAME "username"
#define OAUTH2_PROTO_ROPC_PASSWORD "password"
bool oauth2_ropc_exec(oauth2_log_t *log, oauth2_cfg_ropc_t *cfg,
const char *username, const char *password, char **rtoken,
oauth2_uint_t *status_code)
{
bool rc = false;
oauth2_nv_list_t *params = NULL;
oauth2_http_call_ctx_t *ctx = NULL;
const char *client_id = oauth2_cfg_ropc_get_client_id(cfg);
const oauth2_cfg_endpoint_t *token_endpoint =
oauth2_cfg_ropc_get_token_endpoint(cfg);
oauth2_debug(log, "enter");
if (cfg == NULL) {
oauth2_error(log, "token endpoint cfg is not set");
goto end;
}
if (token_endpoint == NULL) {
oauth2_warn(log, "token endpoint is not set");
goto end;
}
params = oauth2_nv_list_init(log);
oauth2_nv_list_add(log, params, OAUTH2_GRANT_TYPE,
OAUTH2_PROTO_ROPC_GRANT_TYPE_VALUE);
if ((oauth2_cfg_endpoint_auth_type(oauth2_cfg_endpoint_get_auth(
token_endpoint)) == OAUTH2_ENDPOINT_AUTH_NONE) &&
(client_id != NULL))
oauth2_nv_list_add(log, params, OAUTH2_CLIENT_ID, client_id);
if (username != NULL)
oauth2_nv_list_add(log, params, OAUTH2_PROTO_ROPC_USERNAME,
username);
oauth2_nv_list_add(log, params, OAUTH2_PROTO_ROPC_PASSWORD, password);
oauth2_nv_list_merge_into(
log, oauth2_cfg_ropc_get_request_parameters(cfg), params);
ctx = oauth2_http_call_ctx_init(log);
if (ctx == NULL)
goto end;
oauth2_http_call_ctx_ssl_verify_set(
log, ctx, oauth2_cfg_endpoint_get_ssl_verify(token_endpoint));
oauth2_http_call_ctx_outgoing_proxy_set(
log, ctx, oauth2_cfg_endpoint_get_outgoing_proxy(token_endpoint));
if (oauth2_http_ctx_auth_add(
log, ctx, oauth2_cfg_endpoint_get_auth(token_endpoint),
params) == false)
goto end;
rc = oauth2_proto_request(log, oauth2_cfg_ropc_get_token_endpoint(cfg),
ctx, params, rtoken, status_code);
end:
if (params)
oauth2_nv_list_free(log, params);
if (ctx)
oauth2_http_call_ctx_free(log, ctx);
oauth2_debug(log, "leave: %d", rc);
return rc;
}
liboauth2-1.4.4/src/server/ 0000775 0000000 0000000 00000000000 14161030125 0015477 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/src/server/.gitignore 0000664 0000000 0000000 00000000056 14161030125 0017470 0 ustar 00root root 0000000 0000000 /.dirstamp
/.deps/
/.libs/
/*.lo
/*.o
/*.gcno
liboauth2-1.4.4/src/server/apache.c 0000664 0000000 0000000 00000065756 14161030125 0017107 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// clang-format off
oauth2_uint_t log_level_apache2oauth2[] = {
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_WARN,
OAUTH2_LOG_NOTICE,
OAUTH2_LOG_INFO,
OAUTH2_LOG_DEBUG,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE2,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE1,
OAUTH2_LOG_TRACE1
};
oauth2_uint_t log_level_log2apache[] = {
APLOG_ERR,
APLOG_WARNING,
APLOG_NOTICE,
APLOG_INFO,
APLOG_DEBUG,
APLOG_TRACE1,
APLOG_TRACE1
};
oauth2_http_method_t request_method_apache2oauth2[] = {
OAUTH2_HTTP_METHOD_GET,
OAUTH2_HTTP_METHOD_PUT,
OAUTH2_HTTP_METHOD_POST,
OAUTH2_HTTP_METHOD_DELETE,
OAUTH2_HTTP_METHOD_CONNECT,
OAUTH2_HTTP_METHOD_OPTIONS,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN,
OAUTH2_HTTP_METHOD_UNKNOWN
};
// clang-format on
static apr_status_t oauth2_apache_cfg_srv_free(void *data)
{
oauth2_apache_cfg_srv_t *cfg = (oauth2_apache_cfg_srv_t *)data;
// ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
// (const server_rec
//*)oauth2_log_sink_ctx_get(cfg->sink),
// "%s: %s: %pp", __FUNCTION__, "free", cfg);
if (cfg) {
if (cfg->log)
oauth2_log_free(cfg->log);
oauth2_mem_free(cfg);
}
return APR_SUCCESS;
}
void *oauth2_apache_cfg_srv_create(apr_pool_t *pool, server_rec *s,
oauth2_log_function_t server_log_cb)
{
oauth2_apache_cfg_srv_t *cfg =
(oauth2_apache_cfg_srv_t *)oauth2_mem_alloc(
sizeof(oauth2_apache_cfg_srv_t));
// NB: this is not actually set to the/a configured level here...
oauth2_uint_t level = (s && (s->log.level != -1))
? log_level_apache2oauth2[s->log.level]
: OAUTH2_LOG_TRACE1;
cfg->sink = oauth2_log_sink_create(level, server_log_cb, s);
cfg->log = oauth2_log_init(level, cfg->sink);
// ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
// (const server_rec
//*)oauth2_log_sink_ctx_get(cfg->sink),
// "%s: %s: %pp", __FUNCTION__, "create", cfg);
// note: cleanup as part of oauth2_apache_child_cleanup does not work
// with multiple modules loaded
apr_pool_cleanup_register(pool, cfg, oauth2_apache_cfg_srv_free,
oauth2_apache_cfg_srv_free);
return cfg;
}
void *oauth2_apache_cfg_srv_merge(apr_pool_t *pool, void *b, void *a)
{
oauth2_apache_cfg_srv_t *add = (oauth2_apache_cfg_srv_t *)a;
oauth2_apache_cfg_srv_t *cfg = oauth2_apache_cfg_srv_create(
pool, (server_rec *)oauth2_log_sink_ctx_get(add->sink),
oauth2_log_sink_ctx_get(add->sink));
// ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
// (const server_rec
//*)oauth2_log_sink_ctx_get(cfg->sink),
// "%s: %s: %pp", __FUNCTION__, "merge", cfg);
return cfg;
}
/*
* parent/child cleanup
*/
apr_status_t oauth2_apache_child_cleanup(void *data, module *m,
const char *package_name_version)
{
oauth2_shutdown(NULL);
return APR_SUCCESS;
}
apr_status_t oauth2_apache_parent_cleanup(void *data, module *m,
const char *package_name_version)
{
server_rec *s = (server_rec *)data;
oauth2_apache_cfg_srv_t *cfg =
(oauth2_apache_cfg_srv_t *)ap_get_module_config(s->module_config,
m);
oauth2_info(cfg->log, "%s-%s - shutdown", package_name_version,
oauth2_package_string());
oauth2_apache_child_cleanup(s, m, package_name_version);
return APR_SUCCESS;
}
APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
(apr_pool_t *, server_rec *, conn_rec *, request_rec *,
char *));
static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *_oauth2_ssl_var_lookup = NULL;
static const char *oauth2_apache_ssl_var_lookup(apr_pool_t *p, server_rec *s,
conn_rec *c, request_rec *r,
const char *var)
{
return (_oauth2_ssl_var_lookup != NULL)
? (const char *)_oauth2_ssl_var_lookup(p, s, c, r,
(char *)var)
: NULL;
}
/*
* post config
*/
int oauth2_apache_post_config(apr_pool_t *pool, apr_pool_t *p1, apr_pool_t *p2,
server_rec *s, module *m,
const char *package_name_version,
apr_status_t (*parent_cleanup)(void *),
apr_status_t (*child_cleanup)(void *))
{
void *data = NULL;
oauth2_log_t *p = NULL;
oauth2_apache_cfg_srv_t *cfg = NULL;
server_rec *sp = NULL;
apr_pool_userdata_get(&data, package_name_version, s->process->pool);
if (data == NULL) {
apr_pool_userdata_set((const void *)1, package_name_version,
apr_pool_cleanup_null, s->process->pool);
goto end;
}
p = oauth2_init(OAUTH2_LOG_INFO, NULL);
oauth2_log_free(p);
sp = s;
for (sp = s; sp; sp = sp->next) {
cfg = (oauth2_apache_cfg_srv_t *)ap_get_module_config(
sp->module_config, m);
// only now the level has been set according to the config!
oauth2_log_sink_level_set(
cfg->sink, (sp && (sp->log.level != -1))
? log_level_apache2oauth2[sp->log.level]
: OAUTH2_LOG_TRACE1);
}
apr_pool_cleanup_register(pool, s, parent_cleanup, child_cleanup);
_oauth2_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
cfg = (oauth2_apache_cfg_srv_t *)ap_get_module_config(s->module_config,
m);
oauth2_info(cfg->log, "%s-%s - init", package_name_version,
oauth2_package_string());
end:
return OK;
}
static int oauth2_apache_http_request_hdr_add(void *rec, const char *key,
const char *value)
{
oauth2_apache_request_ctx_t *ctx = (oauth2_apache_request_ctx_t *)rec;
return (oauth2_http_request_header_add(ctx->log, ctx->request, key,
value) == true);
}
static const char *oauth2_apache_get_server_name(request_rec *r)
{
const char *rv = NULL;
#ifdef APACHE2_0
rv = (char *)ap_get_server_name(r);
#else
rv = (char *)ap_get_server_name_for_url(r);
#endif
return rv;
}
static const char *oauth2_apache_http_scheme(request_rec *r)
{
const char *rv = NULL;
#ifdef APACHE2_0
rv = ap_http_method(r);
#else
rv = ap_http_scheme(r);
#endif
return rv;
}
static oauth2_apache_request_ctx_t *
oauth2_apache_request_context_init(request_rec *r,
oauth2_log_function_t request_log_cb)
{
oauth2_apache_request_ctx_t *ctx = NULL;
oauth2_log_sink_t *log_sink_apache = NULL;
// TODO: memory allocation failure checks...?
ctx = oauth2_mem_alloc(sizeof(oauth2_apache_request_ctx_t));
ctx->r = r;
// TODO: more elegant log-for-request handling
oauth2_log_level_t level = (r && r->log)
? log_level_apache2oauth2[r->log->level]
: OAUTH2_LOG_TRACE1;
log_sink_apache = oauth2_log_sink_create(level, request_log_cb, r);
ctx->log = oauth2_log_init(level, log_sink_apache);
ctx->request = oauth2_http_request_init(ctx->log);
oauth2_http_request_scheme_set(ctx->log, ctx->request,
oauth2_apache_http_scheme(r));
oauth2_http_request_hostname_set(ctx->log, ctx->request,
oauth2_apache_get_server_name(r));
oauth2_http_request_port_set(ctx->log, ctx->request,
r->connection->local_addr->port);
oauth2_http_request_path_set(ctx->log, ctx->request, r->uri);
oauth2_http_request_method_set(
ctx->log, ctx->request,
request_method_apache2oauth2[r->method_number]);
oauth2_http_request_query_set(ctx->log, ctx->request, r->args);
apr_table_do(oauth2_apache_http_request_hdr_add, ctx, r->headers_in,
NULL);
/*
* a workaround since mod_ssl's CGI envvar setting happens
* only in the fixup handler phase
*/
oauth2_http_request_context_set(
ctx->log, ctx->request, OAUTH2_TLS_CERT_VAR_NAME,
oauth2_apache_ssl_var_lookup(r->pool, r->server, r->connection, r,
"SSL_CLIENT_CERT"));
oauth2_debug(ctx->log, "created request context: %p", ctx);
return ctx;
}
static apr_status_t oauth2_apache_request_context_free(void *rec)
{
oauth2_apache_request_ctx_t *ctx = (oauth2_apache_request_ctx_t *)rec;
if (ctx) {
oauth2_debug(ctx->log, "dispose request context: %p", ctx);
oauth2_http_request_free(ctx->log, ctx->request);
oauth2_log_free(ctx->log);
oauth2_mem_free(ctx);
}
return APR_SUCCESS;
}
oauth2_apache_request_ctx_t *
oauth2_apache_request_context(request_rec *r,
oauth2_log_function_t request_log_cb,
const char *user_data_key)
{
oauth2_apache_request_ctx_t *ctx = NULL;
apr_pool_userdata_get((void **)&ctx, user_data_key, r->pool);
if (ctx == NULL) {
ctx = oauth2_apache_request_context_init(r, request_log_cb);
apr_pool_userdata_set((const void *)ctx, user_data_key,
oauth2_apache_request_context_free,
r->pool);
}
return ctx;
}
int oauth2_apache_return_www_authenticate(oauth2_cfg_source_token_t *cfg,
oauth2_apache_request_ctx_t *ctx,
int status_code, const char *error,
const char *error_description)
{
oauth2_uint_t accept_token_in = OAUTH2_CFG_UINT_UNSET;
char *hdr = NULL;
oauth2_debug(ctx->log, "enter");
accept_token_in = oauth2_cfg_source_token_get_accept_in(cfg);
if (accept_token_in == OAUTH2_CFG_TOKEN_IN_BASIC) {
hdr = apr_psprintf(ctx->r->pool, "%s", OAUTH2_HTTP_HDR_BASIC);
} else {
hdr = apr_psprintf(ctx->r->pool, "%s", OAUTH2_HTTP_HDR_BEARER);
}
if (ap_auth_name(ctx->r) != NULL)
hdr = apr_psprintf(ctx->r->pool, "%s %s=\"%s\"", hdr,
OAUTH2_HTTP_HDR_REALM, ap_auth_name(ctx->r));
if (error != NULL)
hdr = apr_psprintf(ctx->r->pool, "%s%s %s=\"%s\"", hdr,
(ap_auth_name(ctx->r) ? "," : ""),
OAUTH2_ERROR, error);
if (error_description != NULL)
hdr = apr_psprintf(ctx->r->pool, "%s, %s=\"%s\"", hdr,
OAUTH2_ERROR_DESCRIPTION, error_description);
oauth2_apache_hdr_out_add(ctx->log, ctx->r,
OAUTH2_HTTP_HDR_WWW_AUTHENTICATE, hdr);
oauth2_debug(ctx->log, "leave");
return status_code;
}
bool oauth2_apache_request_header_set(oauth2_log_t *log, void *rec,
const char *name, const char *value)
{
request_rec *r = (request_rec *)rec;
oauth2_debug(log, "setting request header: %s=%s", name, value);
apr_table_set(r->headers_in, name, value);
return true;
}
bool oauth2_apache_http_request_set(oauth2_log_t *log,
oauth2_http_request_t *request,
request_rec *r)
{
bool rc = false;
if (request == NULL)
goto end;
oauth2_http_request_headers_loop(log, request,
oauth2_apache_request_header_set, r);
r->args =
apr_pstrdup(r->pool, oauth2_http_request_query_get(log, request));
rc = true;
end:
return rc;
}
bool oauth2_apache_response_header_set(oauth2_log_t *log, void *rec,
const char *name, const char *value)
{
request_rec *r = (request_rec *)rec;
oauth2_apache_hdr_out_add(log, r, name, value);
return true;
}
bool oauth2_apache_http_response_set(oauth2_log_t *log,
oauth2_http_response_t *response,
request_rec *r)
{
bool rc = false;
if ((response == NULL) || (r == NULL))
goto end;
oauth2_http_response_headers_loop(log, response,
oauth2_apache_response_header_set, r);
r->status = oauth2_http_response_status_code_get(log, response);
rc = true;
end:
return rc;
}
void oauth2_apache_hdr_out_add(oauth2_log_t *log, const request_rec *r,
const char *name, const char *value)
{
oauth2_debug(log, "%s: %s", name, value);
apr_table_add(r->err_headers_out, name, value);
}
void oauth2_apache_scrub_headers(oauth2_apache_request_ctx_t *ctx,
oauth2_cfg_target_pass_t *target_pass)
{
apr_hash_t *scrub_hdrs = NULL;
const char *prefix = NULL;
int prefix_len = 0;
const char *authn_hdr = NULL;
const apr_array_header_t *h = NULL;
const apr_table_entry_t *e = NULL;
const char *k = NULL;
apr_table_t *clean_headers = NULL;
bool prefix_matches = false;
bool header_matches = false;
int i = 0;
const char *hdr = NULL;
if (oauth2_cfg_target_pass_get_as_headers(target_pass) == false)
goto end;
prefix = oauth2_cfg_target_pass_get_prefix(target_pass);
scrub_hdrs = apr_hash_make(ctx->r->pool);
if (strcmp(prefix, "") == 0) {
/*
if ((cfg->white_listed_claims != NULL)
&& (apr_hash_count(cfg->white_listed_claims) >
0)) scrub_hdrs = apr_hash_overlay(r->pool,
cfg->white_listed_claims, scrub_hdrs); else oidc_warn(r, "both "
OIDCClaimPrefix " and " OIDCWhiteListedClaims " are empty: this
renders an insecure setup!");
*/
}
authn_hdr = oauth2_cfg_target_pass_get_authn_header(target_pass);
if (authn_hdr != NULL)
apr_hash_set(scrub_hdrs, authn_hdr, APR_HASH_KEY_STRING,
authn_hdr);
prefix_len = prefix ? strlen(prefix) : 0;
h = apr_table_elts(ctx->r->headers_in);
clean_headers = apr_table_make(ctx->r->pool, h->nelts);
e = (const apr_table_entry_t *)h->elts;
for (i = 0; i < h->nelts; i++) {
k = e[i].key;
hdr = (k != NULL) && (scrub_hdrs != NULL)
? apr_hash_get(scrub_hdrs, k, APR_HASH_KEY_STRING)
: NULL;
header_matches =
(hdr != NULL) && (oauth2_strnenvcmp(k, hdr, -1) == 0);
prefix_matches =
(k != NULL) && prefix_len &&
(oauth2_strnenvcmp(k, prefix, prefix_len) == 0);
if (prefix_matches || header_matches) {
oauth2_warn(
ctx->log,
"cleaned suspicious request header (%s: %.32s)", k,
e[i].val);
continue;
}
apr_table_addn(clean_headers, k, e[i].val);
}
ctx->r->headers_in = clean_headers;
end:
return;
}
static const char *oauth2_apache_get_envvar(oauth2_log_t *log, request_rec *r,
const char *name)
{
oauth2_debug(log, "get environment variable: %s", name);
return apr_table_get(r->subprocess_env, name);
}
static void oauth2_apache_set_envvar(oauth2_log_t *log, request_rec *r,
const char *name, const char *value)
{
oauth2_debug(log, "set environment variable: %s=%s", name, value);
if (value)
apr_table_set(r->subprocess_env, name, value);
else
apr_table_unset(r->subprocess_env, name);
/*
#define OAUTH2_APACHE2_USERDATA_ENV_KEY
"oauth2_apache_userdata_env_key"
*
// TODO: pull and set in fixup handler
apr_table_t *env = NULL;
apr_pool_userdata_get((void **) &env, OAUTH2_APACHE2_USERDATA_ENV_KEY,
r->pool); if (env == NULL) env = apr_table_make(r->pool, 5);
apr_table_set(env, name, value);
apr_pool_userdata_set(env, OAUTH2_APACHE2_USERDATA_ENV_KEY, NULL,
r->pool);
*/
}
#define OAUTH2_MAX_POST_DATA_LEN 1024 * 1024
static bool oauth2_apache_post_read(oauth2_log_t *log, request_rec *r,
char **rbuf)
{
bool rc = false;
apr_size_t bytes_read;
apr_size_t bytes_left;
apr_size_t len;
long read_length;
if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK) != OK)
goto end;
len = ap_should_client_block(r) ? r->remaining : 0;
if (len > OAUTH2_MAX_POST_DATA_LEN) {
oauth2_error(
log,
"POST parameter value is too large: %lu bytes (max=%d)",
(unsigned long)len, OAUTH2_MAX_POST_DATA_LEN);
goto end;
}
*rbuf = oauth2_mem_alloc(len + 1);
if (*rbuf == NULL) {
oauth2_error(
log,
"could not allocate memory for %lu bytes of POST data.",
(unsigned long)len);
goto end;
}
(*rbuf)[len] = '\0';
bytes_read = 0;
bytes_left = len;
while (bytes_left > 0) {
read_length =
ap_get_client_block(r, &(*rbuf)[bytes_read], bytes_left);
if (read_length == 0) {
(*rbuf)[bytes_read] = '\0';
break;
} else if (read_length < 0) {
oauth2_error(log,
"failed to read POST data from client");
goto end;
}
bytes_read += read_length;
bytes_left -= read_length;
}
rc = true;
end:
if ((rc == false) && (*rbuf)) {
oauth2_mem_free(*rbuf);
*rbuf = NULL;
}
return rc;
}
static void oauth2_apache_set_target_info(oauth2_apache_request_ctx_t *ctx,
oauth2_cfg_target_pass_t *target_pass,
const char *key, const char *value)
{
char *norm = NULL, *name = NULL;
norm = oauth2_normalize_header_name(key);
if (norm == NULL)
goto end;
name = oauth2_stradd(
NULL, oauth2_cfg_target_pass_get_prefix(target_pass), norm, NULL);
if (name == NULL)
goto end;
if (oauth2_cfg_target_pass_get_as_headers(target_pass))
oauth2_apache_request_header_set(ctx->log, ctx->r, name, value);
if (oauth2_cfg_target_pass_get_as_envvars(target_pass))
oauth2_apache_set_envvar(ctx->log, ctx->r, name, value);
end:
if (norm)
oauth2_mem_free(norm);
if (name)
oauth2_mem_free(name);
}
static void
oauth2_apache_set_target_infos(oauth2_apache_request_ctx_t *ctx,
oauth2_cfg_target_pass_t *target_pass,
json_t *json_token)
{
void *iter = NULL;
const char *key = NULL;
json_t *value = NULL;
char *v = NULL;
iter = json_object_iter(json_token);
while (iter) {
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
if (json_is_string(value)) {
v = oauth2_strdup(json_string_value(value));
} else {
v = oauth2_json_encode(ctx->log, value,
JSON_ENCODE_ANY);
}
oauth2_apache_set_target_info(ctx, target_pass, key, v);
if (v)
oauth2_mem_free(v);
iter = json_object_iter_next(json_token, iter);
}
}
void oauth2_apache_target_pass(oauth2_apache_request_ctx_t *ctx,
oauth2_cfg_target_pass_t *target_pass,
const char *target_token, json_t *json_token)
{
const char *authn_hdr = NULL;
authn_hdr = oauth2_cfg_target_pass_get_authn_header(target_pass);
if ((ctx->r->user != NULL) && (authn_hdr != NULL))
oauth2_apache_request_header_set(ctx->log, ctx->r, authn_hdr,
ctx->r->user);
oauth2_apache_set_target_infos(ctx, target_pass, json_token);
if (target_token != NULL) {
// TODO: hmmm, "access_token" or "target_token" or "token" or
// configurable...?
oauth2_apache_set_target_info(ctx, target_pass, "access_token",
target_token);
}
// TODO: strip cookies according to config setting
// oauth2_http:strip_cookies(r);
}
bool oauth2_apache_set_request_user(oauth2_cfg_target_pass_t *target_pass,
oauth2_apache_request_ctx_t *ctx,
json_t *json_token)
{
bool rc = false;
const char *claim = NULL;
json_t *remote_user = NULL;
if ((target_pass == NULL) || (json_token == NULL))
goto end;
claim = oauth2_cfg_target_get_remote_user_claim(target_pass);
if (claim == NULL) {
oauth2_error(ctx->log, "remote user claim was not set");
goto end;
}
remote_user = json_object_get(json_token, claim);
if ((remote_user == NULL) || (!json_is_string(remote_user))) {
oauth2_error(ctx->log,
"remote user claim \"%s\" could not be found",
claim);
goto end;
}
ctx->r->user =
apr_pstrdup(ctx->r->pool, json_string_value(remote_user));
oauth2_debug(ctx->log, "set user to \"%s\" based on claim: %s=%s",
ctx->r->user, claim, json_string_value(remote_user));
// TODO: more flexibility and or regular expressions?
rc = true;
end:
return rc;
}
static bool _oauth2_apache_env_get_cb(oauth2_log_t *log, void *ctx,
const char *name, char **value)
{
*value = oauth2_strdup(
oauth2_apache_get_envvar(log, (request_rec *)ctx, name));
return true;
}
static bool _oauth2_apache_env_set_cb(oauth2_log_t *log, void *ctx,
const char *name, const char *value)
{
oauth2_apache_set_envvar(log, (request_rec *)ctx, name, value);
return true;
}
static bool _oauth2_apache_read_form_post(oauth2_log_t *log, void *ctx,
oauth2_nv_list_t **params)
{
bool rc = false;
char *data = NULL;
if (oauth2_apache_post_read(log, (request_rec *)ctx, &data) == false)
goto end;
if (oauth2_parse_form_encoded_params(log, data, params) == false)
goto end;
rc = true;
end:
if (data)
oauth2_mem_free(data);
return rc;
}
#define OAUTH2_APACHE_USERDATA_KEY "oauth2_apache_userdata"
static apr_table_t *
oauth2_apache_request_state(oauth2_apache_request_ctx_t *ctx)
{
request_rec *r = (ctx->r->main != NULL) ? ctx->r->main : ctx->r;
apr_table_t *state = NULL;
apr_pool_userdata_get((void **)&state, OAUTH2_APACHE_USERDATA_KEY,
r->pool);
if (state == NULL) {
state = apr_table_make(r->pool, 5);
apr_pool_userdata_set(state, OAUTH2_APACHE_USERDATA_KEY, NULL,
r->pool);
}
return state;
}
static void oauth2_apache_request_state_set(oauth2_apache_request_ctx_t *ctx,
const char *key, const char *value)
{
apr_table_t *state = oauth2_apache_request_state(ctx);
apr_table_setn(state, key, apr_pstrdup(ctx->r->pool, value));
}
static const char *
oauth2_apache_request_state_get(oauth2_apache_request_ctx_t *ctx,
const char *key)
{
apr_table_t *state = oauth2_apache_request_state(ctx);
return apr_table_get(state, key);
}
void oauth2_apache_request_state_set_json(oauth2_apache_request_ctx_t *ctx,
const char *key, json_t *claims)
{
char *s = oauth2_json_encode(ctx->log, claims, 0);
oauth2_apache_request_state_set(ctx, key, s);
oauth2_mem_free(s);
}
void oauth2_apache_request_state_get_json(oauth2_apache_request_ctx_t *ctx,
const char *key, json_t **claims)
{
const char *s_claims = oauth2_apache_request_state_get(ctx, key);
if (s_claims != NULL)
oauth2_json_decode_object(ctx->log, s_claims, claims);
}
static bool oauth2_apache_authz_match_value(oauth2_apache_request_ctx_t *ctx,
const char *spec_c, json_t *val,
const char *key)
{
int i = 0;
oauth2_debug(ctx->log, "matching: spec_c=%s, key=%s", spec_c, key);
/* see if it is a string and it (case-insensitively) matches the
* Require'd value */
if (json_is_string(val)) {
if (apr_strnatcmp(json_string_value(val), spec_c) == 0)
return true;
/* see if it is a integer and it equals the Require'd value */
} else if (json_is_integer(val)) {
if (json_integer_value(val) == atoi(spec_c))
return true;
/* see if it is a boolean and it (case-insensitively) matches
* the Require'd value */
} else if (json_is_boolean(val)) {
if (apr_strnatcmp(json_is_true(val) ? "true" : "false",
spec_c) == 0)
return true;
/* if it is an array, we'll walk it */
} else if (json_is_array(val)) {
/* compare the claim values */
for (i = 0; i < json_array_size(val); i++) {
json_t *elem = json_array_get(val, i);
if (json_is_string(elem)) {
/*
* approximately compare the claim value
* (ignoring whitespace). At this point, spec_c
* points to the NULL-terminated value pattern.
*/
if (apr_strnatcmp(json_string_value(elem),
spec_c) == 0)
return true;
} else if (json_is_boolean(elem)) {
if (apr_strnatcmp(json_is_true(elem) ? "true"
: "false",
spec_c) == 0)
return true;
} else if (json_is_integer(elem)) {
if (json_integer_value(elem) == atoi(spec_c))
return true;
} else {
oauth2_warn(ctx->log,
"unhandled in-array JSON object "
"type [%d] for key \"%s\"",
elem->type, (const char *)key);
}
}
} else {
oauth2_warn(ctx->log,
"unhandled JSON object type [%d] for key \"%s\"",
val->type, (const char *)key);
}
return false;
}
bool oauth2_apache_authz_match_claim(oauth2_apache_request_ctx_t *ctx,
const char *const attr_spec,
const json_t *const claims)
{
const char *key;
json_t *val;
if (claims == NULL)
return false;
/* loop over all of the user claims */
void *iter = json_object_iter((json_t *)claims);
while (iter) {
key = json_object_iter_key(iter);
val = json_object_iter_value(iter);
oauth2_debug(ctx->log, "evaluating key \"%s\"",
(const char *)key);
const char *attr_c = (const char *)key;
const char *spec_c = attr_spec;
/* walk both strings until we get to the end of either or we
* find a differing character */
while ((*attr_c) && (*spec_c) && (*attr_c) == (*spec_c)) {
attr_c++;
spec_c++;
}
/* The match is a success if we walked the whole claim name and
* the attr_spec is at a colon. */
if (!(*attr_c) && (*spec_c) == ':') {
/* skip the colon */
spec_c++;
if (oauth2_apache_authz_match_value(ctx, spec_c, val,
key) == true)
return true;
/* a tilde denotes a string PCRE match */
// } else if (!(*attr_c) &&
//(*spec_c)
//== '~') {
//
// /* skip the tilde */
// spec_c++;
//
// if
//(oauth2_authz_match_expression(r, spec_c, val) ==
// TRUE) return
// true;
/* dot means child nodes must be evaluated */
} else if (!(*attr_c) && (*spec_c) == '.') {
/* skip the dot */
spec_c++;
if (json_is_object(val)) {
oauth2_debug(
ctx->log,
"attribute chunk matched, evaluating "
"children of key: \"%s\".",
key);
return oauth2_apache_authz_match_claim(
ctx, spec_c, json_object_get(claims, key));
} else if (json_is_array(val)) {
oauth2_debug(
ctx->log,
"attribute chunk matched, evaluating array "
"values of key: \"%s\".",
key);
return oauth2_apache_authz_match_value(
ctx, spec_c, json_object_get(claims, key),
key);
} else {
oauth2_debug(
ctx->log,
"\"%s\" matched, and child nodes or array "
"values should be evaluated, but value is "
"not an object or array.",
key);
return false;
}
}
iter = json_object_iter_next((json_t *)claims, iter);
}
return false;
}
authz_status
oauth2_apache_authorize(oauth2_apache_request_ctx_t *ctx,
const json_t *const claims, const char *require_args,
oauth2_apache_authz_match_claim_fn_type match_claim_fn)
{
int count_oauth_claims = 0;
const char *t, *w;
if (ctx->r->user == NULL)
return AUTHZ_DENIED_NO_USER;
/* if no claims, impossible to satisfy */
if (!claims)
return AUTHZ_DENIED;
t = require_args;
while ((w = ap_getword_conf(ctx->r->pool, &t)) && w[0]) {
count_oauth_claims++;
oauth2_debug(ctx->log,
"evaluating claim/expr specification: %s", w);
if (match_claim_fn(ctx, w, claims) == TRUE) {
oauth2_debug(ctx->log,
"require claim/expr '%s' matched", w);
return AUTHZ_GRANTED;
}
}
if (count_oauth_claims == 0) {
oauth2_warn(ctx->log,
"'require claim/expr' missing specification(s) in "
"configuration, denying");
}
return AUTHZ_DENIED;
}
// clang-format off
oauth2_cfg_server_callback_funcs_t oauth2_apache_server_callback_funcs = {
_oauth2_apache_env_get_cb,
_oauth2_apache_env_set_cb,
_oauth2_apache_read_form_post
};
// clang-format on
liboauth2-1.4.4/src/server/nginx.c 0000664 0000000 0000000 00000020132 14161030125 0016764 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/nginx.h"
#include
#include
#include
#include
// yep, this is tightly aligned with the (sequence of...) the log levels in
// lmo/log.h, but is faaast
// clang-format off
/*
static int log_level_nginx2oauth[] = {
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_ERROR,
OAUTH2_LOG_WARN,
OAUTH2_LOG_NOTICE,
OAUTH2_LOG_INFO,
OAUTH2_LOG_DEBUG
};
*/
// TODO: TRACE2 to STDERR??
static int log_level_log2nginx[] = {
NGX_LOG_ERR,
NGX_LOG_WARN,
NGX_LOG_NOTICE,
NGX_LOG_INFO,
NGX_LOG_DEBUG,
NGX_LOG_DEBUG,
NGX_LOG_STDERR
};
// clang-format on
void oauth2_nginx_log(oauth2_log_sink_t *sink, const char *filename,
unsigned long line, const char *function,
oauth2_log_level_t level, const char *msg)
{
// TODO: ngx_err_t?
ngx_log_error_core(log_level_log2nginx[level],
(ngx_log_t *)oauth2_log_sink_ctx_get(sink), 0,
"# %s: %s", function, msg);
}
#define _OAUTH2_NGINX_STRING_COPY(ctx, r_member, set_func) \
char *v = (ctx->r->r_member.len > 0) \
? oauth2_strndup((const char *)ctx->r->r_member.data, \
ctx->r->r_member.len) \
: NULL; \
oauth2_http_request_##set_func##_set(ctx->log, ctx->request, v); \
oauth2_mem_free(v);
#define _OAUTH2_NGINX_START_END_COPY(ctx, r_member_start, r_member_end, \
set_func) \
int len = ctx->r->r_member_end - ctx->r->r_member_start; \
char *v = \
(len > 0) \
? oauth2_strndup((const char *)ctx->r->r_member_start, len) \
: NULL; \
oauth2_http_request_##set_func##_set(ctx->log, ctx->request, v); \
oauth2_mem_free(v);
static void _oauth2_nginx_schema_copy(oauth2_nginx_request_context_t *ctx)
{
_OAUTH2_NGINX_START_END_COPY(ctx, schema_start, schema_end, scheme);
}
static void _oauth2_nginx_host_copy(oauth2_nginx_request_context_t *ctx)
{
_OAUTH2_NGINX_START_END_COPY(ctx, host_start, host_end, hostname);
}
static void _oauth2_nginx_port_copy(oauth2_nginx_request_context_t *ctx)
{
char *v = NULL;
int len = ctx->r->port_end - ctx->r->port_start;
if (len > 0) {
v = oauth2_strndup((const char *)ctx->r->port_start, len);
oauth2_http_request_port_set(ctx->log, ctx->request,
oauth2_parse_uint(NULL, v, 0));
oauth2_mem_free(v);
}
}
static void _oauth2_nginx_path_copy(oauth2_nginx_request_context_t *ctx)
{
_OAUTH2_NGINX_STRING_COPY(ctx, uri, path);
}
static void _oauth2_nginx_method_copy(oauth2_nginx_request_context_t *ctx)
{
oauth2_http_method_t m = OAUTH2_HTTP_METHOD_UNKNOWN;
char *v = (ctx->r->method_name.len > 0)
? oauth2_strndup((const char *)ctx->r->method_name.data,
ctx->r->method_name.len)
: NULL;
if (v == NULL)
goto end;
if (strcmp(v, "GET") == 0)
m = OAUTH2_HTTP_METHOD_GET;
else if (strcmp(v, "POST") == 0)
m = OAUTH2_HTTP_METHOD_POST;
else if (strcmp(v, "PUT") == 0)
m = OAUTH2_HTTP_METHOD_PUT;
else if (strcmp(v, "DELETE") == 0)
m = OAUTH2_HTTP_METHOD_DELETE;
else if (strcmp(v, "CONNECT") == 0)
m = OAUTH2_HTTP_METHOD_CONNECT;
else if (strcmp(v, "OPTIONS") == 0)
m = OAUTH2_HTTP_METHOD_OPTIONS;
oauth2_http_request_method_set(ctx->log, ctx->request, m);
end:
if (v)
oauth2_mem_free(v);
}
static void _oauth2_nginx_query_copy(oauth2_nginx_request_context_t *ctx)
{
_OAUTH2_NGINX_STRING_COPY(ctx, args, query);
}
static void _oauth2_nginx_headers_copy(oauth2_nginx_request_context_t *ctx)
{
char *name = NULL, *value = NULL;
ngx_list_part_t *part;
ngx_table_elt_t *h;
ngx_uint_t i;
part = &ctx->r->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
name =
oauth2_strndup((const char *)h[i].key.data, h[i].key.len);
value = oauth2_strndup((const char *)h[i].value.data,
h[i].value.len);
// TODO: avoid duplicate copy
oauth2_http_request_header_add(ctx->log, ctx->request, name,
value);
oauth2_mem_free(name);
oauth2_mem_free(value);
}
}
void _oauth2_nginx_request_copy(oauth2_nginx_request_context_t *ctx)
{
if ((ctx == NULL) || (ctx->r == NULL))
goto end;
_oauth2_nginx_schema_copy(ctx);
_oauth2_nginx_host_copy(ctx);
_oauth2_nginx_port_copy(ctx);
_oauth2_nginx_path_copy(ctx);
_oauth2_nginx_query_copy(ctx);
_oauth2_nginx_method_copy(ctx);
_oauth2_nginx_headers_copy(ctx);
end:
return;
}
oauth2_nginx_request_context_t *
oauth2_nginx_request_context_init(ngx_http_request_t *r)
{
// ngx_http_core_srv_conf_t *cscf;
oauth2_nginx_request_context_t *ctx = NULL;
oauth2_log_sink_t *log_sink_nginx = NULL;
// if (r == NULL)
// goto end;
// cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
// TODO: memory allocation failure checks...?
ctx = oauth2_mem_alloc(sizeof(oauth2_nginx_request_context_t));
// TODO: get the log level from NGINX...
oauth2_log_level_t level = OAUTH2_LOG_TRACE1;
log_sink_nginx =
oauth2_log_sink_create(level, oauth2_nginx_log, r->connection->log);
ctx->log = oauth2_log_init(level, log_sink_nginx);
ctx->request = oauth2_http_request_init(ctx->log);
ctx->r = r;
_oauth2_nginx_request_copy(ctx);
oauth2_debug(ctx->log, "created NGINX request context: %p", ctx);
// end:
return ctx;
}
void oauth2_nginx_request_context_free(void *rec)
{
oauth2_nginx_request_context_t *ctx =
(oauth2_nginx_request_context_t *)rec;
if (ctx) {
oauth2_debug(ctx->log, "dispose NGINX request context: %p",
ctx);
if (ctx->request)
oauth2_http_request_free(ctx->log, ctx->request);
oauth2_log_free(ctx->log);
oauth2_mem_free(ctx);
}
}
static bool oauth2_nginx_response_header_set(oauth2_log_t *log, void *rec,
const char *name,
const char *value)
{
bool rc = false;
ngx_table_elt_t *h = NULL;
ngx_http_request_t *r = (ngx_http_request_t *)rec;
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL)
goto end;
h->hash = 1;
h->key.len = strlen(name);
h->key.data = ngx_palloc(r->pool, h->key.len);
memcpy(h->key.data, name, h->key.len);
h->value.len = strlen(value);
h->value.data = ngx_palloc(r->pool, h->value.len);
memcpy(h->value.data, value, h->value.len);
rc = true;
end:
return rc;
}
ngx_int_t oauth2_nginx_http_response_set(oauth2_log_t *log,
oauth2_http_response_t *response,
ngx_http_request_t *r)
{
ngx_int_t nrc = NGX_ERROR;
if ((response == NULL) || (r == NULL))
goto end;
oauth2_http_response_headers_loop(log, response,
oauth2_nginx_response_header_set, r);
r->headers_out.status =
oauth2_http_response_status_code_get(log, response);
if (r->headers_out.status == 200)
nrc = NGX_OK;
else if (r->headers_out.status == 302)
nrc = NGX_HTTP_MOVED_TEMPORARILY;
else if (r->headers_out.status == 401)
nrc = NGX_HTTP_UNAUTHORIZED;
else
nrc = r->headers_out.status;
// nrc = ngx_http_send_header(r);
end:
return nrc;
}
liboauth2-1.4.4/src/session.c 0000664 0000000 0000000 00000031560 14161030125 0016025 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/session.h"
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "cfg_int.h"
#include "util_int.h"
typedef struct oauth2_session_rec_t {
char *id;
oauth2_time_t start;
oauth2_time_t expiry;
char *user;
char *id_token;
json_t *id_token_claims;
json_t *userinfo_claims;
} oauth2_session_rec_t;
oauth2_session_rec_t *oauth2_session_rec_init(oauth2_log_t *log)
{
oauth2_session_rec_t *s = (oauth2_session_rec_t *)oauth2_mem_alloc(
sizeof(oauth2_session_rec_t));
s->id = NULL;
s->user = NULL;
s->id_token = NULL;
s->id_token_claims = NULL;
s->userinfo_claims = NULL;
s->expiry = 0;
s->start = oauth2_time_now_sec();
return s;
}
void oauth2_session_rec_free(oauth2_log_t *log, oauth2_session_rec_t *s)
{
if (s->user)
oauth2_mem_free(s->user);
if (s->id_token)
oauth2_mem_free(s->id_token);
if (s->id_token_claims)
json_decref(s->id_token_claims);
if (s->userinfo_claims)
json_decref(s->userinfo_claims);
if (s->id)
oauth2_mem_free(s->id);
if (s)
oauth2_mem_free(s);
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(session, rec, id, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(session, rec, user, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(session, rec, id_token, char *, str)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(session, rec, start, oauth2_time_t, time)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(session, rec, expiry, oauth2_time_t, time)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_GET(session, rec, id_token_claims, json_t *)
_OAUTH2_TYPE_IMPLEMENT_MEMBER_GET(session, rec, userinfo_claims, json_t *)
static bool _oauth2_session_rec_json_object_set(oauth2_log_t *log,
oauth2_session_rec_t *session,
const char *name, json_t *json,
json_t **session_ptr)
{
bool rc = false;
char *s_json = NULL;
if (json == NULL) {
rc = true;
goto end;
}
s_json = oauth2_json_encode(log, json, 0);
oauth2_debug(log, "%s=%s", name, s_json);
*session_ptr = json_incref(json);
rc = true;
end:
if (s_json)
oauth2_mem_free(s_json);
return rc;
}
bool oauth2_session_rec_id_token_claims_set(oauth2_log_t *log,
oauth2_session_rec_t *session,
json_t *id_token)
{
return _oauth2_session_rec_json_object_set(
log, session, "id_token", id_token, &session->id_token_claims);
}
bool oauth2_session_rec_userinfo_claims_set(oauth2_log_t *log,
oauth2_session_rec_t *session,
json_t *userinfo_claims)
{
return _oauth2_session_rec_json_object_set(log, session, "userinfo",
userinfo_claims,
&session->userinfo_claims);
}
#define OAUTH_SESSION_KEY_ID "id"
#define OAUTH_SESSION_KEY_USER "u"
#define OAUTH_SESSION_KEY_ID_TOKEN "i"
#define OAUTH_SESSION_KEY_ID_TOKEN_CLAIMS "ic"
#define OAUTH_SESSION_KEY_USERINFO_CLAIMS "uc"
#define OAUTH_SESSION_KEY_START "s"
#define OAUTH_SESSION_KEY_EXPIRY "e"
bool oauth2_session_load_cookie(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
oauth2_http_request_t *request, json_t **json)
{
bool rc = false;
const char *name = NULL;
char *value = NULL;
name = oauth2_cfg_session_cookie_name_get(log, cfg);
value = oauth2_http_request_cookie_get(log, request, name, true);
if (value == NULL) {
oauth2_debug(log, "no session cookie found");
rc = true;
goto end;
}
rc = oauth2_jose_jwt_decrypt(log, oauth2_crypto_passphrase_get(log),
value, json);
end:
if (value)
oauth2_mem_free(value);
return rc;
}
bool oauth2_session_save_cookie(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response, json_t *json)
{
bool rc = false;
const char *name = NULL, *path = NULL;
;
char *value = NULL;
if (oauth2_jose_jwt_encrypt(log, oauth2_crypto_passphrase_get(log),
json, &value) == false)
goto end;
name = oauth2_cfg_session_cookie_name_get(log, cfg);
path = oauth2_cfg_session_cookie_path_get(log, cfg);
rc = oauth2_http_response_cookie_set(
log, response, name, value, path,
oauth2_http_request_is_secure(log, request), OAUTH2_CFG_TIME_UNSET);
end:
if (value)
oauth2_mem_free(value);
return rc;
}
bool oauth2_session_load_cache(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
oauth2_http_request_t *request, json_t **json)
{
bool rc = false;
const char *name = NULL;
char *key = NULL, *value = NULL;
name = oauth2_cfg_session_cookie_name_get(log, cfg);
key = oauth2_http_request_cookie_get(log, request, name, true);
if (key == NULL) {
oauth2_debug(log, "no session cookie found");
rc = true;
goto end;
}
if (oauth2_cache_get(log, cfg->cache, key, &value) == false)
goto end;
if (value == NULL) {
oauth2_debug(log, "no session found in cache");
rc = true;
goto end;
}
if (oauth2_json_decode_object(log, value, json) == false)
goto end;
oauth2_debug(log, "restored session from cache: %s", value);
rc = true;
end:
if (key)
oauth2_mem_free(key);
if (value)
oauth2_mem_free(value);
return rc;
}
bool oauth2_session_save_cache(oauth2_log_t *log,
const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response, json_t *json)
{
bool rc = false;
const char *name = NULL, *path = NULL;
char *key = NULL, *value = NULL;
value = oauth2_json_encode(log, json, 0);
if (value == NULL)
goto end;
if (oauth2_json_string_get(log, json, OAUTH_SESSION_KEY_ID, &key,
NULL) == false) {
oauth2_error(log, "no session identifier found in session");
goto end;
}
if (oauth2_cache_set(log, cfg->cache, key, value,
oauth2_cfg_session_inactivity_timeout_s_get(
log, cfg)) == false) {
oauth2_error(log, "could not store session in cache");
goto end;
}
name = oauth2_cfg_session_cookie_name_get(log, cfg);
path = oauth2_cfg_session_cookie_path_get(log, cfg);
rc = oauth2_http_response_cookie_set(
log, response, name, key, path,
oauth2_http_request_is_secure(log, request), OAUTH2_CFG_TIME_UNSET);
end:
if (key)
oauth2_mem_free(key);
if (value)
oauth2_mem_free(value);
return rc;
}
#define OAUTH2_SESSION_ID_LENGTH 16
static char *oauth2_session_id_generate(oauth2_log_t *log)
{
return oauth2_rand_str(log, OAUTH2_SESSION_ID_LENGTH);
}
bool oauth2_session_load(oauth2_log_t *log, const oauth2_cfg_session_t *cfg,
oauth2_http_request_t *request,
oauth2_session_rec_t **session)
{
bool rc = false;
json_t *json = NULL, *json_ptr = NULL;
oauth2_session_load_callback_t *session_load_callback = NULL;
json_int_t expiry = 0, start = 0;
oauth2_time_t now = 0;
oauth2_debug(log, "enter");
if (session == NULL)
goto end;
*session = oauth2_session_rec_init(log);
if (*session == NULL)
goto end;
session_load_callback = oauth2_cfg_session_load_callback_get(log, cfg);
if (session_load_callback == NULL)
goto end;
rc = session_load_callback(log, cfg, request, &json);
if ((rc == false) || (json == NULL)) {
if ((rc) && ((*session)->id == NULL))
(*session)->id = oauth2_session_id_generate(log);
goto end;
}
now = oauth2_time_now_sec();
if (oauth2_json_number_get(log, json, OAUTH_SESSION_KEY_START, &start,
0) == false)
goto end;
if (now >= start + oauth2_cfg_session_max_duration_s_get(log, cfg)) {
oauth2_warn(log,
"session has exceeded maximum duration; "
"start=" OAUTH2_TIME_T_FORMAT
" expiry=" OAUTH2_TIME_T_FORMAT
" now=" OAUTH2_TIME_T_FORMAT "",
start,
oauth2_cfg_session_max_duration_s_get(log, cfg),
now);
rc = false;
goto end;
}
(*session)->start = start;
if (oauth2_json_number_get(log, json, OAUTH_SESSION_KEY_EXPIRY, &expiry,
0) == false)
goto end;
if (now >= expiry) {
oauth2_warn(log, "session has expired");
// TODO: refactor and/or remove from cache?
oauth2_session_rec_free(log, *session);
*session = oauth2_session_rec_init(log);
(*session)->id = oauth2_session_id_generate(log);
rc = true;
goto end;
}
(*session)->expiry = expiry;
if (oauth2_json_string_get(log, json, OAUTH_SESSION_KEY_ID,
&(*session)->id, NULL) == false)
goto end;
if (oauth2_json_string_get(log, json, OAUTH_SESSION_KEY_USER,
&(*session)->user, NULL) == false)
goto end;
if (oauth2_json_string_get(log, json, OAUTH_SESSION_KEY_ID_TOKEN,
&(*session)->id_token, NULL) == false)
goto end;
if (oauth2_json_object_get(log, json, OAUTH_SESSION_KEY_ID_TOKEN_CLAIMS,
&json_ptr) == false)
goto end;
oauth2_session_rec_id_token_claims_set(log, *session, json_ptr);
if (json_ptr)
json_decref(json_ptr);
if (oauth2_json_object_get(log, json, OAUTH_SESSION_KEY_USERINFO_CLAIMS,
&json_ptr) == false)
goto end;
oauth2_session_rec_userinfo_claims_set(log, *session, json_ptr);
if (json_ptr)
json_decref(json_ptr);
end:
if (json)
json_decref(json);
oauth2_debug(log, "return: %d", rc);
return rc;
}
bool oauth2_session_handle(oauth2_log_t *log, const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response,
oauth2_session_rec_t *session)
{
bool rc = false;
bool needs_save = false;
/*
* reset the session inactivity timer
* but only do this once per 10% of the inactivity timeout interval
* (with a max to 60 seconds) for performance reasons
*
* now there's a small chance that the session ends 10% (or a minute)
* earlier than configured/expected cq. when there's a request after a
* recent save (so no update) and then no activity happens until a
* request comes in just before the session should expire
* ("recent" and "just before" refer to 10%-with-a-max-of-60-seconds of
* the inactivity interval after the start/last-update and before the
* expiry of the session respectively)
*
* this is be deemed acceptable here because of performance gain
*/
oauth2_time_t interval =
oauth2_cfg_session_inactivity_timeout_s_get(log, cfg);
oauth2_time_t now = oauth2_time_now_sec();
oauth2_time_t slack = interval / 10;
if (slack > 60)
slack = 60;
if (session->expiry - now < interval - slack) {
// session->expiry = now + interval;
needs_save = true;
}
oauth2_debug(log,
"session inactivity timeout: " OAUTH2_TIME_T_FORMAT
", interval: " OAUTH2_TIME_T_FORMAT "",
session->expiry - now, interval);
if (needs_save)
rc = oauth2_session_save(log, cfg, request, response, session);
else
rc = true;
return rc;
}
bool oauth2_session_save(oauth2_log_t *log, const oauth2_cfg_session_t *cfg,
const oauth2_http_request_t *request,
oauth2_http_response_t *response,
oauth2_session_rec_t *session)
{
bool rc = false;
json_t *json = NULL;
oauth2_session_save_callback_t *session_save_callback = NULL;
if (session == NULL)
goto end;
json = json_object();
if (json == NULL)
goto end;
if (session->start > 0)
json_object_set_new(json, OAUTH_SESSION_KEY_START,
json_integer(session->start));
if (session->expiry == 0) {
oauth2_debug(
log,
"setting expiry according to "
"cfg->inactivity_timeout_s=" OAUTH2_TIME_T_FORMAT "",
oauth2_cfg_session_inactivity_timeout_s_get(log, cfg));
session->expiry =
oauth2_time_now_sec() +
oauth2_cfg_session_inactivity_timeout_s_get(log, cfg);
}
if (session->expiry > 0)
json_object_set_new(json, OAUTH_SESSION_KEY_EXPIRY,
json_integer(session->expiry));
if (session->id)
json_object_set_new(json, OAUTH_SESSION_KEY_ID,
json_string(session->id));
if (session->user)
json_object_set_new(json, OAUTH_SESSION_KEY_USER,
json_string(session->user));
if (session->id_token)
json_object_set_new(json, OAUTH_SESSION_KEY_ID_TOKEN,
json_string(session->id_token));
if (session->id_token_claims)
json_object_set(json, OAUTH_SESSION_KEY_ID_TOKEN_CLAIMS,
session->id_token_claims);
if (session->userinfo_claims)
json_object_set(json, OAUTH_SESSION_KEY_USERINFO_CLAIMS,
session->userinfo_claims);
session_save_callback = oauth2_cfg_session_save_callback_get(log, cfg);
if (session_save_callback == NULL)
goto end;
rc = session_save_callback(log, cfg, request, response, json);
end:
if (json)
json_decref(json);
return rc;
}
liboauth2-1.4.4/src/util.c 0000664 0000000 0000000 00000071327 14161030125 0015324 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
// need this at the top for vasprintf
#include "util_int.h"
#include
#include
#include "oauth2/log.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include
#include
#include
#include
#include
#include
#include "cache_int.h"
#include "cfg_int.h"
static CURL *_s_curl = NULL;
static oauth2_uint_t _curl_refcount = 0;
oauth2_log_t *oauth2_init(oauth2_log_level_t level, oauth2_log_sink_t *sink)
{
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
// TODO: align flags/call with memory initialization in mem.c
// possibly providing alloc funcs as part of init?
curl_global_init(CURL_GLOBAL_ALL);
return oauth2_log_init(level, sink);
}
void oauth2_shutdown(oauth2_log_t *log)
{
_oauth2_session_global_cleanup(log);
_oauth2_cache_global_cleanup(log);
if (_s_curl) {
curl_easy_cleanup(_s_curl);
_s_curl = NULL;
}
curl_global_cleanup();
EVP_cleanup();
ERR_free_strings();
CRYPTO_cleanup_all_ex_data();
//#if OPENSSL_API_COMPAT < 0x10100000L
//#if OPENSSL_VERSION_NUMBER < 0x10000000L
// SSL_COMP_free_compression_methods();
//#endif
#if (OPENSSL_VERSION_NUMBER < 0x10100000) || defined(LIBRESSL_VERSION_NUMBER)
//#if OPENSSL_API_COMPAT < 0x10100000L
ERR_remove_thread_state(NULL);
#endif
oauth2_log_free(log);
}
typedef bool(oauth2_cjose_base64_encode_callback_t)(const uint8_t *input,
const size_t inlen,
char **output,
size_t *outlen,
cjose_err *err);
static size_t _oauth2_cjose_base64_encode(
oauth2_log_t *log, oauth2_cjose_base64_encode_callback_t encode,
const uint8_t *src, const size_t src_len, char **dst)
{
bool rc = false;
size_t dst_len;
cjose_err err;
memset(&err, 0, sizeof(err));
oauth2_debug(log, "enter: len=%d", (int)src_len);
if (dst)
*dst = NULL;
dst_len = 0;
if (src == NULL) {
oauth2_warn(log, "not encoding null input to empty string");
goto end;
}
rc = encode(src, src_len, dst, &dst_len, &err);
if (!rc) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "encode", err);
goto end;
}
end:
oauth2_debug(log, "leave: len=%d", (int)dst_len);
return dst_len;
}
size_t oauth2_base64url_encode(oauth2_log_t *log, const uint8_t *src,
const size_t src_len, char **dst)
{
return _oauth2_cjose_base64_encode(log, cjose_base64url_encode, src,
src_len, dst);
}
size_t oauth2_base64_encode(oauth2_log_t *log, const uint8_t *src,
const size_t src_len, char **dst)
{
return _oauth2_cjose_base64_encode(log, cjose_base64_encode, src,
src_len, dst);
}
typedef bool(oauth2_cjose_base64_decode_callback_t)(const char *input,
const size_t inlen,
uint8_t **output,
size_t *outlen,
cjose_err *err);
static bool
_oauth2_cjose_base64_decode(oauth2_log_t *log,
oauth2_cjose_base64_decode_callback_t decode,
const char *src, uint8_t **dst, size_t *dst_len)
{
bool rc = false;
cjose_err err;
size_t src_len = 0;
memset(&err, 0, sizeof(err));
src_len = src ? strlen(src) : 0;
oauth2_debug(log, "enter: len=%d", (int)src_len);
if (dst == NULL)
goto end;
*dst = NULL;
if (dst_len == NULL)
goto end;
*dst_len = 0;
if (src == NULL) {
oauth2_warn(log, "not decoding null input");
goto end;
}
if (decode(src, src_len, dst, dst_len, &err) == false) {
_OAUTH2_UTIL_JOSE_ERR_LOG(log, "decode", err);
goto end;
}
rc = true;
end:
oauth2_debug(log, "leave: len=%d", (dst_len) ? (int)*dst_len : -1);
return rc;
}
bool oauth2_base64url_decode(oauth2_log_t *log, const char *src, uint8_t **dst,
size_t *dst_len)
{
return _oauth2_cjose_base64_decode(log, cjose_base64url_decode, src,
dst, dst_len);
}
bool oauth2_base64_decode(oauth2_log_t *log, const char *src, uint8_t **dst,
size_t *dst_len)
{
return _oauth2_cjose_base64_decode(log, cjose_base64_decode, src, dst,
dst_len);
}
static int oauth2_char_to_env(int c)
{
return isalnum(c) ? toupper(c) : '_';
}
int oauth2_strnenvcmp(const char *a, const char *b, int len)
{
int rv = 0;
int d, i = 0;
while (1) {
// if len < 0 then we don't stop based on length
if (len >= 0 && i >= len)
goto end;
// if we're at the end of both strings, they're equal
if (!*a && !*b)
goto end;
// if the second string is shorter, pick it:
if (*a && !*b) {
rv = 1;
goto end;
}
// if the first string is shorter, pick it:
if (!*a && *b) {
rv = -1;
goto end;
}
// normalize the characters as for conversion to an environment
// variable.
d = oauth2_char_to_env(*a) - oauth2_char_to_env(*b);
if (d) {
rv = d;
goto end;
}
a++;
b++;
i++;
}
end:
return rv;
}
static CURL *oauth2_curl_init(oauth2_log_t *log)
{
if (_s_curl == NULL) {
_s_curl = curl_easy_init();
if (_s_curl == NULL) {
oauth2_error(log, "curl_easy_init() error");
}
}
_curl_refcount++;
return _s_curl;
}
static void oauth2_curl_free(CURL *curl)
{
_curl_refcount--;
if ((_curl_refcount == 0) && (_s_curl)) {
curl_easy_cleanup(_s_curl);
_s_curl = NULL;
}
}
char *oauth2_url_encode(oauth2_log_t *log, const char *src)
{
char *dst = NULL, *rc = NULL;
CURL *curl = NULL;
oauth2_debug(log, "enter: %s", src);
if (src == NULL) {
oauth2_warn(log, "not encoding empty string");
goto end;
}
curl = oauth2_curl_init(log);
if (curl == NULL)
goto end;
rc = curl_easy_escape(curl, src, strlen(src));
if (rc == NULL) {
oauth2_error(log, "curl_easy_escape() error");
goto end;
}
dst = oauth2_strdup(rc);
end:
if (rc)
curl_free(rc);
if (curl)
oauth2_curl_free(curl);
oauth2_debug(log, "leave: %s", dst);
return dst;
}
char *oauth2_url_decode(oauth2_log_t *log, const char *src)
{
char *dst = NULL, *rc = NULL;
char *replaced = NULL;
CURL *curl = NULL;
int i = 0;
oauth2_debug(log, "enter: %s", src);
if (src == NULL) {
oauth2_warn(log, "not decoding empty string");
goto end;
}
curl = oauth2_curl_init(log);
if (curl == NULL)
goto end;
replaced = oauth2_strdup(src);
if (replaced == NULL)
goto end;
// https://github.com/unshiftio/querystringify/issues/7#issuecomment-287627341
for (i = 0; replaced[i] != '\0'; i++)
if (replaced[i] == '+')
// NOTE: technically it would be more correct to make
// this a %20...
replaced[i] = ' ';
rc = curl_easy_unescape(curl, replaced, strlen(replaced), NULL);
if (rc == NULL) {
oauth2_error(log, "curl_easy_unescape() error");
goto end;
}
dst = oauth2_strdup(rc);
end:
if (rc)
curl_free(rc);
if (replaced)
oauth2_mem_free(replaced);
if (curl)
oauth2_curl_free(curl);
oauth2_debug(log, "leave: %s", dst);
return dst;
}
// TODO: this has performance/memory issues for large chunks of HTML
char *oauth2_html_escape(oauth2_log_t *log, const char *src)
{
char *dst = NULL, *rc = NULL;
const char escape_chars[6] = {'&', '\'', '\"', '>', '<', '\0'};
const char *const replace_chars[] = {"&", "'", """,
">", "<"};
unsigned int i, j = 0, k, n = 0,
escape_chars_len = strlen(escape_chars);
size_t m = 0, src_len = src ? strlen(src) : 0;
oauth2_debug(log, "enter: %s", src);
if (src == NULL)
goto end;
rc = oauth2_mem_alloc(src_len * 6 + 1);
for (i = 0; i < src_len; i++) {
for (n = 0; n < escape_chars_len; n++) {
if (src[i] == escape_chars[n]) {
m = strlen(replace_chars[n]);
for (k = 0; k < m; k++)
rc[j + k] = replace_chars[n][k];
j += m;
break;
}
}
if (n == escape_chars_len) {
rc[j] = src[i];
j++;
}
}
rc[j] = '\0';
dst = oauth2_strdup(rc);
end:
if (rc)
oauth2_mem_free(rc);
oauth2_debug(log, "leave: %s", dst);
return dst;
}
static char *_oauth2_trim(char *src)
{
char *rv = NULL;
char *start = NULL, *end = NULL;
char *buf = NULL;
if (src == NULL)
goto end;
buf = oauth2_strdup(src);
start = buf;
while (isspace(*start))
++start;
end = &start[strlen(start)];
while (--end >= start && isspace(*end))
*end = '\0';
rv = oauth2_strdup(start);
end:
if (buf)
oauth2_mem_free(buf);
return rv;
}
char *oauth2_getword(const char **line, char stop)
{
const char *pos = *line;
int len;
char *res;
while ((*pos != stop) && *pos) {
++pos;
}
len = pos - *line;
res = oauth2_strndup(*line, len);
if (stop) {
while (*pos == stop) {
++pos;
}
}
*line = pos;
return res;
}
bool _oauth2_nv_list_parse(oauth2_log_t *log, const char *input,
oauth2_nv_list_t *tuples, char sep_tuple,
char sep_nv, bool trim, bool url_decode)
{
bool rc = false;
const char *p = NULL;
char *save_input = NULL, *save_val = NULL;
char *key = NULL, *val = NULL;
char *dec_key = NULL, *dec_val = NULL;
char *trm_key = NULL, *trm_val = NULL;
if ((input == NULL) || (tuples == NULL))
goto end;
save_input = oauth2_strdup(input);
p = save_input;
while (p && *p && (val = oauth2_getword(&p, sep_tuple))) {
save_val = val;
key = oauth2_getword((const char **)&val, sep_nv);
if (key == NULL)
continue;
trm_key = trim ? _oauth2_trim(key) : oauth2_strdup(key);
trm_val = trim ? _oauth2_trim(val) : oauth2_strdup(val);
if (url_decode) {
dec_key = oauth2_url_decode(log, trm_key);
dec_val = oauth2_url_decode(log, trm_val);
oauth2_nv_list_add(log, tuples, dec_key, dec_val);
oauth2_mem_free(dec_key);
oauth2_mem_free(dec_val);
} else {
oauth2_nv_list_add(log, tuples, trm_key, trm_val);
}
oauth2_mem_free(trm_key);
if (trm_val)
oauth2_mem_free(trm_val);
oauth2_mem_free(key);
if (save_val)
oauth2_mem_free(save_val);
}
rc = true;
end:
if (save_input)
oauth2_mem_free(save_input);
return rc;
}
bool oauth2_parse_form_encoded_params(oauth2_log_t *log, const char *data,
oauth2_nv_list_t **params)
{
bool rc = false;
if (params == NULL)
goto end;
if (data == NULL) {
rc = true;
goto end;
}
*params = oauth2_nv_list_init(log);
if (*params == NULL)
goto end;
rc = _oauth2_nv_list_parse(log, data, *params, _OAUTH2_CHAR_AMP,
_OAUTH2_CHAR_EQUAL, false, true);
end:
return rc;
}
/*
* internal
*/
char *oauth2_strndup(const char *src, size_t len)
{
char *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_mem_alloc(len + 1);
if (dst == NULL)
goto end;
memcpy(dst, src, len);
dst[len] = '\0';
end:
return dst;
}
char *oauth2_strdup(const char *src)
{
return oauth2_strndup(src, src ? strlen(src) : 0);
}
char *_oauth2_stradd4(char *src, const char *add1, const char *add2,
const char *add3, const char *add4)
{
char *ptr = NULL;
size_t len = 0;
if (src == NULL)
src = oauth2_strdup("");
if (src == NULL)
goto end;
if (add1 == NULL)
add1 = "";
if (add2 == NULL)
add2 = "";
if (add3 == NULL)
add3 = "";
if (add4 == NULL)
add4 = "";
len = strlen(src) + strlen(add1) + strlen(add2) + strlen(add3) +
strlen(add4) + 1;
ptr = oauth2_mem_alloc(len);
if (ptr == NULL)
goto end;
oauth2_snprintf(ptr, len, "%s%s%s%s%s", src, add1, add2, add3, add4);
end:
if (src)
oauth2_mem_free(src);
return ptr;
}
char *oauth2_stradd(char *src, const char *add1, const char *add2,
const char *add3)
{
return _oauth2_stradd4(src, add1, add2, add3, NULL);
}
static bool _oauth2_nv2s(oauth2_log_t *log, void *rec, const char *key,
const char *value)
{
bool rc = false;
char **str = (char **)rec;
if (str == NULL)
goto end;
*str = _oauth2_stradd4(*str, " ", key, _OAUTH2_STR_EQUAL, value);
rc = true;
end:
return rc;
}
char *oauth2_nv_list2s(oauth2_log_t *log, const oauth2_nv_list_t *list)
{
char *str = oauth2_strdup("[");
oauth2_nv_list_loop(log, list, _oauth2_nv2s, &str);
str = oauth2_stradd(str, " ]", NULL, NULL);
return str;
}
bool _oauth2_struct_slot_str_set(void *struct_ptr, size_t offset,
const char *value)
{
bool rc = false;
char *base = NULL, **ptr = NULL;
if ((struct_ptr == NULL) || (value == NULL))
goto end;
base = (char *)struct_ptr;
ptr = (char **)(base + offset);
if (*ptr)
oauth2_mem_free(*ptr);
*ptr = oauth2_strdup(value);
rc = (*ptr != NULL);
end:
return rc;
}
#define _OAUTH2_STRUCT_MEMBER_SET(name, type) \
bool _oauth2_struct_slot_##name##_set(void *struct_ptr, size_t offset, \
const type value) \
{ \
bool rc = false; \
char *base = NULL; \
type *ptr = NULL; \
\
if (struct_ptr == NULL) \
goto end; \
\
base = (char *)struct_ptr; \
ptr = (type *)(base + offset); \
\
*ptr = value; \
\
rc = true; \
\
end: \
\
return rc; \
}
_OAUTH2_STRUCT_MEMBER_SET(bln, bool)
_OAUTH2_STRUCT_MEMBER_SET(integer, int)
_OAUTH2_STRUCT_MEMBER_SET(uint, oauth2_uint_t)
_OAUTH2_STRUCT_MEMBER_SET(time, oauth2_time_t)
bool _oauth2_struct_slot_ptr_set(void *struct_ptr, size_t offset,
const void *value)
{
bool rc = false;
char *base = NULL;
void **ptr = NULL;
if (struct_ptr == NULL)
goto end;
base = (char *)struct_ptr;
ptr = (void **)(base + offset);
// TODO: should probably make a typed definition for nv_list to free
// here if already set...
*ptr = (void *)value;
rc = true;
end:
return rc;
}
int oauth2_snprintf(char *dst, size_t len, const char *fmt, ...)
{
va_list ap;
int rc = 0;
if ((dst == NULL) || (fmt == NULL))
goto end;
va_start(ap, fmt);
rc = vsnprintf(dst, len, fmt, ap);
va_end(ap);
end:
return rc;
}
/*
* name value pairs/list stuff
*/
typedef struct _oauth2_nv_t {
char *name;
char *value;
struct _oauth2_nv_t *next;
} _oauth2_nv_t;
static _oauth2_nv_t *_oauth2_nv_new(oauth2_log_t *log, const char *name,
const char *value)
{
_oauth2_nv_t *ptr = NULL;
ptr = oauth2_mem_alloc(sizeof(_oauth2_nv_t));
if (ptr != NULL) {
ptr->name = name ? oauth2_strdup(name) : NULL;
ptr->value = value ? oauth2_strdup(value) : NULL;
ptr->next = NULL;
}
return ptr;
}
static void _oauth2_nv_free(oauth2_log_t *log, _oauth2_nv_t *ptr)
{
if (ptr->name)
oauth2_mem_free(ptr->name);
if (ptr->value)
oauth2_mem_free(ptr->value);
oauth2_mem_free(ptr);
}
typedef struct oauth2_nv_list_t {
_oauth2_nv_t *first;
bool case_sensitive;
} oauth2_nv_list_t;
oauth2_nv_list_t *oauth2_nv_list_init(oauth2_log_t *log)
{
oauth2_nv_list_t *ptr = NULL;
ptr = oauth2_mem_alloc(sizeof(oauth2_nv_list_t));
if (ptr != NULL) {
ptr->case_sensitive = true;
}
return ptr;
}
void oauth2_nv_list_free(oauth2_log_t *log, oauth2_nv_list_t *list)
{
_oauth2_nv_t *ptr = NULL;
if (list == NULL)
goto end;
while ((ptr = list->first)) {
list->first = list->first->next;
_oauth2_nv_free(log, ptr);
}
oauth2_mem_free(list);
end:
return;
}
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(nv, list, case_sensitive, bool, bln)
static bool _oauth2_nv_list_find(oauth2_log_t *log,
const oauth2_nv_list_t *list, const char *name,
_oauth2_nv_t **ptr, _oauth2_nv_t **prev)
{
bool rc = false;
if ((list == NULL) || (name == NULL))
goto end;
for (*ptr = list->first; *ptr; *ptr = (*ptr)->next) {
if (list->case_sensitive) {
if (strcmp((*ptr)->name, name) == 0) {
rc = true;
break;
}
} else {
if (strcasecmp((*ptr)->name, name) == 0) {
rc = true;
break;
}
}
*prev = *ptr;
}
end:
return rc;
}
bool oauth2_nv_list_set(oauth2_log_t *log, oauth2_nv_list_t *list,
const char *name, const char *value)
{
bool rc = false;
_oauth2_nv_t *ptr = NULL, *prev = NULL;
if ((list == NULL) || (name == NULL))
goto end;
_oauth2_nv_list_find(log, list, name, &ptr, &prev);
if (ptr == NULL) {
rc = oauth2_nv_list_add(log, list, name, value);
goto end;
}
if (ptr->value)
oauth2_mem_free(ptr->value);
ptr->value = value ? oauth2_strdup(value) : NULL;
rc = true;
end:
return rc;
}
bool oauth2_nv_list_unset(oauth2_log_t *log, oauth2_nv_list_t *list,
const char *name)
{
bool rc = false;
_oauth2_nv_t *ptr = NULL, *prev = NULL;
if ((list == NULL) || (name == NULL))
goto end;
_oauth2_nv_list_find(log, list, name, &ptr, &prev);
if (ptr) {
if (prev)
prev->next = ptr->next;
else
list->first = ptr->next;
_oauth2_nv_free(log, ptr);
}
rc = true;
end:
return rc;
}
bool oauth2_nv_list_add(oauth2_log_t *log, oauth2_nv_list_t *list,
const char *name, const char *value)
{
bool rc = false;
_oauth2_nv_t *ptr = NULL, *prev = NULL;
if ((list == NULL) || (name == NULL))
goto end;
ptr = _oauth2_nv_new(log, name, value);
if (ptr == NULL)
goto end;
if (list->first == NULL) {
list->first = ptr;
} else {
for (prev = list->first; prev->next; prev = prev->next)
;
prev->next = ptr;
}
rc = true;
end:
return rc;
}
const char *oauth2_nv_list_get(oauth2_log_t *log, const oauth2_nv_list_t *list,
const char *name)
{
const char *value = NULL;
_oauth2_nv_t *ptr = NULL, *prev = NULL;
if ((list == NULL) || (name == NULL))
goto end;
_oauth2_nv_list_find(log, list, name, &ptr, &prev);
if (ptr)
value = ptr->value;
end:
if (name != NULL)
oauth2_debug(log, "%s=%s", name, value ? value : "(null)");
return value;
}
void oauth2_nv_list_loop(oauth2_log_t *log, const oauth2_nv_list_t *list,
oauth2_nv_list_loop_cb_t *callback, void *rec)
{
_oauth2_nv_t *ptr = NULL;
if ((list == NULL) || (callback == NULL))
goto end;
for (ptr = list->first; ptr; ptr = ptr->next) {
if (callback(log, rec, ptr->name, ptr->value) == false)
break;
}
end:
return;
}
static bool _oauth2_nv_list_copy(oauth2_log_t *log, void *rec, const char *key,
const char *value)
{
oauth2_nv_list_t *dst = (oauth2_nv_list_t *)rec;
return oauth2_nv_list_add(log, dst, key, value);
}
oauth2_nv_list_t *oauth2_nv_list_clone(oauth2_log_t *log,
const oauth2_nv_list_t *src)
{
oauth2_nv_list_t *dst = NULL;
if (src == NULL)
goto end;
dst = oauth2_nv_list_init(log);
if (dst == NULL)
goto end;
if (oauth2_nv_list_case_sensitive_set(
log, dst, oauth2_nv_list_case_sensitive_get(log, src)) == false)
goto end;
oauth2_nv_list_loop(log, src, _oauth2_nv_list_copy, dst);
end:
return dst;
}
void oauth2_nv_list_merge_into(oauth2_log_t *log,
const oauth2_nv_list_t *source,
oauth2_nv_list_t *target)
{
if (source) {
oauth2_nv_list_loop(log, source, _oauth2_nv_list_copy, target);
}
}
/*
* JSON
*/
bool oauth2_json_decode_object(oauth2_log_t *log, const char *payload,
json_t **json)
{
bool rc = false;
json_error_t err;
*json = json_loads(payload, 0, &err);
if (*json == NULL) {
oauth2_error(log, "json_loads failed: %s", err.text);
goto end;
}
if (*json == NULL) {
oauth2_error(log, "JSON parsing returned an error: %s (%s)",
err.text, payload);
goto end;
}
if (!json_is_object(*json)) {
oauth2_error(log, "parsed JSON did not contain a JSON object");
json_decref(*json);
*json = NULL;
goto end;
}
rc = true;
end:
return rc;
}
bool oauth2_json_object_get(oauth2_log_t *log, const json_t *json,
const char *name, json_t **value)
{
bool rc = false;
json_t *v = NULL;
if ((json == NULL) || (name == NULL) || (value == NULL))
goto end;
v = json_object_get(json, name);
if (v == NULL)
goto end;
if (json_is_null(v)) {
rc = true;
goto end;
}
if (!json_is_object(v)) {
oauth2_warn(log, "found a non-object object with key: \"%s\"",
name);
goto end;
}
json_incref(v);
*value = v;
rc = true;
end:
return rc;
}
bool oauth2_json_string_get(oauth2_log_t *log, const json_t *json,
const char *name, char **value,
const char *default_value)
{
bool rc = false;
json_t *v = NULL;
if ((json == NULL) || (name == NULL) || (value == NULL)) {
if (default_value)
*value = oauth2_strdup(default_value);
goto end;
}
v = json_object_get(json, name);
if (v == NULL) {
if (default_value)
*value = oauth2_strdup(default_value);
rc = true;
goto end;
}
if (json_is_null(v)) {
rc = true;
goto end;
}
if (!json_is_string(v)) {
oauth2_warn(log, "found a non-string object with key: \"%s\"",
name);
goto end;
}
*value = oauth2_strdup(json_string_value(v));
rc = true;
end:
return rc;
}
bool oauth2_json_number_get(oauth2_log_t *log, const json_t *json,
const char *name, json_int_t *number,
const json_int_t default_value)
{
bool rc = false;
json_t *v = NULL;
if ((json == NULL) || (name == NULL) || (number == NULL)) {
*number = default_value;
goto end;
}
v = json_object_get(json, name);
if (v == NULL) {
*number = default_value;
rc = true;
goto end;
}
if (json_is_null(v)) {
*number = default_value;
rc = true;
goto end;
}
if (!json_is_number(v)) {
oauth2_warn(log, "found a non-number object with key: \"%s\"",
name);
goto end;
}
*number = json_integer_value(v);
rc = true;
end:
return rc;
}
char *oauth2_json_encode(oauth2_log_t *log, json_t *json, size_t flags)
{
char *s = json_dumps(json, flags);
char *s_value = oauth2_strdup(s);
free(s);
return s_value;
}
static bool oauth2_json_string_print(oauth2_log_t *log, json_t *result,
const char *key, const char *msg)
{
bool rc = false;
json_t *value = NULL;
char *str = NULL;
value = json_object_get(result, key);
if ((value != NULL) && (!json_is_null(value))) {
str = oauth2_json_encode(log, value, JSON_ENCODE_ANY);
oauth2_error(log,
"%s: response contained an \"%s\" entry with "
"value: \"%s\"",
msg, key,
oauth2_json_encode(log, value, JSON_ENCODE_ANY));
oauth2_mem_free(str);
rc = true;
}
return rc;
}
static bool oauth2_json_check_error(oauth2_log_t *log, json_t *json)
{
bool rc = false;
if (oauth2_json_string_print(log, json, "error",
"oidc_util_check_json_error") == true) {
oauth2_json_string_print(log, json, "error_description",
"oidc_util_check_json_error");
rc = true;
}
return rc;
}
bool oauth2_json_decode_check_error(oauth2_log_t *log, const char *str,
json_t **json)
{
bool rc = false;
if (oauth2_json_decode_object(log, str, json) == false)
goto end;
if (oauth2_json_check_error(log, *json) == true) {
json_decref(*json);
*json = NULL;
goto end;
}
rc = true;
end:
return rc;
}
/*
* other
*/
#define OAUTH2_RANDOM_BUFSIZE 4096
bool _oauth2_rand_bytes(oauth2_log_t *log, uint8_t *buf, size_t len)
{
bool rc = true;
int chunk = 0;
uint8_t *ptr = buf;
while (len > 0) {
chunk =
len < OAUTH2_RANDOM_BUFSIZE ? len : OAUTH2_RANDOM_BUFSIZE;
if (RAND_bytes(ptr, chunk) <= 0) {
oauth2_error(log, "could not generate random bytes %d",
chunk);
rc = false;
break;
}
len -= chunk;
ptr += chunk;
}
return rc;
}
char *_oauth2_bytes2str(oauth2_log_t *log, uint8_t *buf, size_t len)
{
char *rv = NULL, *ptr = NULL;
int i = 0, n = 0;
rv = oauth2_mem_alloc(len * 2 + 1);
if (rv == NULL)
goto end;
ptr = rv;
for (i = 0; i < len; i++) {
n = oauth2_snprintf(ptr, 3, "%02x", buf[i]);
if (n != 2) {
oauth2_error(log, "could not oauth2_snprintf byte %d",
i);
oauth2_mem_free(rv);
rv = NULL;
goto end;
}
ptr += 2;
}
rv[len * 2] = '\0';
end:
return rv;
}
char *oauth2_rand_str(oauth2_log_t *log, size_t len)
{
char *rv = NULL;
uint8_t *buf = NULL;
size_t half_len = 0;
if (len == 0)
goto end;
half_len = len / 2 + 1;
buf = oauth2_mem_alloc(half_len);
if (buf == NULL)
goto end;
if (_oauth2_rand_bytes(log, buf, half_len) == false)
goto end;
rv = _oauth2_bytes2str(log, buf, half_len);
// need this if len is uneven
rv[len] = '\0';
end:
if (buf)
oauth2_mem_free(buf);
// oauth2_error(log, " ## returning: %s (%lu)", rv, len);
return rv;
}
/*
char *oauth2_rand_str(oauth2_log_t *log, size_t len)
{
char *rv = NULL;
bool rc = 0;
uint8_t *buf = NULL;
if (len == 0)
goto end;
buf = oauth2_mem_alloc(len);
if (buf == NULL)
goto end;
rc = _oauth2_rand_bytes(log, buf, len);
if (rc == false)
goto end;
oauth2_base64url_encode(log, (const uint8_t *)buf, len, &rv);
if (rv)
rv[len] = '\0';
end:
if (buf)
oauth2_mem_free(buf);
// oauth2_error(log, " ## returning: %s (%lu)", rv, len);
return rv;
}
*/
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include
#include // portable: uint64_t MSVC: __int64
// MSVC defines this in winsock2.h!?
// typedef struct timeval {
// long tv_sec;
// long tv_usec;
//} timeval;
int gettimeofday(struct timeval *tp, struct timezone *tzp)
{
// Note: some broken versions only have 8 trailing zero's, the correct
// epoch has 9 trailing zero's This magic number is the number of 100
// nanosecond intervals since January 1, 1601 (UTC) until 00:00:00
// January 1, 1970
static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
time = ((uint64_t)file_time.dwLowDateTime);
time += ((uint64_t)file_time.dwHighDateTime) << 32;
tp->tv_sec = (long)((time - EPOCH) / 10000000L);
tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
return 0;
}
#endif
static oauth2_time_t _oauth2_time_now_ms()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * (uint64_t)OAUTH2_MSEC_PER_SEC) +
(tv.tv_usec / OAUTH2_USEC_PER_MSEC);
}
oauth2_time_t oauth2_time_now_sec()
{
return _oauth2_time_now_ms() / OAUTH2_MSEC_PER_SEC;
}
oauth2_time_t oauth2_parse_time_sec(oauth2_log_t *log, const char *seconds,
oauth2_time_t default_value)
{
oauth2_time_t result = default_value;
if (seconds)
result = (oauth2_time_t)strtol(seconds, NULL, 10);
return result;
}
bool oauth2_parse_bool(oauth2_log_t *log, const char *value, bool default_value)
{
return value ? (strcasecmp(value, "true") == 0) : default_value;
}
oauth2_uint_t oauth2_parse_uint(oauth2_log_t *log, const char *int_value,
oauth2_uint_t default_value)
{
oauth2_uint_t result = default_value;
if (int_value)
result = (oauth2_uint_t)strtol(int_value, NULL, 10);
return result;
}
/*
* normalize a string for use as an HTTP Header Name. Any invalid
* characters (per http://tools.ietf.org/html/rfc2616#section-4.2 and
* http://tools.ietf.org/html/rfc2616#section-2.2) are replaced with
* a dash ('-') character.
*/
char *oauth2_normalize_header_name(const char *str)
{
/* token = 1*
* CTL =
* separators = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | <">
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT */
const char *separators = "()<>@,;:\\\"/[]?={} \t";
char *ns = oauth2_strdup(str);
size_t i;
for (i = 0; i < strlen(ns); i++) {
if (ns[i] < 32 || ns[i] == 127)
ns[i] = '-';
else if (strchr(separators, ns[i]) != NULL)
ns[i] = '-';
}
return ns;
}
char *oauth_read_file(oauth2_log_t *log, const char *filename)
{
char *rv = NULL;
FILE *fp = NULL;
long fsize = 0;
size_t n = 0;
fp = fopen(filename, "rb");
if (fp == NULL) {
oauth2_error(log, "could not read file: %s", filename);
goto end;
}
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
rv = oauth2_mem_alloc(fsize + 1);
n = fread(rv, 1, fsize, fp);
if (n != fsize) {
oauth2_error(log, "read only %ld bytes from file of %ld length",
n, fsize);
oauth2_mem_free(rv);
rv = NULL;
goto end;
}
rv[fsize] = 0;
end:
if (fp)
fclose(fp);
return rv;
}
liboauth2-1.4.4/src/util_int.h 0000664 0000000 0000000 00000015573 14161030125 0016204 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_UTIL_INT_H_
#define _OAUTH2_UTIL_INT_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#ifdef _MSC_VER
// not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in
// mingw
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#define close _close
#endif
// need this for vasprintf with stdio.h
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include
#include
#include
#include "oauth2/log.h"
#include "oauth2/util.h"
/*
* internal defines
*/
#define _OAUTH2_CHAR_COLON ':'
#define _OAUTH2_CHAR_QUERY '?'
#define _OAUTH2_CHAR_FSLASH '/'
#define _OAUTH2_CHAR_EQUAL '='
#define _OAUTH2_CHAR_SPACE ' '
#define _OAUTH2_CHAR_SEMICOL ';'
#define _OAUTH2_CHAR_AMP '&'
#define _OAUTH2_CHAR_DOT '.'
#define _OAUTH2_CHAR_COMMA ','
#define _OAUTH2_STR_COMMA ","
#define _OAUTH2_STR_COLON ":"
#define _OAUTH2_STR_QMARK "?"
#define _OAUTH2_STR_AMP "&"
#define _OAUTH2_STR_SEMICOL ";"
#define _OAUTH2_STR_EQUAL "="
#define _OAUTH2_STR_DOT "."
/*
* internal generic (log) macros
*/
#define _OAUTH2_UTIL_JOSE_ERR_LOG(log, msg, err) \
oauth2_error(log, "%s failed: [%s:%lu %s %s]", msg ? msg : "", \
err.file ? err.file : "", err.line, \
err.function ? err.function : "", \
err.message ? err.message : "")
/*
* struct type set/get macros
*/
#define _OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(module, object, member, type, name) \
bool oauth2_##module##_##object##_##member##_set( \
oauth2_log_t *log, oauth2_##module##_##object##_t *p, \
const type v) \
{ \
return _oauth2_struct_slot_##name##_set( \
p, offsetof(oauth2_##module##_##object##_t, member), v); \
}
#define _OAUTH2_TYPE_IMPLEMENT_MEMBER_GET(module, object, member, type) \
type oauth2_##module##_##object##_##member##_get( \
oauth2_log_t *log, const oauth2_##module##_##object##_t *p) \
{ \
if (p == NULL) \
abort(); \
return p->member; \
}
#define _OAUTH2_TYPE_IMPLEMENT_MEMBER_SET_GET(module, object, member, type, \
name) \
_OAUTH2_TYPE_IMPLEMENT_MEMBER_SET(module, object, member, type, name) \
_OAUTH2_TYPE_IMPLEMENT_MEMBER_GET(module, object, member, type)
/*
* internal utility functions
*/
bool _oauth2_struct_slot_str_set(void *struct_ptr, size_t offset,
const char *value);
bool _oauth2_struct_slot_integer_set(void *struct_ptr, size_t offset,
const int value);
bool _oauth2_struct_slot_bln_set(void *struct_ptr, size_t offset,
const bool value);
bool _oauth2_struct_slot_ptr_set(void *struct_ptr, size_t offset,
const void *value);
bool _oauth2_struct_slot_uint_set(void *struct_ptr, size_t offset,
oauth2_uint_t value);
bool _oauth2_struct_slot_time_set(void *struct_ptr, size_t offset,
oauth2_time_t value);
char *_oauth2_stradd4(char *s1, const char *s2, const char *s3, const char *s4,
const char *s5);
bool _oauth2_nv_list_parse(oauth2_log_t *log, const char *input,
oauth2_nv_list_t *tuples, char sep_tuple,
char sep_nv, bool trim, bool url_decode);
char *_oauth2_bytes2str(oauth2_log_t *log, uint8_t *buf, size_t len);
/*
* struct list member management macros
*/
#define _OAUTH2_MEMBER_LIST_IMPLEMENT_SET(module, type, list) \
bool oauth2_##module##_##type##_##list##_set( \
oauth2_log_t *log, oauth2_##module##_##type##_t *r, \
const char *name, const char *value) \
{ \
return r ? oauth2_nv_list_set(log, r->list, name, value) \
: false; \
}
#define _OAUTH2_MEMBER_LIST_IMPLEMENT_UNSET(module, type, list) \
bool oauth2_##module##_##type##_##list##_unset( \
oauth2_log_t *log, oauth2_##module##_##type##_t *r, \
const char *name) \
{ \
return r ? oauth2_nv_list_unset(log, r->list, name) : false; \
}
#define _OAUTH2_MEMBER_LIST_IMPLEMENT_ADD(module, type, list) \
bool oauth2_##module##_##type##_##list##_add( \
oauth2_log_t *log, oauth2_##module##_##type##_t *r, \
const char *name, const char *value) \
{ \
return r ? oauth2_nv_list_add(log, r->list, name, value) \
: false; \
}
#define _OAUTH2_MEMBER_LIST_IMPLEMENT_GET(module, type, list) \
const char *oauth2_##module##_##type##_##list##_get( \
oauth2_log_t *log, const oauth2_##module##_##type##_t *r, \
const char *name) \
{ \
return r ? oauth2_nv_list_get(log, r->list, name) : NULL; \
}
#define _OAUTH2_MEMBER_LIST_IMPLEMENT_UNSET_GET(module, type, list) \
_OAUTH2_MEMBER_LIST_IMPLEMENT_UNSET(module, type, list) \
_OAUTH2_MEMBER_LIST_IMPLEMENT_GET(module, type, list)
#define _OAUTH2_MEMBER_LIST_IMPLEMENT_SET_ADD_UNSET_GET(module, type, list) \
_OAUTH2_MEMBER_LIST_IMPLEMENT_SET(module, type, list) \
_OAUTH2_MEMBER_LIST_IMPLEMENT_UNSET_GET(module, type, list) \
_OAUTH2_MEMBER_LIST_IMPLEMENT_ADD(module, type, list)
#endif /* _OAUTH2_UTIL_INT_H_ */
liboauth2-1.4.4/src/version.c 0000664 0000000 0000000 00000002175 14161030125 0016027 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include
const char *oauth2_version()
{
return OAUTH2_PACKAGE_VERSION;
}
const char *oauth2_package_string()
{
return OAUTH2_PACKAGE_NAME "-" OAUTH2_PACKAGE_VERSION;
}
liboauth2-1.4.4/test/ 0000775 0000000 0000000 00000000000 14161030125 0014361 5 ustar 00root root 0000000 0000000 liboauth2-1.4.4/test/.gitignore 0000664 0000000 0000000 00000000103 14161030125 0016343 0 ustar 00root root 0000000 0000000 /*.o
/*.log
/*.trs
/*.gcda
/*.gcno
/.deps/
/.dirstamp
/Dockerfile*
liboauth2-1.4.4/test/Dockerfile 0000664 0000000 0000000 00000003653 14161030125 0016362 0 ustar 00root root 0000000 0000000 #FROM ubuntu:trusty
FROM ubuntu:bionic
MAINTAINER hans.zandbelt@zmartzone.eu
RUN apt-get update && apt-get install -y pkg-config make gcc gdb lcov valgrind vim curl wget
RUN apt-get update && apt-get install -y autoconf automake libtool
RUN apt-get update && apt-get install -y libssl-dev libjansson-dev libcurl4-openssl-dev check
RUN apt-get update && apt-get install -y apache2-dev
#RUN apt-get update && apt-get install -y libcjose-dev
RUN apt-get update && apt-get install -y libpcre3-dev zlib1g-dev
ENV NGINX_VERSION 1.16.1
WORKDIR /root
RUN wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
RUN tar zxvf nginx-${NGINX_VERSION}.tar.gz
RUN ln -s nginx-${NGINX_VERSION} nginx
RUN cd /root/nginx && ./configure --with-debug
#ENV FLAVOR trusty
ENV FLAVOR bionic
ENV CJOSE_VERSION 0.6.1.5
ENV CJOSE_PKG libcjose0_${CJOSE_VERSION}-1~${FLAVOR}+1_arm64.deb
RUN curl -s -L -o ~/${CJOSE_PKG} https://mod-auth-openidc.org/download/${CJOSE_PKG}
RUN dpkg -i ~/${CJOSE_PKG}
ENV CJOSE_PKG libcjose-dev_${CJOSE_VERSION}-1~${FLAVOR}+1_arm64.deb
RUN curl -s -L -o ~/${CJOSE_PKG} https://mod-auth-openidc.org/download/${CJOSE_PKG}
RUN dpkg -i ~/${CJOSE_PKG}
RUN apt-get update && apt-get install -y -f
RUN apt-get update && apt-get install -y libmemcached-dev memcached
RUN apt-get update && apt-get install -y libhiredis-dev redis-server
ENV SRCDIR /root/liboauth2
RUN mkdir ${SRCDIR}
WORKDIR ${SRCDIR}
ENV LD_LIBRARY_PATH ${SRCDIR}/.libs
ENV CK_FORK "no"
RUN sed -i "s/bind .*/bind 127.0.0.1/g" /etc/redis/redis.conf
RUN echo "requirepass foobared" >> /etc/redis/redis.conf
RUN echo "#!/bin/sh" >> ./start.sh
RUN echo "service memcached start" >> ./start.sh
RUN echo "service redis-server start" >> ./start.sh
RUN chmod a+x ./start.sh
COPY . ${SRCDIR}
ARG CONFIGURE_ARGS
RUN ./autogen.sh
RUN ./configure \
CFLAGS="-g -O0 -I/usr/include/apache2" \
LDFLAGS="-lrt" \
--with-nginx=/root/nginx \
${CONFIGURE_ARGS}
RUN make all
RUN make check_liboauth2
liboauth2-1.4.4/test/check_apache.c 0000664 0000000 0000000 00000016270 14161030125 0017111 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/apache.h"
#include "oauth2/mem.h"
#include
static apr_pool_t *pool = NULL;
static request_rec *request = NULL;
static oauth2_log_t *_log = 0;
static request_rec *setup_request(apr_pool_t *pool)
{
// const unsigned int kIdx = 0;
// const unsigned int kEls = kIdx + 1;
request_rec *request =
(request_rec *)apr_pcalloc(pool, sizeof(request_rec));
request->pool = pool;
request->headers_in = apr_table_make(request->pool, 0);
request->headers_out = apr_table_make(request->pool, 0);
request->err_headers_out = apr_table_make(request->pool, 0);
apr_table_set(request->headers_in, "Host", "www.example.com");
apr_table_set(request->headers_in, "OIDC_foo", "some-value");
apr_table_set(request->headers_in, "Cookie",
"foo=bar; "
"oauth2_openidc_session"
"=0123456789abcdef; baz=zot");
request->server = apr_pcalloc(request->pool, sizeof(struct server_rec));
request->server->process =
apr_pcalloc(request->pool, sizeof(struct process_rec));
request->server->process->pool = request->pool;
request->connection =
apr_pcalloc(request->pool, sizeof(struct conn_rec));
request->connection->bucket_alloc =
apr_bucket_alloc_create(request->pool);
request->connection->local_addr =
apr_pcalloc(request->pool, sizeof(apr_sockaddr_t));
apr_pool_userdata_set("https", "scheme", NULL, request->pool);
request->server->server_hostname = "www.example.com";
request->connection->local_addr->port = 443;
request->unparsed_uri = "/bla?foo=bar¶m1=value1";
request->args = "foo=bar¶m1=value1";
apr_uri_parse(request->pool,
"https://www.example.com/bla?foo=bar¶m1=value1",
&request->parsed_uri);
/*
auth_openidc_module.module_index = kIdx;
oidc_cfg *cfg = oidc_create_server_config(request->pool,
request->server); cfg->provider.issuer = "https://idp.example.com";
cfg->provider.authorization_endpoint_url =
"https://idp.example.com/authorize";
cfg->provider.scope = "openid";
cfg->provider.client_id = "client_id";
cfg->provider.token_binding_policy =
OIDC_TOKEN_BINDING_POLICY_OPTIONAL; cfg->redirect_uri =
"https://www.example.com/protected/";
oidc_dir_cfg *d_cfg = oidc_create_dir_config(request->pool,
NULL);
*/
/*
request->server->module_config = apr_pcalloc(request->pool,
sizeof(ap_conf_vector_t *) * kEls);
request->per_dir_config = apr_pcalloc(request->pool,
sizeof(ap_conf_vector_t *) * kEls);
*/
/*
ap_set_module_config(request->server->module_config,
&auth_openidc_module, cfg);
ap_set_module_config(request->per_dir_config,
&auth_openidc_module, d_cfg);
cfg->crypto_passphrase = "12345678901234567890123456789012";
cfg->cache = &oidc_cache_shm;
cfg->cache_cfg = NULL;
cfg->cache_shm_size_max = 500;
cfg->cache_shm_entry_size_max = 16384 + 255 + 17;
cfg->cache_encrypt = 1;
if (cfg->cache->post_config(request->server) != OK) {
printf("cfg->cache->post_config failed!\n");
exit(-1);
}
*/
return request;
}
static void check_apache_log_request(oauth2_log_sink_t *sink,
const char *filename, unsigned long line,
const char *function,
oauth2_log_level_t level, const char *msg)
{
oauth2_log(_log, filename, line, function, level, "%s", msg);
}
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
apr_initialize();
apr_pool_create(&pool, NULL);
request = setup_request(pool);
}
static void teardown(void)
{
apr_pool_destroy(pool);
apr_terminate();
oauth2_shutdown(_log);
}
START_TEST(test_apache_request_state)
{
json_error_t err;
const char *s_claims = "{ \"sub\": \"joe\" }";
json_t *in_claims = NULL, *out_claims = NULL;
const char *key = "C";
char *value = NULL;
oauth2_apache_request_ctx_t *ctx = NULL;
ctx = oauth2_apache_request_context(request, check_apache_log_request,
"check_apache");
in_claims = json_loads(s_claims, 0, &err);
oauth2_apache_request_state_set_json(ctx, key, in_claims);
oauth2_apache_request_state_get_json(ctx, key, &out_claims);
ck_assert_ptr_ne(out_claims, NULL);
oauth2_json_string_get(_log, out_claims, "sub", &value, NULL);
ck_assert_ptr_ne(value, NULL);
ck_assert_str_eq(value, "joe");
oauth2_mem_free(value);
json_decref(in_claims);
json_decref(out_claims);
}
END_TEST
START_TEST(test_apache_authz_match_claim)
{
json_error_t err;
oauth2_apache_request_ctx_t *ctx = NULL;
const char *s_claims = "{ \"sub\": \"joe\" }";
json_t *claims = NULL;
bool rc = false;
ctx = oauth2_apache_request_context(request, check_apache_log_request,
"check_apache");
claims = json_loads(s_claims, 0, &err);
rc = oauth2_apache_authz_match_claim(ctx, "sub:joe", claims);
ck_assert_int_eq(rc, true);
rc = oauth2_apache_authz_match_claim(ctx, "sub:hans", claims);
ck_assert_int_eq(rc, false);
json_decref(claims);
}
END_TEST
START_TEST(test_apache_authorize)
{
json_error_t err;
oauth2_apache_request_ctx_t *ctx = NULL;
const char *s_claims = "{ \"sub\": \"joe\" }";
json_t *claims = NULL;
authz_status rc = AUTHZ_DENIED;
ctx = oauth2_apache_request_context(request, check_apache_log_request,
"check_apache");
claims = json_loads(s_claims, 0, &err);
rc = oauth2_apache_authorize(ctx, claims, "sub:hans",
oauth2_apache_authz_match_claim);
ck_assert_int_eq(rc, AUTHZ_DENIED_NO_USER);
request->user = "joe";
rc = oauth2_apache_authorize(ctx, claims, "sub:hans",
oauth2_apache_authz_match_claim);
ck_assert_int_eq(rc, AUTHZ_DENIED);
rc = oauth2_apache_authorize(ctx, claims, "sub:joe",
oauth2_apache_authz_match_claim);
ck_assert_int_eq(rc, AUTHZ_GRANTED);
json_decref(claims);
}
END_TEST
START_TEST(test_apache_http_response_set)
{
bool rc = false;
oauth2_http_response_t *response = NULL;
response = oauth2_http_response_init(_log);
rc = oauth2_apache_http_response_set(_log, response, request);
ck_assert_int_eq(rc, true);
oauth2_http_response_free(_log, response);
}
END_TEST
Suite *oauth2_check_apache_suite()
{
Suite *s = suite_create("apache");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_apache_request_state);
tcase_add_test(c, test_apache_authz_match_claim);
tcase_add_test(c, test_apache_authorize);
tcase_add_test(c, test_apache_http_response_set);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_cache.c 0000664 0000000 0000000 00000013405 14161030125 0016730 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/cache.h"
#include "oauth2/cfg.h"
#include "oauth2/mem.h"
#include
#include
#include
#include
static oauth2_log_t *_log = 0;
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_shutdown(_log);
}
// oauth2_cfg_set_cache
START_TEST(test_cache_bogus)
{
char *rv = NULL;
rv = oauth2_cfg_set_cache(_log, NULL, "bogus", NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
}
END_TEST
static void _test_basic_cache(oauth2_cache_t *c)
{
bool rc = false;
char *value = NULL;
rc = oauth2_cache_set(_log, c, "piet", "klaas", 2);
ck_assert_int_eq(rc, true);
value = NULL;
rc = oauth2_cache_get(_log, c, "piet", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(value, NULL);
ck_assert_str_eq(value, "klaas");
oauth2_mem_free(value);
sleep(3);
value = NULL;
rc = oauth2_cache_get(_log, c, "piet", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_eq(value, NULL);
rc = oauth2_cache_set(_log, c, "piet", "klaas", 1);
ck_assert_int_eq(rc, true);
value = NULL;
rc = oauth2_cache_get(_log, c, "piet", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(value, NULL);
ck_assert_str_eq(value, "klaas");
oauth2_mem_free(value);
rc = oauth2_cache_set(_log, c, "piet", NULL, 0);
ck_assert_int_eq(rc, true);
value = NULL;
rc = oauth2_cache_get(_log, c, "piet", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_eq(value, NULL);
value = NULL;
rc = oauth2_cache_get(_log, c, "piet", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_eq(value, NULL);
}
START_TEST(test_cache_shm)
{
bool rc = false;
char *value = NULL;
oauth2_cache_t *c = NULL;
char *rv = NULL;
rv = oauth2_cfg_set_cache(_log, NULL, "shm",
"max_val_size=16&max_entries=2");
ck_assert_ptr_eq(rv, NULL);
c = oauth2_cache_obtain(_log, NULL);
ck_assert_ptr_ne(c, NULL);
_test_basic_cache(c);
// override the max nr of entries
rc = oauth2_cache_set(_log, c, "hans", "zandbelt", 1);
ck_assert_int_eq(rc, true);
rc = oauth2_cache_set(_log, c, "nog", "een", 1);
ck_assert_int_eq(rc, true);
rc = oauth2_cache_set(_log, c, "hallo", "dan", 1);
ck_assert_int_eq(rc, true);
value = NULL;
rc = oauth2_cache_get(_log, c, "piet", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_eq(value, NULL);
rc = oauth2_cache_set(_log, c, "value_too_long", "12345678901234567890",
1);
ck_assert_int_eq(rc, false);
rv = oauth2_cfg_set_cache(
_log, NULL, "shm",
"name=short_key_size&key_hash_algo=none&max_key_size=8");
ck_assert_ptr_eq(rv, NULL);
c = oauth2_cache_obtain(_log, "short_key_size");
ck_assert_ptr_ne(c, NULL);
rc = oauth2_cache_set(_log, c, "hans", "zandbelt", 1);
ck_assert_int_eq(rc, true);
rc = oauth2_cache_set(_log, c,
"key_too_long_"
"123456789012345678901234567890123456789012345678"
"9012345678901234567890",
"12345678901234567890", 1);
ck_assert_int_eq(rc, false);
}
END_TEST
START_TEST(test_cache_file)
{
bool rc = false;
oauth2_cache_t *c = NULL;
char *rv = NULL;
char *value = NULL;
rv = oauth2_cfg_set_cache(
_log, NULL, "file",
"name=file&key_hash_algo=none&max_key_size=8&clean_interval=1");
ck_assert_ptr_eq(rv, NULL);
c = oauth2_cache_obtain(_log, "file");
ck_assert_ptr_ne(c, NULL);
_test_basic_cache(c);
rc = oauth2_cache_set(_log, c, "hans", "zandbelt", 1);
ck_assert_int_eq(rc, true);
// also wait for the cache clean cycle (interval=1) to run
sleep(1);
rc = oauth2_cache_set(_log, c, "hans2", "zandbelt2", 1);
ck_assert_int_eq(rc, true);
value = NULL;
rc = oauth2_cache_get(_log, c, "hans", &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_eq(value, NULL);
// TODO: test file /tmp/mod-auth-openidc-hans exists?
}
END_TEST
#ifdef HAVE_LIBMEMCACHE
START_TEST(test_cache_memcache)
{
oauth2_cache_t *c = NULL;
char *rv = NULL;
rv = oauth2_cfg_set_cache(_log, NULL, "memcache", "name=memcache");
ck_assert_ptr_eq(rv, NULL);
c = oauth2_cache_obtain(_log, "memcache");
ck_assert_ptr_ne(c, NULL);
_test_basic_cache(c);
}
END_TEST
#endif
#ifdef HAVE_LIBHIREDIS
START_TEST(test_cache_redis)
{
oauth2_cache_t *c = NULL;
char *rv = NULL;
rv = oauth2_cfg_set_cache(_log, NULL, "redis",
"name=redis&password=foobared");
ck_assert_ptr_eq(rv, NULL);
c = oauth2_cache_obtain(_log, "redis");
ck_assert_ptr_ne(c, NULL);
_test_basic_cache(c);
}
END_TEST
#endif
Suite *oauth2_check_cache_suite()
{
Suite *s = suite_create("cache");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_cache_bogus);
tcase_add_test(c, test_cache_shm);
tcase_add_test(c, test_cache_file);
#ifdef HAVE_LIBMEMCACHE
tcase_add_test(c, test_cache_memcache);
#endif
#ifdef HAVE_LIBHIREDIS
tcase_add_test(c, test_cache_redis);
#endif
tcase_set_timeout(c, 8);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_cfg.c 0000664 0000000 0000000 00000014143 14161030125 0016424 0 ustar 00root root 0000000 0000000 #include
#include "check_liboauth2.h"
#include "oauth2/cfg.h"
#include
#include "cfg_int.h"
static oauth2_log_t *_log = 0;
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_shutdown(_log);
}
typedef struct test_cfg_slot_struct {
oauth2_flag_t flag;
oauth2_uint_t uint;
} test_cfg_slot_struct;
START_TEST(test_flag_slot)
{
const char *rv = NULL;
test_cfg_slot_struct st = {OAUTH2_CFG_FLAG_UNSET,
OAUTH2_CFG_UINT_UNSET};
rv = oauth2_cfg_set_flag_slot(
NULL, offsetof(test_cfg_slot_struct, flag), NULL);
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.flag, OAUTH2_CFG_FLAG_UNSET);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, OAUTH2_CFG_FLAG_UNSET);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"");
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.flag, OAUTH2_CFG_FLAG_UNSET);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"true");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, true);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"false");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, false);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"True");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, true);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"False");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, false);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"TRUE");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, true);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"FALSE");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, false);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"0");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, false);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"1");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, true);
st.flag = OAUTH2_CFG_FLAG_UNSET;
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"2");
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.flag, OAUTH2_CFG_FLAG_UNSET);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"On");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, true);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"Off");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, false);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"ON");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, true);
rv = oauth2_cfg_set_flag_slot(&st, offsetof(test_cfg_slot_struct, flag),
"OFF");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.flag, false);
}
END_TEST
START_TEST(test_uint_slot)
{
const char *rv = NULL;
test_cfg_slot_struct st = {OAUTH2_CFG_FLAG_UNSET,
OAUTH2_CFG_UINT_UNSET};
rv = oauth2_cfg_set_uint_slot(
NULL, offsetof(test_cfg_slot_struct, uint), NULL);
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.uint, OAUTH2_CFG_UINT_UNSET);
rv = oauth2_cfg_set_uint_slot(&st, offsetof(test_cfg_slot_struct, uint),
NULL);
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.uint, OAUTH2_CFG_UINT_UNSET);
rv = oauth2_cfg_set_uint_slot(&st, offsetof(test_cfg_slot_struct, uint),
"");
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.uint, OAUTH2_CFG_UINT_UNSET);
rv = oauth2_cfg_set_uint_slot(&st, offsetof(test_cfg_slot_struct, uint),
"1two");
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.uint, OAUTH2_CFG_UINT_UNSET);
rv = oauth2_cfg_set_uint_slot(&st, offsetof(test_cfg_slot_struct, uint),
"-1");
ck_assert_ptr_ne(rv, NULL);
ck_assert_uint_eq(st.uint, OAUTH2_CFG_UINT_UNSET);
rv = oauth2_cfg_set_uint_slot(&st, offsetof(test_cfg_slot_struct, uint),
"1");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(st.uint, 1);
}
END_TEST
START_TEST(test_target_pass)
{
oauth2_cfg_target_pass_t *cfg = NULL, *cfg2 = NULL, *cfg3 = NULL;
char *rv = NULL;
cfg = oauth2_cfg_target_pass_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_set_target_pass_options(_log, NULL, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
ck_assert_uint_eq(oauth2_cfg_target_pass_get_as_envvars(cfg), true);
ck_assert_uint_eq(oauth2_cfg_target_pass_get_as_headers(cfg), true);
ck_assert_ptr_eq(oauth2_cfg_target_pass_get_authn_header(cfg), NULL);
ck_assert_str_eq(oauth2_cfg_target_pass_get_prefix(cfg),
"OAUTH2_CLAIM_");
ck_assert_str_eq(oauth2_cfg_target_get_remote_user_claim(cfg), "sub");
rv = oauth2_cfg_set_target_pass_options(
_log, cfg,
"envvars=false&headers=false&authn_header=auth&prefix=oidc&remote_"
"user_claim=preferred_username");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(oauth2_cfg_target_pass_get_as_envvars(cfg), false);
ck_assert_uint_eq(oauth2_cfg_target_pass_get_as_headers(cfg), false);
ck_assert_str_eq(oauth2_cfg_target_pass_get_authn_header(cfg), "auth");
ck_assert_str_eq(oauth2_cfg_target_pass_get_prefix(cfg), "oidc");
ck_assert_str_eq(oauth2_cfg_target_get_remote_user_claim(cfg),
"preferred_username");
oauth2_cfg_target_pass_merge(_log, NULL, NULL, NULL);
cfg2 = oauth2_cfg_target_pass_init(_log);
ck_assert_ptr_ne(cfg2, NULL);
cfg3 = oauth2_cfg_target_pass_init(_log);
ck_assert_ptr_ne(cfg3, NULL);
oauth2_cfg_target_pass_merge(_log, cfg2, cfg, cfg3);
oauth2_cfg_target_pass_free(_log, cfg3);
oauth2_cfg_target_pass_free(_log, cfg2);
oauth2_cfg_target_pass_free(_log, cfg);
}
END_TEST
Suite *oauth2_check_cfg_suite()
{
Suite *s = suite_create("cfg");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_flag_slot);
tcase_add_test(c, test_uint_slot);
tcase_add_test(c, test_target_pass);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_http.c 0000664 0000000 0000000 00000060244 14161030125 0016647 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/http.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include
static oauth2_log_t *_log = NULL;
OAUTH2_CHECK_HTTP_PATHS
void oauth2_check_http_cleanup()
{
oauth2_check_http_base_free();
}
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
// for coverage
oauth2_http_request_free(_log, NULL);
}
static void teardown(void)
{
oauth2_shutdown(_log);
}
static void *faulty_alloc(size_t amt)
{
return NULL;
}
START_TEST(test_request_scheme)
{
char *scheme = NULL;
oauth2_http_request_t *r = NULL;
bool rc;
// set null scheme
scheme = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_scheme_set(_log, r, NULL);
ck_assert_int_eq(rc, false);
oauth2_http_request_free(_log, r);
// set no scheme and get default
scheme = NULL;
r = oauth2_http_request_init(_log);
scheme = oauth2_http_request_scheme_get(_log, r);
ck_assert_str_eq(scheme, "https");
oauth2_mem_free(scheme);
oauth2_http_request_free(_log, r);
// set scheme via native setting
scheme = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "http");
scheme = oauth2_http_request_scheme_get(_log, r);
ck_assert_str_eq(scheme, "http");
oauth2_mem_free(scheme);
oauth2_http_request_free(_log, r);
// set scheme via X-Forwarded-Proto
scheme = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "http");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto", "https");
scheme = oauth2_http_request_scheme_get(_log, r);
ck_assert_str_eq(scheme, "https");
oauth2_mem_free(scheme);
oauth2_http_request_free(_log, r);
// lowercase header
scheme = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "http");
oauth2_http_request_header_set(_log, r, "x-forwarded-proto", "https");
scheme = oauth2_http_request_scheme_get(_log, r);
ck_assert_str_eq(scheme, "https");
oauth2_mem_free(scheme);
oauth2_http_request_free(_log, r);
// set scheme via X-Forwarded-Proto with multiple entries
scheme = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "http");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto",
"https, http");
scheme = oauth2_http_request_scheme_get(_log, r);
ck_assert_str_eq(scheme, "https");
oauth2_mem_free(scheme);
oauth2_http_request_free(_log, r);
// set no scheme and add some headers
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "One", "1");
oauth2_http_request_header_set(_log, r, "Two", "2");
scheme = oauth2_http_request_scheme_get(_log, r);
ck_assert_str_eq(scheme, "https");
oauth2_mem_free(scheme);
oauth2_http_request_free(_log, r);
// get scheme from null request
scheme = NULL;
scheme = oauth2_http_request_scheme_get(_log, NULL);
ck_assert_ptr_eq(scheme, NULL);
}
END_TEST
START_TEST(test_request_hostname)
{
char *hostname = NULL;
oauth2_http_request_t *r = NULL;
bool rc;
// get default hostname null
hostname = NULL;
hostname = oauth2_http_request_hostname_get(_log, NULL);
ck_assert_ptr_eq(hostname, NULL);
// set null hostname
hostname = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_hostname_set(_log, r, NULL);
ck_assert_int_eq(rc, false);
oauth2_http_request_free(_log, r);
// set and get hostname
hostname = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
hostname = oauth2_http_request_hostname_get(_log, r);
ck_assert_str_eq(hostname, "internal");
oauth2_mem_free(hostname);
oauth2_http_request_free(_log, r);
// set native hostname but override via X-Forwarded-Host
hostname = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host", "external");
hostname = oauth2_http_request_hostname_get(_log, r);
ck_assert_str_eq(hostname, "external");
oauth2_mem_free(hostname);
oauth2_http_request_free(_log, r);
// set native hostname but override via X-Forwarded-Host that includes
// port
hostname = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"external:8080");
hostname = oauth2_http_request_hostname_get(_log, r);
ck_assert_str_eq(hostname, "external");
oauth2_mem_free(hostname);
oauth2_http_request_free(_log, r);
}
END_TEST
START_TEST(test_request_port)
{
char *port = NULL;
oauth2_http_request_t *r = NULL;
bool rc;
// get default port null
port = NULL;
r = oauth2_http_request_init(_log);
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// set native port
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 443);
ck_assert_int_eq(rc, true);
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get port via X-Forwareded-Port
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Port", "8080");
port = oauth2_http_request_port_get(_log, r);
ck_assert_str_eq(port, "8080");
oauth2_mem_free(port);
oauth2_http_request_free(_log, r);
// get port via X-Forwarded-Host overriding Host
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "Host", "internal:8282");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"external:8181");
port = oauth2_http_request_port_get(_log, r);
ck_assert_str_eq(port, "8181");
oauth2_mem_free(port);
oauth2_http_request_free(_log, r);
// get port via Host that includes port
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "Host", "internal:8282");
port = oauth2_http_request_port_get(_log, r);
ck_assert_str_eq(port, "8282");
oauth2_mem_free(port);
oauth2_http_request_free(_log, r);
// get port as null default for scheme, skipping Host header that
// doesn't contain a port
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "http");
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get default port null, skipping Host and X-Forwarded-Host that don't
// contain a port
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "Host", "internal");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host", "external");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get default port null for https scheme, skipping Host that doesn't
// contain a port
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "https");
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get native port set to default scheme port
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 443);
ck_assert_int_eq(rc, true);
oauth2_http_request_scheme_set(_log, r, "https");
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get native port, skipping Host that doesn't contain a port
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 8080);
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_str_eq(port, "8080");
oauth2_mem_free(port);
oauth2_http_request_free(_log, r);
// get default port for scheme determined via X-Forwarded-Proto,
// overriding native port
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 8080);
oauth2_http_request_header_set(_log, r, "Host", "internal");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto", "https");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get default port null for default scheme https
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 443);
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get default port null for scheme http
port = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, r, "http");
rc = oauth2_http_request_port_set(_log, r, 80);
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_ptr_eq(port, NULL);
oauth2_http_request_free(_log, r);
// get native port 80 overriding provided scheme
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 80);
oauth2_http_request_scheme_set(_log, r, "https");
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_str_eq(port, "80");
oauth2_mem_free(port);
oauth2_http_request_free(_log, r);
// get native port 8080 overriding provided scheme
port = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_port_set(_log, r, 8080);
oauth2_http_request_scheme_set(_log, r, "https");
oauth2_http_request_header_set(_log, r, "Host", "internal");
port = oauth2_http_request_port_get(_log, r);
ck_assert_str_eq(port, "8080");
oauth2_mem_free(port);
oauth2_http_request_free(_log, r);
}
END_TEST
START_TEST(test_request_header)
{
const char *value = NULL;
oauth2_http_request_t *r = NULL;
oauth2_mem_alloc_fn_t alloc_save;
bool rc;
// set a bunch of headers and retrieve one of them
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_header_set(_log, r, "One", "1");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Two", "2");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Three", "3");
ck_assert_int_eq(rc, true);
value = oauth2_http_request_header_get(_log, r, "Two");
ck_assert_str_eq(value, "2");
oauth2_http_request_free(_log, r);
// retrieve null header from request
r = oauth2_http_request_init(_log);
value = oauth2_http_request_header_get(_log, r, NULL);
ck_assert_ptr_eq(value, NULL);
oauth2_http_request_free(_log, r);
// retrieve from null request
r = NULL;
value = oauth2_http_request_header_get(_log, r, "Two");
ck_assert_ptr_eq(value, NULL);
// retrieve null header from null request
r = NULL;
value = oauth2_http_request_header_get(_log, r, NULL);
ck_assert_ptr_eq(value, NULL);
// set header using failing memory allocation
r = NULL;
r = oauth2_http_request_init(_log);
alloc_save = oauth2_mem_get_alloc();
oauth2_mem_set_alloc_funcs(faulty_alloc, oauth2_mem_get_realloc(),
oauth2_mem_get_dealloc());
rc = oauth2_http_request_header_set(_log, r, "One", "1");
ck_assert_int_eq(rc, false);
oauth2_http_request_free(_log, r);
// create request using failing memory allocation
r = oauth2_http_request_init(_log);
ck_assert_ptr_eq(r, NULL);
// reset the memory allocator function to their defaults
oauth2_mem_set_alloc_funcs(alloc_save, oauth2_mem_get_realloc(),
oauth2_mem_get_dealloc());
}
END_TEST
START_TEST(test_url_base)
{
char *base = NULL;
oauth2_http_request_t *r = NULL;
// non-initialized
base = NULL;
r = oauth2_http_request_init(_log);
base = oauth2_http_request_url_base_get(_log, r);
ck_assert_ptr_eq(base, NULL);
oauth2_http_request_free(_log, r);
// only hostname initialized, defaults to https
base = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
base = oauth2_http_request_url_base_get(_log, r);
ck_assert_str_eq(base, "https://internal");
oauth2_mem_free(base);
oauth2_http_request_free(_log, r);
// hostname and port initialized, scheme defaults to https
base = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
oauth2_http_request_port_set(_log, r, 8080);
base = oauth2_http_request_url_base_get(_log, r);
ck_assert_str_eq(base, "https://internal:8080");
oauth2_mem_free(base);
oauth2_http_request_free(_log, r);
// X-Forwarded-Host with port provided
base = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
oauth2_http_request_port_set(_log, r, 8080);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"external:9000");
base = oauth2_http_request_url_base_get(_log, r);
ck_assert_str_eq(base, "https://external:9000");
oauth2_mem_free(base);
oauth2_http_request_free(_log, r);
// X-Forwarded-Proto and X-Forwarded-Host provided
base = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
oauth2_http_request_port_set(_log, r, 8080);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto", "http");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"external:9000");
base = oauth2_http_request_url_base_get(_log, r);
ck_assert_str_eq(base, "http://external:9000");
oauth2_mem_free(base);
oauth2_http_request_free(_log, r);
// X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Port provided
base = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_hostname_set(_log, r, "internal");
oauth2_http_request_port_set(_log, r, 8080);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto", "http");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"external:9000");
oauth2_http_request_header_set(_log, r, "X-Forwarded-Port", "8000");
base = oauth2_http_request_url_base_get(_log, r);
ck_assert_str_eq(base, "http://external:8000");
oauth2_mem_free(base);
oauth2_http_request_free(_log, r);
}
END_TEST
START_TEST(test_url_get)
{
char *url = NULL;
oauth2_http_request_t *r = NULL;
// mostly test backwards compatibility
url = NULL;
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "Host", "www.example.com");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.example.com");
oauth2_mem_free(url);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"www.outer.com");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.outer.com");
oauth2_mem_free(url);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Host",
"www.outer.com:654");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.outer.com:654");
oauth2_mem_free(url);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Port", "321");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.outer.com:321");
oauth2_mem_free(url);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto", "http");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "http://www.outer.com:321");
oauth2_mem_free(url);
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto",
"https, http");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.outer.com:321");
oauth2_mem_free(url);
// add a space after the comma...
oauth2_http_request_header_set(_log, r, "X-Forwarded-Proto",
"https , http");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.outer.com:321");
oauth2_mem_free(url);
oauth2_http_request_header_unset(_log, r, "X-Forwarded-Host");
oauth2_http_request_header_unset(_log, r, "X-Forwarded-Port");
url = oauth2_http_request_url_get(_log, r);
ck_assert_str_eq(url, "https://www.example.com");
oauth2_mem_free(url);
// test deleting first header
oauth2_http_request_header_set(_log, r, "Host", NULL);
url = oauth2_http_request_url_get(_log, r);
ck_assert_ptr_eq(url, NULL);
oauth2_http_request_free(_log, r);
}
END_TEST
START_TEST(test_query_encode)
{
oauth2_nv_list_t *args = NULL;
char *url = NULL, *enc = NULL;
args = oauth2_nv_list_init(_log);
enc = oauth2_http_url_query_encode(_log, NULL, args);
ck_assert_str_eq(enc, "");
oauth2_mem_free(enc);
url = "https://www.example.com";
enc = oauth2_http_url_query_encode(_log, url, args);
ck_assert_str_eq(enc, "https://www.example.com");
oauth2_mem_free(enc);
url = "https://www.example.com";
oauth2_nv_list_add(_log, args, "two", "TWO TWO");
enc = oauth2_http_url_query_encode(_log, url, args);
ck_assert_str_eq(enc, "https://www.example.com?two=TWO%20TWO");
oauth2_mem_free(enc);
url = "https://www.example.com?one=ONE";
enc = oauth2_http_url_query_encode(_log, url, args);
ck_assert_str_eq(enc, "https://www.example.com?one=ONE&two=TWO%20TWO");
oauth2_mem_free(enc);
url = "https://www.example.com";
oauth2_nv_list_add(_log, args, "none", NULL);
enc = oauth2_http_url_query_encode(_log, url, args);
ck_assert_str_eq(enc, "https://www.example.com?two=TWO%20TWO&none=");
oauth2_mem_free(enc);
oauth2_nv_list_free(_log, args);
}
END_TEST
START_TEST(test_form_encode)
{
oauth2_nv_list_t *args = NULL;
char *enc = NULL;
args = oauth2_nv_list_init(_log);
enc = oauth2_http_url_form_encode(_log, args);
ck_assert_ptr_eq(enc, NULL);
oauth2_nv_list_add(_log, args, "two", "TWO TWO");
enc = oauth2_http_url_form_encode(_log, args);
ck_assert_str_eq(enc, "two=TWO%20TWO");
oauth2_mem_free(enc);
oauth2_nv_list_add(_log, args, "three", "THREE&THREE");
enc = oauth2_http_url_form_encode(_log, args);
ck_assert_str_eq(enc, "two=TWO%20TWO&three=THREE%26THREE");
oauth2_mem_free(enc);
oauth2_nv_list_free(_log, args);
}
END_TEST
static char *get_json = "{ \"my\": \"json\" }";
static char *get_json_path = "/my_json";
static char *oauth2_check_http_serve_get(const char *request)
{
if (strncmp(request, get_json_path, strlen(get_json_path)) == 0) {
return oauth2_strdup(get_json);
}
return oauth2_strdup("problem");
}
static char *post_json = "{ \"form\": \"post\" }";
static char *post_form_json_path = "/post_json";
static char *oauth2_check_http_serve_post(const char *request)
{
if (strncmp(request, post_form_json_path,
strlen(post_form_json_path)) == 0) {
return oauth2_strdup(post_json);
}
return oauth2_strdup("problem");
}
START_TEST(test_http_get)
{
bool rc;
char *response = NULL, *url = NULL;
oauth2_nv_list_t *params = oauth2_nv_list_init(_log);
oauth2_http_call_ctx_t *ctx = oauth2_http_call_ctx_init(_log);
url = oauth2_stradd(NULL, oauth2_check_http_base_url(), get_json_path,
NULL);
rc = oauth2_http_get(_log, url, NULL, NULL, NULL, NULL);
ck_assert_int_eq(rc, false);
rc = oauth2_http_get(_log, url, NULL, NULL, &response, NULL);
ck_assert_int_eq(rc, true);
ck_assert_str_eq(response, get_json);
oauth2_mem_free(response);
oauth2_http_call_ctx_basic_auth_set(_log, ctx, "hans:ja", "my secret",
true);
oauth2_http_call_ctx_cookie_add(_log, ctx, "mycookie", "mycvalue");
oauth2_http_call_ctx_cookie_add(_log, ctx, "othercookie", "my2ndvalue");
oauth2_http_call_ctx_hdr_add(_log, ctx, "SM_SESSION", "something");
oauth2_nv_list_add(_log, params, "jan", "piet");
rc = oauth2_http_get(_log, url, params, ctx, &response, NULL);
ck_assert_int_eq(rc, true);
ck_assert_str_eq(response, get_json);
oauth2_mem_free(response);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
oauth2_mem_free(url);
}
END_TEST
START_TEST(test_http_post_form)
{
bool rc;
char *response = NULL, *url = NULL;
oauth2_nv_list_t *params = oauth2_nv_list_init(_log);
url = oauth2_stradd(NULL, oauth2_check_http_base_url(),
post_form_json_path, NULL);
oauth2_nv_list_add(_log, params, "jan", "piet");
rc = oauth2_http_post_form(_log, url, params, NULL, &response, NULL);
ck_assert_int_eq(rc, true);
ck_assert_str_eq(response, post_json);
oauth2_mem_free(response);
oauth2_nv_list_free(_log, params);
oauth2_mem_free(url);
}
END_TEST
START_TEST(test_cookies)
{
bool rc = false;
oauth2_http_request_t *r = NULL;
char *rv = NULL;
const char *rvv = NULL;
oauth2_nv_list_t *params = NULL;
params = NULL;
rc = oauth2_parse_form_encoded_params(
_log, "jan=piet&klaas=vaak&hans=zandbelt", ¶ms);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(params, NULL);
oauth2_nv_list_free(_log, params);
r = oauth2_http_request_init(_log);
oauth2_http_request_header_set(_log, r, "Host", "www.example.com");
oauth2_http_request_header_set(_log, r, "Cookie",
"jan=piet; klaas=vaak; hans=zandbelt");
rv = oauth2_http_request_cookie_get(_log, r, "klaas", false);
ck_assert_ptr_ne(rv, NULL);
ck_assert_str_eq(rv, "vaak");
oauth2_mem_free(rv);
rv = oauth2_http_request_cookie_get(_log, r, "klaas", true);
ck_assert_ptr_ne(rv, NULL);
ck_assert_str_eq(rv, "vaak");
oauth2_mem_free(rv);
rv = oauth2_http_request_cookie_get(_log, r, "klaas", true);
ck_assert_ptr_eq(rv, NULL);
rvv = oauth2_http_request_header_get(_log, r, "Cookie");
ck_assert_ptr_ne(rvv, NULL);
ck_assert_str_eq(rvv, "jan=piet; hans=zandbelt");
oauth2_http_request_free(_log, r);
}
END_TEST
START_TEST(test_auth)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = oauth2_http_call_ctx_init(_log);
rc = oauth2_http_auth_client_cert(_log, "cert.pem", "key.pem", ctx);
ck_assert_int_eq(rc, true);
rc = oauth2_http_auth_basic(_log, "myusername", "mypassword", ctx);
ck_assert_int_eq(rc, true);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_xml_http_request)
{
bool rc = false;
oauth2_http_request_t *r = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_header_set(_log, r, "Accept", "text/html");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_is_xml_http_request(_log, r);
ck_assert_int_eq(rc, false);
rc = oauth2_http_request_header_set(_log, r, "Accept",
"application/json");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_is_xml_http_request(_log, r);
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Accept", "*/*");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_is_xml_http_request(_log, r);
ck_assert_int_eq(rc, false);
rc = oauth2_http_request_header_set(_log, r, "X-Requested-With",
"XMLHttpRequest");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(
_log, r, "Accept",
"text/html, application/xhtml+xml, application/xml;q=0.9, "
"image/webp, */*;q=0.8");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_is_xml_http_request(_log, r);
ck_assert_int_eq(rc, true);
oauth2_http_request_free(_log, r);
}
END_TEST
Suite *oauth2_check_http_suite()
{
Suite *s = suite_create("http");
TCase *c = tcase_create("core");
liboauth2_check_register_http_callbacks(oauth2_check_http_base_path(),
oauth2_check_http_serve_get,
oauth2_check_http_serve_post);
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_request_scheme);
tcase_add_test(c, test_request_hostname);
tcase_add_test(c, test_request_port);
tcase_add_test(c, test_url_base);
tcase_add_test(c, test_url_get);
tcase_add_test(c, test_request_header);
tcase_add_test(c, test_query_encode);
tcase_add_test(c, test_form_encode);
tcase_add_test(c, test_http_get);
tcase_add_test(c, test_http_post_form);
tcase_add_test(c, test_cookies);
tcase_add_test(c, test_auth);
tcase_add_test(c, test_xml_http_request);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_ipc.c 0000664 0000000 0000000 00000005711 14161030125 0016441 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/ipc.h"
#include "oauth2/mem.h"
#include
#include
static oauth2_log_t *_log = 0;
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_shutdown(_log);
}
START_TEST(test_sema)
{
bool rc = false;
oauth2_ipc_sema_t *s = NULL;
s = oauth2_ipc_sema_init(_log);
ck_assert_ptr_ne(s, NULL);
rc = oauth2_ipc_sema_post_config(_log, s);
ck_assert_int_eq(rc, true);
rc = oauth2_ipc_sema_post(_log, s);
ck_assert_int_eq(rc, true);
rc = oauth2_ipc_sema_post(_log, s);
ck_assert_int_eq(rc, true);
rc = oauth2_ipc_sema_wait(_log, s);
ck_assert_int_eq(rc, true);
rc = oauth2_ipc_sema_wait(_log, s);
ck_assert_int_eq(rc, true);
// TODO: check for timeout
// rc = oauth2_ipc_sema_wait(_log, s);
// ck_assert_int_eq(rc, true);
oauth2_ipc_sema_free(_log, s);
s = NULL;
}
END_TEST
START_TEST(test_mutex)
{
bool rc = false;
oauth2_ipc_mutex_t *m = NULL;
m = oauth2_ipc_mutex_init(_log);
ck_assert_ptr_ne(m, NULL);
rc = oauth2_ipc_mutex_post_config(_log, m);
ck_assert_int_eq(rc, true);
rc = oauth2_ipc_mutex_lock(_log, m);
ck_assert_int_eq(rc, true);
// TODO: check timeout
// rc = oauth2_ipc_mutex_lock(_log, m);
// ck_assert_int_eq(rc, true);
rc = oauth2_ipc_mutex_unlock(_log, m);
ck_assert_int_eq(rc, true);
oauth2_ipc_mutex_free(_log, m);
m = NULL;
}
END_TEST
START_TEST(test_shm)
{
bool rc = false;
oauth2_ipc_shm_t *shm = NULL;
void *ptr = NULL;
shm = oauth2_ipc_shm_init(_log, 256);
ck_assert_ptr_ne(shm, NULL);
rc = oauth2_ipc_shm_post_config(_log, shm);
ck_assert_int_eq(rc, true);
ptr = oauth2_ipc_shm_get(_log, shm);
ck_assert_ptr_ne(ptr, NULL);
oauth2_ipc_shm_free(_log, shm);
shm = NULL;
}
END_TEST
Suite *oauth2_check_ipc_suite()
{
Suite *s = suite_create("ipc");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_sema);
tcase_add_test(c, test_mutex);
tcase_add_test(c, test_shm);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_jose.c 0000664 0000000 0000000 00000035742 14161030125 0016635 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include
#include
#include
#include "check_liboauth2.h"
#include "jose_int.h"
static oauth2_log_t *_log = 0;
static const char *secret1 = NULL;
static const char *secret2 = NULL;
static json_t *payload1 = NULL;
const char *s_payload1 = NULL;
static json_t *payload2 = NULL;
const char *s_payload2 = NULL;
static const char *serialized_hdr = NULL;
static const char *encrypted1 = NULL;
static const char *encrypted1_corrupt_tag = NULL;
static const char *encrypted1_signed2 = NULL;
static const char *encrypted1_signed2_corrupt_sig = NULL;
static const char *encrypted1_signed2_corrupt_hdr = NULL;
static const char *encrypted1_signed2_corrupt_payload = NULL;
OAUTH2_CHECK_HTTP_PATHS
void oauth2_check_jose_cleanup()
{
oauth2_check_http_base_free();
}
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
secret1 = "12345";
secret2 = "abcde";
s_payload1 = "{\"iss\":\"https://example.org\"}";
json_error_t err;
payload1 = json_loads(s_payload1, 0, &err);
s_payload2 = "{\"aud\":\"https://another.org\"}";
payload2 = json_loads(s_payload2, 0, &err);
serialized_hdr = "eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..";
encrypted1 =
"eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..-"
"jvbGVBwu8GOvVwF.3lPSed2UdIu-"
"obtRNcMaCP7WUYETLwkXD2BZbx0sjOUiRNbHXQmYm7c0B4Mp2f2m-C-"
"hAzBdlPGDjeNP1PiFZiWFtDRGuskW4qGrUoFCSWZx5vAyfOFjuRN2ydst7"
"geoD32_8zY-pYyVzQ.HWN2Hq8sLnFWT_XKU20Mpw";
// encrypted_wrong_payload =
encrypted1_corrupt_tag =
"eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..-"
"jvbGVBwu8GOvVwF.3lPSed2UdIu-"
"obtRNcMaCP7WUYETLwkXD2BZbx0sjOUiRNbHXQmYm7c0B4Mp2f2m-C-"
"hAzBdlPGDjeNP1PiFZiWFtDRGuskW4qGrUoFCSWZx5vAyfOFjuRN2ydst7"
"geoD32_8zY-pYyVzQ.HWN2Hq8sLnFWT_XKU20MpW";
encrypted1_signed2 = "eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0.."
"4lW9e06wrl3DMuNs.-"
"wmcLlYUPuGGWKPIimR3j66Y15yarBpaF75g07Q23epRmYO7NL"
"Gvwt7tiGYJGxqh_6f9SHJDK7wMYR4GsP6W4AZWZOurCxY_"
"PdwZWnrPit11s7zi77fFEqz3b3g2scYbZd9PfN-KJ4Ol0g."
"oH5VdKxGZanSP0H0-XuGtg";
encrypted1_signed2_corrupt_sig =
"eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..f-t6lZhrSTUwQUhe."
"1fIV8PJSgpoOgiR-0yVlkzzcgfpEghVnevJuYbO06DA9x-"
"X47wzpoIPIX941fXmSFIItQDiF1t9lVxfIhnJ46JYuYlOCwkn_"
"6vUIQKpCbGbUNwvrPo8aF8g75T8FMKmeqjjawmby2nwawQ."
"Sm6gRVXw7RhI7NI2hfIglg";
encrypted1_signed2_corrupt_hdr =
"eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..0yJnmp9r_YW8am-4."
"Qe6GoANyIV3ET_bb4Npr-QguqGzzTNrm_"
"iWvx5iYchUr8HL1Qv8jWQq8FOeuNHhbdNW0huKhH_rjfNA_"
"bNHN6nUGmHmeSkhWMKPsf2JnrDZIdcCN7uXVxhKeMXe5TGZ6sY5AdF7lX8Ufcg."
"auXeS-tKPnxFj-iUYULfOQ";
encrypted1_signed2_corrupt_payload =
"eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0..eeCTIS3jEdKO45ao.PM_"
"PHuxN-zPZ9gNVLgpuXZl5dXCTCTQedYCj_"
"QBoOrKhg93A1QOrj1NcHt0NSspDkJ9PpVnJG6T1nzKPIZsdWHHjse33xqs4HNbDW2y"
"bBMuZFQ_S9eEDfEVusEcls0cg0pcrtemeJ8fb.NDWKfDRNgbl5anMH1NCbIw";
}
static void teardown(void)
{
json_decref(payload1);
json_decref(payload2);
oauth2_shutdown(_log);
}
static void *faulty_alloc(size_t amt)
{
return NULL;
}
START_TEST(test_hash_bytes)
{
bool rc;
const char *src = "abc";
unsigned char src_hash[32] = {
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40,
0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17,
0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad};
unsigned int dst_len;
unsigned char *dst;
oauth2_mem_alloc_fn_t alloc_save;
dst = NULL;
dst_len = 0;
rc = oauth2_jose_hash_bytes(_log, OAUTH2_JOSE_OPENSSL_ALG_SHA256,
(const unsigned char *)src, strlen(src),
&dst, &dst_len);
ck_assert_int_eq(rc, true);
ck_assert_int_eq(sizeof(src_hash), dst_len);
oauth2_mem_free(dst);
dst = NULL;
dst_len = 0;
alloc_save = oauth2_mem_get_alloc();
oauth2_mem_set_alloc_funcs(faulty_alloc, oauth2_mem_get_realloc(),
oauth2_mem_get_dealloc());
rc = oauth2_jose_hash_bytes(_log, OAUTH2_JOSE_OPENSSL_ALG_SHA256,
(const unsigned char *)src, strlen(src),
&dst, &dst_len);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
dst = (unsigned char *)oauth2_strdup("test-faulty-alloc");
ck_assert_ptr_eq(dst, NULL);
oauth2_mem_set_alloc_funcs(alloc_save, oauth2_mem_get_realloc(),
oauth2_mem_get_dealloc());
dst = NULL;
dst_len = 0;
rc = oauth2_jose_hash_bytes(_log, "non-existing-digest",
(const unsigned char *)src, strlen(src),
&dst, &dst_len);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
rc = oauth2_jose_hash_bytes(_log, OAUTH2_JOSE_OPENSSL_ALG_SHA256, NULL,
0, &dst, &dst_len);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
rc = oauth2_jose_hash_bytes(_log, OAUTH2_JOSE_OPENSSL_ALG_SHA256, NULL,
0, NULL, NULL);
ck_assert_int_eq(rc, false);
}
END_TEST
START_TEST(test_jwk_create_symmetric)
{
bool rc;
const char *client_secret = "abc";
oauth2_jose_jwk_t *jwk = NULL;
rc = oauth2_jose_jwk_create_symmetric(_log, client_secret, NULL, &jwk);
ck_assert_int_eq(rc, true);
oauth2_jose_jwk_release(jwk);
rc = oauth2_jose_jwk_create_symmetric(
_log, client_secret, OAUTH2_JOSE_OPENSSL_ALG_SHA256, &jwk);
ck_assert_int_eq(rc, true);
oauth2_jose_jwk_release(jwk);
jwk = NULL;
rc = oauth2_jose_jwk_create_symmetric(
_log, NULL, OAUTH2_JOSE_OPENSSL_ALG_SHA256, &jwk);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(jwk, NULL);
rc = oauth2_jose_jwk_create_symmetric(_log, NULL, NULL, &jwk);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(jwk, NULL);
rc = oauth2_jose_jwk_create_symmetric(_log, client_secret,
"bogus-algorithm", &jwk);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(jwk, NULL);
}
END_TEST
START_TEST(test_jwt_encrypt)
{
bool rc;
char *cser = NULL;
rc = oauth2_jose_jwt_encrypt(_log, secret1, payload1, &cser);
ck_assert_int_eq(rc, true);
// TODO: this fails intermittently in docker-valgrind (only on first
// runs...) !?
ck_assert(strncmp(cser, serialized_hdr, strlen(serialized_hdr)) == 0);
oauth2_mem_free(cser);
cser = NULL;
rc = oauth2_jose_jwt_encrypt(_log, secret2, payload2, &cser);
ck_assert_int_eq(rc, true);
ck_assert(strncmp(cser, serialized_hdr, strlen(serialized_hdr)) == 0);
oauth2_mem_free(cser);
cser = NULL;
rc = oauth2_jose_jwt_encrypt(_log, NULL, payload1, &cser);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(cser, NULL);
rc = oauth2_jose_jwt_encrypt(_log, secret1, NULL, &cser);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(cser, NULL);
rc = oauth2_jose_jwt_encrypt(_log, secret1, payload1, NULL);
ck_assert_int_eq(rc, false);
}
END_TEST
START_TEST(test_jwt_decrypt)
{
bool rc;
char *cser = NULL;
json_t *result = NULL;
rc = oauth2_jose_jwt_decrypt(_log, secret1, encrypted1, &result);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(result, NULL);
cser = json_dumps(payload1, JSON_PRESERVE_ORDER | JSON_COMPACT);
ck_assert_str_eq(cser, s_payload1);
oauth2_mem_free(cser);
json_decref(result);
result = NULL;
rc = oauth2_jose_jwt_decrypt(_log, secret1, encrypted1_corrupt_tag,
&result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc =
oauth2_jose_jwt_decrypt(_log, secret1, encrypted1_signed2, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(_log, secret1,
encrypted1_signed2_corrupt_sig, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(_log, secret1,
encrypted1_signed2_corrupt_hdr, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(
_log, secret1, encrypted1_signed2_corrupt_payload, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(_log, secret2, encrypted1, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(_log, NULL, encrypted1, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(_log, secret1, NULL, &result);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(result, NULL);
rc = oauth2_jose_jwt_decrypt(_log, secret1, encrypted1, NULL);
ck_assert_int_eq(rc, false);
}
END_TEST
static char *get_jwks_uri_json =
"{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"k1\",\"use\":\"sig\",\"n\":"
"\"hKvkosOyK33gznaRCNgakMLE2GHS5_7K34oqZRsAWC-7aC420eJNL2z_"
"8Z7ouWXpJNZ2YHQcqxPe4UZGtiDiFYLdDbQPrCDiTpuRYybe1UmZJ3Kk5fBx9yXKU0zbdSKYPE"
"eq1w5Fi7rt46YkZ6qwv3Yixo7eTxbglezJOx_YcS5sfXxcwBU1nYbGU_"
"MgrBXAfy1Hea5tcUSPot-BTMcuj_doHLT_sEm4AZwaZiLhMiqfI-"
"J6Gv5Hg6aBTXpYv50DEdcoZzkabMHxjHICS9w2FGWAzMt_"
"AvW4ISlbAxlBroXhTEXC6GIJwoDTskuPlCO4CVa3axh0s1D49JFJoBYasw\",\"e\":"
"\"AQAB\",\"x5c\":["
"\"MIIDSjCCAjKgAwIBAgIGAVvvqweOMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNVBAYTAlVTMQsw"
"CQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMQ0wCwYDVQQKEwRQaW5nMQwwCgYDVQQLEwNEZX"
"YxHDAaBgNVBAMTE0NvbmZpZyBTaWduaW5nIENlcnQwHhcNMTcwNTEwMDAwMzM0WhcNMzIwNTA2"
"MDAwMzM0WjBmMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ08xDzANBgNVBAcTBkRlbnZlcjENMA"
"sGA1UEChMEUGluZzEMMAoGA1UECxMDRGV2MRwwGgYDVQQDExNDb25maWcgU2lnbmluZyBDZXJ0"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhKvkosOyK33gznaRCNgakMLE2GHS5/"
"7K34oqZRsAWC+7aC420eJNL2z/"
"8Z7ouWXpJNZ2YHQcqxPe4UZGtiDiFYLdDbQPrCDiTpuRYybe1UmZJ3Kk5fBx9yXKU0zbdSKYPE"
"eq1w5Fi7rt46YkZ6qwv3Yixo7eTxbglezJOx/YcS5sfXxcwBU1nYbGU/"
"MgrBXAfy1Hea5tcUSPot+BTMcuj/doHLT/"
"sEm4AZwaZiLhMiqfI+J6Gv5Hg6aBTXpYv50DEdcoZzkabMHxjHICS9w2FGWAzMt/"
"AvW4ISlbAxlBroXhTEXC6GIJwoDTskuPlCO4CVa3axh0s1D49JFJoBYaswIDAQABMA0GCSqGSI"
"b3DQEBCwUAA4IBAQBCYXguSAbrwHw9g+UXuWzgj6b3jN+"
"OAAQUuvpnY0KrNBentCgC3ualfgieB2c0cyLXBFTNDzMCVb2eB+f66/"
"ZRQC8W6DTc5aCE3nTH8tSzbMLwwlMnQelkQMF4LZ9NZmrubVT2IYZ+"
"hzwHhvVOHSQ6kqjQHXWcZ30VEbe6EV47LC1M78v+UX3CP+"
"lOcovbyHl9J4VqQLKlxajr0QAqHnETkr84fI54RE2kSkWVuWp36VNY39Sl0/"
"yEmnouFbV0UBMZck7gMNseCtwSYdkwls/LDFEp9D4rF1gHRlSBRskNc/"
"NaasTSX4JpNf+xakm7yePtuWyAY/"
"fQ7ETSPMJdVEaL\"],\"x5t\":\"31YdH_bv2Hlg89wmwBphxJZaK64\"}]}";
static char *jwks_uri_path = "/jwks_uri";
static char *oauth2_check_jose_serve_get(const char *request)
{
if (strncmp(request, jwks_uri_path, strlen(jwks_uri_path)) == 0) {
return oauth2_strdup(get_jwks_uri_json);
}
return oauth2_strdup("problem");
}
START_TEST(test_jwks_resolve_uri)
{
oauth2_cfg_token_verify_t *verify = NULL;
oauth2_jose_jwk_list_t *list = NULL;
const char *rv = NULL;
bool refresh = false;
char *url = NULL;
oauth2_jose_jwt_verify_ctx_t *ptr = NULL;
url = oauth2_stradd(NULL, oauth2_check_http_base_url(), jwks_uri_path,
NULL);
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "jwks_uri", url,
"ssl_verify=false");
ck_assert_ptr_eq(rv, NULL);
ptr = (oauth2_jose_jwt_verify_ctx_t *)verify->ctx->ptr;
list = ptr->jwks_provider->resolve(_log, ptr->jwks_provider, &refresh);
ck_assert_ptr_ne(list, NULL);
oauth2_jose_jwk_list_free(_log, list);
oauth2_mem_free(url);
oauth2_cfg_token_verify_free(_log, verify);
}
END_TEST
START_TEST(test_jwk_resolve_plain)
{
oauth2_cfg_token_verify_t *verify = NULL;
oauth2_jose_jwk_list_t *list = NULL;
const char *rv = NULL;
bool refresh = false;
oauth2_jose_jwt_verify_ctx_t *ptr = NULL;
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "plain",
"mysecret", "kid=mykid");
ck_assert_ptr_eq(rv, NULL);
ptr = (oauth2_jose_jwt_verify_ctx_t *)verify->ctx->ptr;
list = ptr->jwks_provider->resolve(_log, ptr->jwks_provider, &refresh);
ck_assert_ptr_ne(list, NULL);
oauth2_jose_jwk_list_free(_log, list);
oauth2_cfg_token_verify_free(_log, verify);
}
END_TEST
START_TEST(test_jwt_verify)
{
bool rc = false;
json_t *json_payload = NULL;
char *s_payload = NULL;
char *jwt = NULL;
oauth2_jose_jwk_t *jwk = NULL;
char *rv = NULL;
oauth2_cfg_token_verify_t *verify = NULL;
rc = oauth2_jose_jwt_verify(_log, NULL, jwt, &json_payload, &s_payload);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(json_payload, NULL);
ck_assert_ptr_eq(s_payload, NULL);
rc = oauth2_jose_jwk_create_symmetric(_log, "my_good_secret", NULL,
&jwk);
ck_assert_int_eq(rc, true);
jwt = oauth2_jwt_create(_log, jwk->jwk, CJOSE_HDR_ALG_HS256, "my_iss",
"my_sub", "my_client_id", "my_aud", 60, true,
true);
ck_assert_ptr_ne(jwt, NULL);
oauth2_jose_jwk_release(jwk);
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "plain", "my_wrong_secret", "kid=my_wrong_kid1");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, false);
oauth2_cfg_token_verify_free(_log, verify);
verify = NULL;
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "plain", "my_good_secret",
"kid=my_good_kid&expiry=1&verify.iat=required");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(json_payload, NULL);
json_decref(json_payload);
oauth2_cfg_token_verify_free(_log, verify);
verify = NULL;
sleep(3);
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "plain", "my_good_secret",
"kid=my_good_kid&expiry=1&verify.iat=required&verify.iat.slack_"
"before=2");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, false);
oauth2_cfg_token_verify_free(_log, verify);
verify = NULL;
oauth2_mem_free(jwt);
}
END_TEST
Suite *oauth2_check_jose_suite()
{
Suite *s = suite_create("jose");
TCase *c = tcase_create("core");
liboauth2_check_register_http_callbacks(
oauth2_check_http_base_path(), oauth2_check_jose_serve_get, NULL);
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_hash_bytes);
tcase_add_test(c, test_jwk_create_symmetric);
tcase_add_test(c, test_jwt_encrypt);
tcase_add_test(c, test_jwt_decrypt);
tcase_add_test(c, test_jwks_resolve_uri);
tcase_add_test(c, test_jwk_resolve_plain);
tcase_add_test(c, test_jwt_verify);
tcase_set_timeout(c, 8);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_liboauth2.c 0000664 0000000 0000000 00000023004 14161030125 0017552 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../include/oauth2/log.h"
#include "../include/oauth2/mem.h"
#include "util_int.h"
//#include
static int http_server_port = 8888;
static int http_server_signal_delivered = 0;
#define HTTP_SERVER_BUFSIZE 8096
#define HTTP_SERVER_SIGNUM SIGCONT
static void http_server_signal_handler(int signum)
{
http_server_signal_delivered = signum;
}
static void http_server_error(oauth2_log_t *log, int type, int socket_fd)
{
ssize_t rc;
static char buf[HTTP_SERVER_BUFSIZE + 1];
char *status = "500 Internal Server Error";
if (type == 400)
status = "400 Bad Request";
else if (type == 403)
status = "403 Forbidden";
else if (type == 404)
status = "404 Not Found";
else if (type == 405)
status = "405 Method Not Allowed";
else if (type == 406)
status = "406 Not Acceptable";
sprintf(buf, "HTTP/1.1 %s\nContent-Length: 0\nConnection: close\n\n",
status);
rc = write(socket_fd, buf, strlen(buf));
(void)rc;
}
struct {
char *ext;
char *filetype;
} extensions[] = {{"html", "text/html"}, {"json", "application/json"}, {0, 0}};
typedef struct http_serve_routing_t {
char *path;
http_serve_callback_get_t *callback_get;
http_serve_callback_get_t *callback_post;
struct http_serve_routing_t *next;
} http_serve_routing_t;
static http_serve_routing_t *http_serve_routing_table = NULL;
void liboauth2_check_register_http_callbacks(
const char *path, http_serve_callback_get_t *cb_get,
http_serve_callback_post_t *cb_post)
{
http_serve_routing_t *ptr = NULL;
if (http_serve_routing_table == NULL) {
http_serve_routing_table =
oauth2_mem_alloc(sizeof(http_serve_routing_t));
ptr = http_serve_routing_table;
} else {
for (ptr = http_serve_routing_table; ptr->next; ptr = ptr->next)
;
ptr->next = oauth2_mem_alloc(sizeof(http_serve_routing_t));
ptr = ptr->next;
}
ptr->path = oauth2_strdup(path);
ptr->callback_get = cb_get;
ptr->callback_post = cb_post;
ptr->next = NULL;
}
static void liboauth2_check_cleanup_http_callbacks()
{
http_serve_routing_t *ptr = NULL;
while ((ptr = http_serve_routing_table)) {
http_serve_routing_table = http_serve_routing_table->next;
oauth2_mem_free(ptr->path);
oauth2_mem_free(ptr);
}
}
static void http_server_process(oauth2_log_t *log, int fd, int hit)
{
int j, file_fd, buflen;
long i, ret, len;
char *fstr;
static char buffer[HTTP_SERVER_BUFSIZE + 1];
static char outbuf[HTTP_SERVER_BUFSIZE + 1];
ssize_t rc;
char *response = NULL;
http_serve_routing_t *ptr = NULL;
ret = read(fd, buffer, HTTP_SERVER_BUFSIZE);
if (ret == 0 || ret == -1) {
http_server_error(log, 400, fd);
return;
}
if (ret > 0 && ret < HTTP_SERVER_BUFSIZE)
buffer[ret] = 0;
else
buffer[0] = 0;
for (i = 0; i < ret; i++)
if (buffer[i] == '\r' || buffer[i] == '\n')
buffer[i] = '*';
oauth2_debug(log, "request: %s %d", buffer, hit);
/*
if (strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4)) {
http_server_error(log, 405, fd);
return;
}
*/
for (i = 4; i < HTTP_SERVER_BUFSIZE; i++) {
if (buffer[i] == ' ') {
buffer[i] = 0;
break;
}
}
for (j = 0; j < i - 1; j++)
if (buffer[j] == '.' && buffer[j + 1] == '.') {
http_server_error(log, 403, fd);
return;
}
if (!strncmp(&buffer[0], "GET /\0", 6) ||
!strncmp(&buffer[0], "get /\0", 6))
(void)strcpy(buffer, "GET /index.html");
buflen = strlen(buffer);
fstr = (char *)0;
for (i = 0; extensions[i].ext != 0; i++) {
len = strlen(extensions[i].ext);
if (!strncmp(&buffer[buflen - len], extensions[i].ext, len)) {
fstr = extensions[i].filetype;
break;
}
}
if (fstr == 0) {
// http_server_error(log, 406, fd);
// return;
fstr = "application/json";
}
for (ptr = http_serve_routing_table; ptr; ptr = ptr->next) {
len = strlen(ptr->path);
if (strncmp(&buffer[0], "GET", 3) == 0) {
if (strncmp(&buffer[4], ptr->path, len) == 0) {
if (ptr->callback_get)
response =
ptr->callback_get(&buffer[4 + len]);
}
}
if (strncmp(&buffer[0], "POST", 4) == 0) {
if (strncmp(&buffer[5], ptr->path, len) == 0) {
if (ptr->callback_post)
response = ptr->callback_post(
&buffer[5 + len]);
}
}
if (response) {
sprintf(outbuf,
"HTTP/1.1 200\nContent-Length: "
"%zu\nConnection: close\n\n",
strlen(response));
rc = write(fd, outbuf, strlen(outbuf));
rc = write(fd, response, strlen(response));
(void)rc;
oauth2_mem_free(response);
return;
}
}
if ((file_fd = open(&buffer[5], O_RDONLY)) == -1) {
http_server_error(log, 404, fd);
return;
}
oauth2_debug(log, "SEND: %s, %d", &buffer[5], hit);
len = (long)lseek(file_fd, (off_t)0, SEEK_END);
(void)lseek(file_fd, (off_t)0, SEEK_SET);
(void)sprintf(buffer,
"HTTP/1.1 200 OK\nServer: "
"libmodauth2/1.0\nContent-Length: "
"%ld\nConnection: close\nContent-Type: %s\n\n",
len, fstr);
oauth2_debug(log, "Header: %s, %d", buffer, hit);
rc = write(fd, buffer, strlen(buffer));
(void)rc;
while ((ret = read(file_fd, buffer, HTTP_SERVER_BUFSIZE)) > 0) {
rc = write(fd, buffer, ret);
(void)rc;
}
}
// static sem_t *sema = NULL;
pid_t http_server_spawn()
{
int listenfd, socketfd, hit;
static struct sockaddr_in serv_addr;
static struct sockaddr_in cli_addr;
fd_set read_fd_set;
socklen_t length;
pid_t pid = fork();
if (pid != 0) {
return pid;
}
struct sigaction action;
action.sa_flags = 0;
action.sa_handler = http_server_signal_handler;
sigemptyset(&action.sa_mask);
sigaction(HTTP_SERVER_SIGNUM, &action, NULL);
oauth2_log_t *log = oauth2_log_init(OAUTH2_LOG_TRACE1, NULL);
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
oauth2_error(log, "socket failed");
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(http_server_port);
if (bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) <
0)
oauth2_error(log, "bind failed");
if (listen(listenfd, 64) < 0)
oauth2_error(log, "listen failed");
FD_ZERO(&read_fd_set);
FD_SET(listenfd, &read_fd_set);
// sem_post(sema);
for (hit = 1;; hit++) {
if (select(FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
if (http_server_signal_delivered == HTTP_SERVER_SIGNUM)
break;
oauth2_error(log, "select failed: %s", strerror(errno));
}
if (FD_ISSET(listenfd, &read_fd_set)) {
length = sizeof(cli_addr);
if ((socketfd =
accept(listenfd, (struct sockaddr *)&cli_addr,
&length)) <= 0) {
oauth2_error(log, "accept failed: %s, %d",
strerror(errno), socketfd);
continue;
}
if (fork() == 0) {
http_server_process(log, socketfd, hit);
close(socketfd);
oauth2_log_free(log);
exit(0);
}
}
}
close(listenfd);
oauth2_log_free(log);
exit(0);
}
int main(void)
{
int n_failed;
SRunner *sr = srunner_create(suite_create("liboauth2"));
// srunner_set_fork_status(sr, CK_NOFORK);
srunner_add_suite(sr, oauth2_check_version_suite());
srunner_add_suite(sr, oauth2_check_mem_suite());
srunner_add_suite(sr, oauth2_check_log_suite());
srunner_add_suite(sr, oauth2_check_cfg_suite());
srunner_add_suite(sr, oauth2_check_util_suite());
srunner_add_suite(sr, oauth2_check_ipc_suite());
srunner_add_suite(sr, oauth2_check_cache_suite());
srunner_add_suite(sr, oauth2_check_jose_suite());
srunner_add_suite(sr, oauth2_check_http_suite());
srunner_add_suite(sr, oauth2_check_proto_suite());
srunner_add_suite(sr, oauth2_check_oauth2_suite());
srunner_add_suite(sr, oauth2_check_openidc_suite());
#ifdef HAVE_APACHE
srunner_add_suite(sr, oauth2_check_apache_suite());
#endif
#ifdef HAVE_NGINX
srunner_add_suite(sr, oauth2_check_nginx_suite());
#endif
// sema = sem_open ("sema", O_CREAT | O_EXCL, 0644, 0);
pid_t pid = http_server_spawn();
// sleep(1);
// sem_wait(sema);
// srunner_run_all(sr, CK_ENV);
srunner_run_all(sr, CK_VERBOSE);
n_failed = srunner_ntests_failed(sr);
srunner_free(sr);
kill(pid, HTTP_SERVER_SIGNUM);
waitpid(pid, NULL, 0);
// http_serve_routing_t *ptr = NULL;
// while (http_serve_routing_table) {
// ptr = http_serve_routing_table;
// http_serve_routing_table =
// http_serve_routing_table->next; oauth2_mem_free(ptr);
// }
// sem_unlink("sema");
oauth2_check_jose_cleanup();
oauth2_check_http_cleanup();
oauth2_check_proto_cleanup();
oauth2_check_oauth2_cleanup();
oauth2_check_openidc_cleanup();
liboauth2_check_cleanup_http_callbacks();
return (n_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
liboauth2-1.4.4/test/check_liboauth2.h 0000664 0000000 0000000 00000016154 14161030125 0017567 0 ustar 00root root 0000000 0000000 #ifndef _OAUTH2_CHECK_LIBOAUTH2_H_
#define _OAUTH2_CHECK_LIBOAUTH2_H_
/***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
//#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
//#pragma GCC diagnostic ignored
//"-Wincompatible-pointer-types-discards-qualifiers"
//#pragma GCC diagnostic ignored "-Wpointer-sign"
#include
Suite *oauth2_check_version_suite();
Suite *oauth2_check_mem_suite();
Suite *oauth2_check_log_suite();
Suite *oauth2_check_cfg_suite();
Suite *oauth2_check_util_suite();
Suite *oauth2_check_ipc_suite();
Suite *oauth2_check_cache_suite();
Suite *oauth2_check_jose_suite();
void oauth2_check_jose_cleanup();
Suite *oauth2_check_http_suite();
void oauth2_check_http_cleanup();
Suite *oauth2_check_proto_suite();
void oauth2_check_proto_cleanup();
Suite *oauth2_check_oauth2_suite();
void oauth2_check_oauth2_cleanup();
Suite *oauth2_check_openidc_suite();
void oauth2_check_openidc_cleanup();
#ifdef HAVE_APACHE
Suite *oauth2_check_apache_suite();
#endif
#ifdef HAVE_NGINX
Suite *oauth2_check_nginx_suite();
#endif
typedef char *(http_serve_callback_get_t)(const char *request);
typedef char *(http_serve_callback_post_t)(const char *request);
void liboauth2_check_register_http_callbacks(
const char *path, http_serve_callback_get_t *get_cb,
http_serve_callback_post_t *post_cb);
#define _ck_assert_bin(X, OP, Y, LEN) \
do { \
const uint8_t *_chk_x = (X); \
const uint8_t *_chk_y = (Y); \
const size_t _chk_len = (LEN); \
ck_assert_msg(0 OP memcmp(_chk_x, _chk_y, _chk_len), \
"Assertion '" #X #OP #Y "' failed: " #LEN \
"==%z, " #X "==0x%zx, " #Y "==0x%zx", \
_chk_len, _chk_x, _chk_y); \
} while (0);
#define ck_assert_bin_eq(X, Y, LEN) _ck_assert_bin(X, ==, Y, LEN)
#ifndef _ck_assert_ptr
#define _ck_assert_ptr(X, OP, Y) \
do { \
const void *_ck_x = (X); \
const void *_ck_y = (Y); \
ck_assert_msg(_ck_x OP _ck_y, \
"Assertion '%s' failed: %s == %#x, %s == %#x", \
#X " " #OP " " #Y, #X, _ck_x, #Y, _ck_y); \
} while (0)
#define ck_assert_ptr_eq(X, Y) _ck_assert_ptr(X, ==, Y)
#define ck_assert_ptr_ne(X, Y) _ck_assert_ptr(X, !=, Y)
#endif
#ifndef _ck_assert_uint
#define _ck_assert_uint(X, OP, Y) \
do { \
uintmax_t _ck_x = (X); \
uintmax_t _ck_y = (Y); \
ck_assert_msg(_ck_x OP _ck_y, \
"Assertion '%s' failed: %s == %ju, %s == %ju", \
#X " " #OP " " #Y, #X, _ck_x, #Y, _ck_y); \
} while (0)
#define ck_assert_uint_eq(X, Y) _ck_assert_uint(X, ==, Y)
#define ck_assert_uint_ne(X, Y) _ck_assert_uint(X, !=, Y)
#endif
#define OAUTH2_CHECK_HTTP_PATHS \
static char *_http_base_path = NULL; \
\
static char *oauth2_check_http_base_path() \
{ \
char *p = NULL, *path = NULL; \
if (_http_base_path == NULL) { \
path = oauth2_strdup(__FILE__); \
p = strrchr(path, '.'); \
if (p) \
*p = '\0'; \
p = path; \
while (*p == '.') { \
p++; \
if (*p == '/') \
p++; \
} \
if (*p == '/') \
p++; \
_http_base_path = oauth2_stradd(NULL, "/", p, NULL); \
oauth2_mem_free(path); \
} \
return _http_base_path; \
} \
\
static char *_http_base_url = NULL; \
\
static char *oauth2_check_http_base_url() \
{ \
if (_http_base_url == NULL) \
_http_base_url = oauth2_stradd( \
NULL, "http://127.0.0.1:8888", \
oauth2_check_http_base_path(), NULL); \
return _http_base_url; \
} \
\
static void oauth2_check_http_base_free() \
{ \
if (_http_base_url != NULL) { \
oauth2_mem_free(_http_base_url); \
_http_base_url = NULL; \
} \
if (_http_base_path != NULL) { \
oauth2_mem_free(_http_base_path); \
_http_base_path = NULL; \
} \
}
#endif /* _OAUTH2_CHECK_LIBOAUTH2_H_ */
liboauth2-1.4.4/test/check_log.c 0000664 0000000 0000000 00000005143 14161030125 0016446 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/log.h"
#include
#include
static oauth2_log_t *_log = 0;
static void setup(void)
{
// for coverage
oauth2_log_free(NULL);
_log = oauth2_log_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_log_free(_log);
}
START_TEST(test_log)
{
// mostly to complete coverage
// TODO: could return bytes written from oauth2_log statements
oauth2_debug(NULL, NULL);
// TOOD: could return bool from oauth2_log_sink_add
oauth2_log_sink_add(_log, &oauth2_log_sink_stderr);
oauth2_info(_log, NULL);
oauth2_info(_log, "");
oauth2_log_sink_level_set(&oauth2_log_sink_stderr, OAUTH2_LOG_ERROR);
}
END_TEST
static int check_log_test_sink_callback_dummy = 0;
static void
check_log_test_sink_callback(oauth2_log_sink_t *sink, const char *filename,
unsigned long line, const char *function,
oauth2_log_level_t level, const char *msg)
{
check_log_test_sink_callback_dummy = 1;
}
START_TEST(test_sink)
{
char *dummy = "dummy";
oauth2_log_sink_t *sink = oauth2_log_sink_create(
OAUTH2_LOG_TRACE1, check_log_test_sink_callback, dummy);
oauth2_log_sink_add(_log, sink);
ck_assert_ptr_eq(oauth2_log_sink_callback_get(sink),
check_log_test_sink_callback);
ck_assert_ptr_eq(oauth2_log_sink_ctx_get(sink), dummy);
check_log_test_sink_callback_dummy = 0;
oauth2_info(_log, "");
ck_assert_int_eq(check_log_test_sink_callback_dummy, 1);
}
END_TEST
Suite *oauth2_check_log_suite()
{
Suite *s = suite_create("log");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_log);
tcase_add_test(c, test_sink);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_mem.c 0000664 0000000 0000000 00000011675 14161030125 0016452 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/mem.h"
#include
#include
#include
static oauth2_mem_alloc_fn_t _save_alloc = NULL;
static oauth2_mem_realloc_fn_t _save_realloc = NULL;
static oauth2_mem_dealloc_fn_t _save_dealloc = NULL;
static void *test_alloc(size_t amt)
{
return malloc(amt);
}
static void *test_realloc(void *ptr, size_t amt)
{
return realloc(ptr, amt);
}
static void test_dealloc(void *ptr)
{
free(ptr);
}
static void test_mem_functions_set()
{
_save_alloc = oauth2_mem_get_alloc();
_save_realloc = oauth2_mem_get_realloc();
_save_dealloc = oauth2_mem_get_dealloc();
oauth2_mem_set_alloc_funcs(test_alloc, test_realloc, test_dealloc);
}
static void test_mem_functions_reset()
{
oauth2_mem_set_alloc_funcs(_save_alloc, _save_realloc, _save_dealloc);
_save_alloc = NULL;
_save_realloc = NULL;
_save_dealloc = NULL;
}
static void *test_alloc3(size_t amt, const char *file, int line)
{
return malloc(amt);
}
static void *test_realloc3(void *ptr, size_t amt, const char *file, int line)
{
return realloc(ptr, amt);
}
static void test_dealloc3(void *ptr, const char *file, int line)
{
free(ptr);
}
static oauth2_mem_alloc3_fn_t _save_alloc3 = NULL;
static oauth2_mem_realloc3_fn_t _save_realloc3 = NULL;
static oauth2_mem_dealloc3_fn_t _save_dealloc3 = NULL;
static void test_mem_functions_set3()
{
_save_alloc3 = oauth2_mem_get_alloc3();
_save_realloc3 = oauth2_mem_get_realloc3();
_save_dealloc3 = oauth2_mem_get_dealloc3();
oauth2_mem_set_alloc_ex_funcs(test_alloc3, test_realloc3,
test_dealloc3);
}
/*
* TODO: why does this result in a timeout?
* probably we can call this only once anyhow, but would it affect the
other check_util tests?
* perhaps separate it out in a different suite then?
static void test_mem_functions_reset3() {
cjose_set_alloc_ex_funcs(_save_alloc3, _save_realloc3, _save_dealloc3);
oauth2_mem_set_alloc_ex_funcs(_save_alloc3, _save_realloc3,
_save_dealloc3);
_save_alloc3 = NULL;
_save_realloc3 = NULL;
_save_dealloc3 = NULL;
}
*/
static void setup(void)
{
// provide coverage for oauth2_mem_calloc_callback
// NB: the setup for cURL can only be initialized once and stays this
// way
test_mem_functions_set();
CURL *curl1 = NULL, *curl2 = NULL;
curl1 = curl_easy_init();
curl2 = curl_easy_duphandle(curl1);
curl_easy_cleanup(curl2);
curl_easy_cleanup(curl1);
}
static void teardown(void)
{
}
START_TEST(test_mem)
{
void *ptr = NULL;
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
test_mem_functions_set();
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
ptr = oauth2_mem_alloc(8);
ptr = oauth2_mem_get_realloc()(ptr, 8);
oauth2_mem_free(ptr);
test_mem_functions_reset();
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
test_mem_functions_set3();
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
ptr = oauth2_mem_alloc(8);
ptr = oauth2_mem_get_realloc()(ptr, 8);
oauth2_mem_free(ptr);
}
END_TEST
Suite *oauth2_check_mem_suite()
{
Suite *s = suite_create("mem");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_mem);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_nginx.c 0000664 0000000 0000000 00000010311 14161030125 0017001 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/mem.h"
#include "oauth2/nginx.h"
#include
static oauth2_log_t *_log = 0;
static ngx_http_request_t *_request = NULL;
static char *_url = "https://example.org:8080/some?jan=piet";
static ngx_str_t _uri = ngx_string("/some");
static ngx_str_t _method_name = ngx_string("POST");
static ngx_str_t _args = ngx_string("param=value");
static void setup(void)
{
ngx_table_elt_t *h = NULL;
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
_request = oauth2_mem_alloc(sizeof(ngx_http_request_t));
_request->schema_start = (u_char *)_url;
_request->schema_end = (u_char *)_url + strlen("https");
_request->host_start = (u_char *)_url + strlen("https://");
_request->host_end = (u_char *)_url + strlen("https://example.org");
_request->port_start = (u_char *)_url + strlen("https://example.org:");
_request->port_end =
(u_char *)_url + strlen("https://example.org:8080");
_request->uri = _uri;
_request->method_name = _method_name;
_request->args = _args;
_request->pool = ngx_create_pool(1024, NULL);
_request->connection = oauth2_mem_alloc(sizeof(ngx_connection_t));
_request->connection->log = NULL;
ngx_list_init(&_request->headers_out.headers, _request->pool, 20,
sizeof(ngx_table_elt_t));
ngx_list_init(&_request->headers_in.headers, _request->pool, 20,
sizeof(ngx_table_elt_t));
h = ngx_list_push(&_request->headers_in.headers);
h->hash = ngx_hash(ngx_hash(ngx_hash('H', 'o'), 's'), 't');
h->key.data = (u_char *)oauth2_strdup("Host");
h->key.len = sizeof("Host") - 1;
h->lowcase_key = (u_char *)"host";
h->value.data = (u_char *)oauth2_strdup("example.org");
h->value.len = sizeof("example.org") - 1;
}
static void list_free(ngx_list_t *list)
{
ngx_list_part_t *part;
ngx_table_elt_t *h;
ngx_uint_t i;
part = &list->part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
oauth2_mem_free(h[i].value.data);
oauth2_mem_free(h[i].key.data);
}
oauth2_mem_free(list->part.elts);
}
static void teardown(void)
{
list_free(&_request->headers_out.headers);
list_free(&_request->headers_in.headers);
oauth2_mem_free(_request->connection);
ngx_destroy_pool(_request->pool);
oauth2_mem_free(_request);
oauth2_shutdown(_log);
}
START_TEST(test_request_context)
{
oauth2_nginx_request_context_t *ctx = NULL;
ctx = oauth2_nginx_request_context_init(_request);
ck_assert_ptr_ne(ctx, NULL);
// TODO: check request values
oauth2_nginx_request_context_free(ctx);
}
END_TEST
START_TEST(test_nginx_http_response_set)
{
ngx_int_t nrc = NGX_ERROR;
oauth2_http_response_t *response = NULL;
response = oauth2_http_response_init(_log);
oauth2_http_response_header_set(_log, response, "Content-Length",
"512");
nrc = oauth2_nginx_http_response_set(_log, response, _request);
ck_assert_int_eq(nrc, NGX_OK);
// TODO: check status code and response headers
oauth2_http_response_free(_log, response);
}
END_TEST
/*
*/
Suite *oauth2_check_nginx_suite()
{
Suite *s = suite_create("nginx");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_request_context);
tcase_add_test(c, test_nginx_http_response_set);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_oauth2.c 0000664 0000000 0000000 00000116705 14161030125 0017076 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/mem.h"
#include "oauth2/oauth2.h"
#include "oauth2_int.h"
#include
#include
static oauth2_log_t *_log = 0;
OAUTH2_CHECK_HTTP_PATHS
void oauth2_check_oauth2_cleanup()
{
oauth2_check_http_base_free();
}
static void setup(void)
{
_log = oauth2_log_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_log_free(_log);
}
START_TEST(test_oauth2_verify_clone)
{
oauth2_cfg_token_verify_t *src = NULL, *dst = NULL;
char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(_log, &src, "plain",
"mysecret", NULL);
ck_assert_ptr_eq(rv, NULL);
dst = oauth2_cfg_token_verify_clone(_log, src);
oauth2_cfg_token_verify_free(_log, dst);
oauth2_cfg_token_verify_free(_log, src);
}
END_TEST
static void test_oauth_auth_clone(oauth2_cfg_endpoint_auth_t *src)
{
bool rc = false;
oauth2_cfg_endpoint_auth_t *dst = NULL;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_nv_list_t *params = NULL;
params = oauth2_nv_list_init(_log);
ctx = oauth2_http_call_ctx_init(_log);
dst = oauth2_cfg_endpoint_auth_clone(_log, src);
ck_assert_ptr_ne(dst, NULL);
rc = oauth2_http_ctx_auth_add(_log, ctx, dst, params);
ck_assert_int_eq(rc, true);
oauth2_nv_list_free(_log, params);
oauth2_cfg_endpoint_auth_free(_log, dst);
oauth2_http_call_ctx_free(_log, ctx);
}
START_TEST(test_oauth2_auth_client_secret_basic)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
char *rv = NULL;
// const char *str = NULL;
// TODO: make the actual call
ctx = oauth2_http_call_ctx_init(_log);
params = oauth2_nv_list_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_basic",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_id", "myclient");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_basic",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_secret", "mysecret");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_basic",
params, NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC,
oauth2_cfg_endpoint_auth_type(auth));
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, NULL);
ck_assert_int_eq(rc, true);
test_oauth_auth_clone(auth);
// str = oauth2_http_call_ctx_hdr_get(_log, ctx, "Authorization");
// ck_assert_ptr_ne(str, NULL);
// ck_assert_str_eq(str, "bXljbGllbnQ6bXlzZWNyZXQ=");
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_oauth2_auth_client_secret_post)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_nv_list_t *post = NULL;
char *rv = NULL;
const char *str = NULL;
ctx = oauth2_http_call_ctx_init(_log);
params = oauth2_nv_list_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_post",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_id", "myclient");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_post",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_secret", "mysecret");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_post",
params, NULL);
ck_assert_ptr_eq(rv, NULL);
post = oauth2_nv_list_init(_log);
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, post);
ck_assert_int_eq(rc, true);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST,
oauth2_cfg_endpoint_auth_type(auth));
str = oauth2_nv_list_get(_log, post, "client_id");
ck_assert_ptr_ne(str, NULL);
ck_assert_str_eq(str, "myclient");
str = oauth2_nv_list_get(_log, post, "client_secret");
ck_assert_ptr_ne(str, NULL);
ck_assert_str_eq(str, "mysecret");
oauth2_nv_list_free(_log, post);
test_oauth_auth_clone(auth);
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_oauth2_auth_client_secret_jwt)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_nv_list_t *post = NULL;
char *rv = NULL;
const char *str = NULL;
ctx = oauth2_http_call_ctx_init(_log);
params = oauth2_nv_list_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_jwt",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_id", "myclient");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_jwt",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_secret", "mysecret");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_jwt",
params, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "aud", "myaud");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_secret_jwt",
params, NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT,
oauth2_cfg_endpoint_auth_type(auth));
post = oauth2_nv_list_init(_log);
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, post);
ck_assert_int_eq(rc, true);
str = oauth2_nv_list_get(_log, post, "client_assertion_type");
ck_assert_ptr_ne(str, NULL);
ck_assert_str_eq(
str, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
str = oauth2_nv_list_get(_log, post, "client_assertion");
ck_assert_ptr_ne(str, NULL);
ck_assert(
strncmp(str, "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.",
strlen("eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.")) == 0);
oauth2_nv_list_free(_log, post);
test_oauth_auth_clone(auth);
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_oauth2_auth_private_key_jwt)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
oauth2_nv_list_t *post = NULL;
char *rv = NULL;
const char *str = NULL;
const char *s_jwk = NULL;
s_jwk =
"{"
"\"kty\" : \"RSA\","
"\"n\": "
"\"ym7jipmB37CgdonwGFVRuZmRfCl3lVh91fmm5CXHcNlUFZNR3D6Q9r63PpGRnfSs"
"X3dOweh8BXd2AJ3mxvcE4z9xH--tA5EaOGI7IVF0Ip_"
"i3flGg85xOADlb8rX3ez1NqkqMVJeeJypKhCCDNfvu_"
"MXSdPLglU969YQF5xKAK8VFRfI6EfxxrZ_3Dvt2CKDV4LTPPJe9KI2_"
"LuLQFBJ3MzlCTVxY6gyaljrWaDq7q5Lt3GB1KYS0Yd8COEQwsclOLm0Tddhg4cle-"
"DfaTMi7xsTZsPKyac5x17Y4N4isHhZULuWHX7o1bs809xcj-_-YCRq6C61je_"
"mzFhuF4pczw\","
"\"e\": \"AQAB\","
"\"d\": "
"\"qvxW_"
"e8DoCnUn8uLHUKTsS1hkXqFI4SHZYFl0jeG6m7ncwHolxvR3ljg9tyGHuFX55sizu7"
"MMuHgrkyxbUWgv0ILD2qmvOiHOTDfuRjP-58JRW0UfqiVQTSgl3jCNRW9WdoxZU-"
"ptD6_NGSVNLwAJsUB2r4mm4PctaMuHINKjp_TnuD-5vfi9Tj88hbqvX_"
"0j8T62ZaLRdERb1KGDM_"
"8bnqQpnLZ0MZQnpLQ8cKIcjj7p0II6pzvqgdO1RqfYx7qG0cbcIRh26rnB9X4rp5Br"
"bvDzKe6NOqacZUcNUmbPzI01-hiT0HgJvV592CBOxt2T31ltQ4wCEdzhQeT3n9_"
"wQ\""
"}";
ctx = oauth2_http_call_ctx_init(_log);
params = oauth2_nv_list_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "private_key_jwt", params,
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "client_id", "myclient");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "private_key_jwt", params,
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "jwk", s_jwk);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "private_key_jwt", params,
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
ck_assert_ptr_ne(auth, NULL);
oauth2_nv_list_add(_log, params, "aud", "myaud");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "private_key_jwt", params,
NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT,
oauth2_cfg_endpoint_auth_type(auth));
post = oauth2_nv_list_init(_log);
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, post);
ck_assert_int_eq(rc, true);
str = oauth2_nv_list_get(_log, post, "client_assertion_type");
ck_assert_ptr_ne(str, NULL);
ck_assert_str_eq(
str, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
str = oauth2_nv_list_get(_log, post, "client_assertion");
ck_assert_ptr_ne(str, NULL);
ck_assert(
strncmp(str, "eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.",
strlen("eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.")) == 0);
oauth2_nv_list_free(_log, post);
test_oauth_auth_clone(auth);
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_oauth2_auth_client_cert)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
char *rv = NULL;
// TODO: make the actual call
ctx = oauth2_http_call_ctx_init(_log);
params = oauth2_nv_list_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_cert", params,
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "cert", "mycert.pem");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_cert", params,
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
oauth2_cfg_endpoint_auth_free(_log, auth);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "key", "mykey.pem");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "client_cert", params,
NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_CLIENT_CERT,
oauth2_cfg_endpoint_auth_type(auth));
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, NULL);
ck_assert_int_eq(rc, true);
test_oauth_auth_clone(auth);
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_oauth2_auth_http_basic)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
char *rv = NULL;
// TODO: make the actual call
ctx = oauth2_http_call_ctx_init(_log);
params = oauth2_nv_list_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
oauth2_nv_list_add(_log, params, "username", "myuser");
oauth2_nv_list_add(_log, params, "password", "mysecret");
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "basic", params, NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_BASIC,
oauth2_cfg_endpoint_auth_type(auth));
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, NULL);
ck_assert_int_eq(rc, true);
test_oauth_auth_clone(auth);
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
START_TEST(test_oauth2_auth_none)
{
bool rc = false;
oauth2_http_call_ctx_t *ctx = NULL;
oauth2_cfg_endpoint_auth_t *auth = NULL;
oauth2_nv_list_t *params = NULL;
char *rv = NULL;
rv = oauth2_cfg_set_endpoint_auth(_log, NULL, "none", NULL, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
ctx = oauth2_http_call_ctx_init(_log);
auth = oauth2_cfg_endpoint_auth_init(_log);
ck_assert_ptr_ne(auth, NULL);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "bogus", NULL, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_cfg_set_endpoint_auth(_log, auth, "none", NULL, NULL);
ck_assert_ptr_eq(rv, NULL);
ck_assert_int_eq(OAUTH2_ENDPOINT_AUTH_NONE,
oauth2_cfg_endpoint_auth_type(auth));
rc = oauth2_http_ctx_auth_add(_log, ctx, auth, NULL);
ck_assert_int_eq(rc, true);
test_oauth_auth_clone(auth);
oauth2_cfg_endpoint_auth_free(_log, auth);
oauth2_nv_list_free(_log, params);
oauth2_http_call_ctx_free(_log, ctx);
}
END_TEST
static char *get_jwks_uri_json =
"{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"k1\",\"use\":\"sig\",\"n\":"
"\"hKvkosOyK33gznaRCNgakMLE2GHS5_7K34oqZRsAWC-7aC420eJNL2z_"
"8Z7ouWXpJNZ2YHQcqxPe4UZGtiDiFYLdDbQPrCDiTpuRYybe1UmZJ3Kk5fBx9yXKU0zbdSKYPE"
"eq1w5Fi7rt46YkZ6qwv3Yixo7eTxbglezJOx_YcS5sfXxcwBU1nYbGU_"
"MgrBXAfy1Hea5tcUSPot-BTMcuj_doHLT_sEm4AZwaZiLhMiqfI-"
"J6Gv5Hg6aBTXpYv50DEdcoZzkabMHxjHICS9w2FGWAzMt_"
"AvW4ISlbAxlBroXhTEXC6GIJwoDTskuPlCO4CVa3axh0s1D49JFJoBYasw\",\"e\":"
"\"AQAB\",\"x5c\":["
"\"MIIDSjCCAjKgAwIBAgIGAVvvqweOMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNVBAYTAlVTMQsw"
"CQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMQ0wCwYDVQQKEwRQaW5nMQwwCgYDVQQLEwNEZX"
"YxHDAaBgNVBAMTE0NvbmZpZyBTaWduaW5nIENlcnQwHhcNMTcwNTEwMDAwMzM0WhcNMzIwNTA2"
"MDAwMzM0WjBmMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ08xDzANBgNVBAcTBkRlbnZlcjENMA"
"sGA1UEChMEUGluZzEMMAoGA1UECxMDRGV2MRwwGgYDVQQDExNDb25maWcgU2lnbmluZyBDZXJ0"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhKvkosOyK33gznaRCNgakMLE2GHS5/"
"7K34oqZRsAWC+7aC420eJNL2z/"
"8Z7ouWXpJNZ2YHQcqxPe4UZGtiDiFYLdDbQPrCDiTpuRYybe1UmZJ3Kk5fBx9yXKU0zbdSKYPE"
"eq1w5Fi7rt46YkZ6qwv3Yixo7eTxbglezJOx/YcS5sfXxcwBU1nYbGU/"
"MgrBXAfy1Hea5tcUSPot+BTMcuj/doHLT/"
"sEm4AZwaZiLhMiqfI+J6Gv5Hg6aBTXpYv50DEdcoZzkabMHxjHICS9w2FGWAzMt/"
"AvW4ISlbAxlBroXhTEXC6GIJwoDTskuPlCO4CVa3axh0s1D49JFJoBYaswIDAQABMA0GCSqGSI"
"b3DQEBCwUAA4IBAQBCYXguSAbrwHw9g+UXuWzgj6b3jN+"
"OAAQUuvpnY0KrNBentCgC3ualfgieB2c0cyLXBFTNDzMCVb2eB+f66/"
"ZRQC8W6DTc5aCE3nTH8tSzbMLwwlMnQelkQMF4LZ9NZmrubVT2IYZ+"
"hzwHhvVOHSQ6kqjQHXWcZ30VEbe6EV47LC1M78v+UX3CP+"
"lOcovbyHl9J4VqQLKlxajr0QAqHnETkr84fI54RE2kSkWVuWp36VNY39Sl0/"
"yEmnouFbV0UBMZck7gMNseCtwSYdkwls/LDFEp9D4rF1gHRlSBRskNc/"
"NaasTSX4JpNf+xakm7yePtuWyAY/"
"fQ7ETSPMJdVEaL\"],\"x5t\":\"31YdH_bv2Hlg89wmwBphxJZaK64\"}]}";
static char *get_jwks_uri_path = "/jwks_uri";
static char *get_eckey_pem =
"-----BEGIN PUBLIC "
"KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXJ+33eo/"
"U6z4PGV0++"
"Qdj1Ev2363\n47i7PxTx8Tr87RYHXIIXLRmH1aIz0OVLt4eM9iXDlDGB6ldBFsM8P61nqQ=="
"\n-----END PUBLIC KEY-----";
static char *get_eckey_url_path = "/ec_key";
static char *introspection_result_json = "{ \"active\": true }";
static char *post_introspection_path = "/introspection";
const char *valid_access_token = "my_valid_token";
static char *metadata_path = "/.well-known/oauth2-configuration";
static char metadata[512];
static char *get_metadata_json()
{
static char *format = "{"
"\"jwks_uri\": \"%s%s\","
"\"introspection_endpoint\": \"%s%s\""
"}";
oauth2_snprintf(metadata, sizeof(metadata), format,
oauth2_check_http_base_url(), get_jwks_uri_path,
oauth2_check_http_base_url(), post_introspection_path);
return metadata;
}
static char *oauth2_check_oauth2_serve_get(const char *request)
{
char *rv = NULL;
if (strncmp(request, get_jwks_uri_path, strlen(get_jwks_uri_path)) ==
0) {
rv = oauth2_strdup(get_jwks_uri_json);
goto end;
}
if (strncmp(request, get_eckey_url_path, strlen(get_eckey_url_path)) ==
0) {
rv = oauth2_strdup(get_eckey_pem);
goto end;
}
if (strncmp(request, metadata_path, strlen(metadata_path)) == 0) {
rv = oauth2_strdup(get_metadata_json());
goto end;
}
rv = oauth2_strdup("problem");
end:
return rv;
}
static char *oauth2_check_oauth2_serve_post(const char *request)
{
oauth2_nv_list_t *params = NULL;
char *data = NULL;
const char *token = NULL;
const char *sep = "****";
char *rv = NULL;
if (strncmp(request, post_introspection_path,
strlen(post_introspection_path)) == 0) {
request += strlen(post_introspection_path) + 5;
data = strstr(request, sep);
if (data == NULL)
goto error;
data += strlen(sep);
if (oauth2_parse_form_encoded_params(_log, data, ¶ms) ==
false)
goto error;
token = oauth2_nv_list_get(_log, params, "token");
if (token == NULL)
goto error;
if ((token) && (strcmp(token, valid_access_token) == 0))
rv = oauth2_strdup(introspection_result_json);
else
rv = oauth2_strdup("{ \"active\": false }");
oauth2_nv_list_free(_log, params);
goto end;
}
error:
rv = oauth2_strdup("problem");
end:
return rv;
}
START_TEST(test_oauth2_verify_jwk)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt =
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0."
"eyJzY29wZSI6W10sImNsaWVudF9pZF9uYW1lIjoicm9fY2xpZW50IiwiYWdpZCI6Im"
"4zak1UazdXSDVVSU9FTWNEZEZPSVR5eFZ2VW1XRHVyIiwiT3JnTmFtZSI6IlBpbmcg"
"SWRlbnRpdHkgQ29ycG9yYXRpb24iLCJjbmYiOnsieDV0I1MyNTYiOiJsNnU5S1VDZ0"
"I2UHpHdklpTS0tWEYwTHF3N1ZYejdvQWtoUkhhbEZqOGkwIn0sIlVzZXJuYW1lIjoi"
"am9lIiwiZXhwIjoxNTQyMTI5NzgzfQ.MUghlaVxy5ij3HODBl6spAA-h6W7D-"
"PoKyhDfR5DnODQqwb5zaqba2pWyJ0d6-4AQfQ6dIe0jfwQeUrPTu2DZLtk3H-"
"noCSjtXrFV_RFNfz9kqdEXwkVZAX8H_ySrYFcAx3Ac9C8bZzjRUM6c4emql-"
"I6T1fVGqO_"
"bVUsWbPmPtNanq3UyqTrlDwQ6weO0ZbLH9tcDpZD4ojNCJjkHa3lvjwYzPNwlAI6a_"
"DGng-7rgrobhOiaAgBAwLhq9fvTtM2MWNmWXmUCymq3nGqG_d_t5i_"
"x7Zf28T3ejzEX-ETefpTENX7BJ57-vQbAeECRTIo_LhzKTaDkiZWpf6JgraQg";
char *jwk = "{\"kty\":\"RSA\",\"kid\":\"k1\",\"use\":\"sig\",\"n\":"
"\"hKvkosOyK33gznaRCNgakMLE2GHS5_7K34oqZRsAWC-7aC420eJNL2z_"
"8Z7ouWXpJNZ2YHQcqxPe4UZGtiDiFYLdDbQPrCDiTpuRYybe1UmZJ3Kk5f"
"Bx9yXKU0zbdSKYPE"
"eq1w5Fi7rt46YkZ6qwv3Yixo7eTxbglezJOx_YcS5sfXxcwBU1nYbGU_"
"MgrBXAfy1Hea5tcUSPot-BTMcuj_doHLT_sEm4AZwaZiLhMiqfI-"
"J6Gv5Hg6aBTXpYv50DEdcoZzkabMHxjHICS9w2FGWAzMt_"
"AvW4ISlbAxlBroXhTEXC6GIJwoDTskuPlCO4CVa3axh0s1D49JFJoBYasw"
"\",\"e\":"
"\"AQAB\"}";
json_t *json_payload = NULL;
const char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "jwk", jwk,
"verify.exp=skip");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_jwk_dpop)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt = "eyJhbGciOiAiUlMyNTYifQ."
"ewogICJzdWIiOiAic29tZW9uZUBleGFtcGxlLmNvbSIsCiAgImlzcyI6IC"
"JodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsCiAgImF1ZCI6ICJodHRw"
"czovL3Jlc291cmNlLmV4YW1wbGUub3JnIiwKICAibmJmIjogMTU2MjI2Mj"
"YxMSwKICAiZXhwIjogMTU2MjI2NjIxNiwKICAiY25mIjogewogICAgImpr"
"dCI6ICIwWmNPQ09SWk5ZeS1EV3BxcTMwalp5SkdIVE4wZDJIZ2xCVjN1aW"
"d1QTRJIgogIH0KfQ.o0j8O6PMQX4R-YPyiyNcdywMzHtUV3leeUn8X9w_"
"CHq_M9UQndIbsNrzClKD8wTm3LuSO3v6wMsdxSC7Ypk3iTMKZCWK66fx9-"
"BIo2d4fbJeC32YbL0FkSmZNzuqIPn1xfxoQ3Lx7_P9vS0k-"
"frefuoWWR8NmVDGXuCxVg1INtoC1QUUB3rCe3PnY8cNNfijlSGSArODffQ"
"XRR0CLULDGV87RgAeRZOHzfDc2lQr9ifLC8FfM6wRPBDCgljE5Ygyfc58x"
"FAIEW_GnVJzRV-WN83PMJ-le-DMDHLnk_"
"YvYbjKRG4Awr5OLm8jD8tc5YlwSZKRo7TOE_pGkctSTf1ang";
char *jwk = "{\"kty\":\"RSA\",\"kid\":\"k1\",\"use\":\"sig\",\"n\":"
"\"ym7jipmB37CgdonwGFVRuZmRfCl3lVh91fmm5CXHcNlUFZNR3D6Q9r63"
"PpGRnfSsX3dOweh8BXd2AJ3mxvcE4z9xH--tA5EaOGI7IVF0Ip_"
"i3flGg85xOADlb8rX3ez1NqkqMVJeeJypKhCCDNfvu_"
"MXSdPLglU969YQF5xKAK8VFRfI6EfxxrZ_3Dvt2CKDV4LTPPJe9KI2_"
"LuLQFBJ3MzlCTVxY6gyaljrWaDq7q5Lt3GB1KYS0Yd8COEQwsclOLm0Tdd"
"hg4cle-DfaTMi7xsTZsPKyac5x17Y4N4isHhZULuWHX7o1bs809xcj-_-"
"YCRq6C61je_mzFhuF4pczw\",\"e\":\"AQAB\"}";
char *dpop =
"eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik"
"VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdC"
"R"
"nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1J"
"E"
"QSIsImNydiI6IlAtMjU2In19."
"eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj"
"oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0"
"Z"
"WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOH0."
"lNhmpAX1WwmpBvwhok4E74kWCiGB"
"NdavjLAeevGy32H3dbF0Jbri69Nm2ukkwb-uyUI4AUg1JSskfWIyo4UCbQ";
json_t *json_payload = NULL;
oauth2_http_request_t *request = NULL;
const char *rv = NULL;
/*
oauth2_nv_list_t *params = NULL;
oauth2_cache_t *cache = NULL;
rc = oauth2_parse_form_encoded_params(
_log, "name=dpop-cache&max_entries=5", ¶ms);
ck_assert_int_eq(rc, true);
cache = oauth2_cache_init(_log, "shm", params);
rc = oauth2_cache_post_config(_log, cache);
ck_assert_int_eq(rc, true);
*/
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "jwk", jwk,
"verify.exp=skip&type=dpop&dpop.iat.verify=skip"); // &dpop.cache=dpop-cache
ck_assert_ptr_eq(rv, NULL);
request = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, request, "https");
oauth2_http_request_hostname_set(_log, request, "resource.example.org");
// oauth2_http_request_port_set();
oauth2_http_request_path_set(_log, request, "/protectedresource");
oauth2_http_request_method_set(_log, request, OAUTH2_HTTP_METHOD_GET);
oauth2_http_request_header_set(_log, request, "DPoP", dpop);
rc = oauth2_token_verify(_log, request, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
// now it should be in the cache
json_decref(json_payload);
rc = oauth2_token_verify(_log, request, verify, jwt, &json_payload);
ck_assert_int_eq(rc, false);
oauth2_http_request_free(_log, request);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
/*
oauth2_nv_list_free(_log, params);
*/
}
END_TEST
START_TEST(test_oauth2_verify_jwks_uri)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt =
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0."
"eyJzY29wZSI6W10sImNsaWVudF9pZF9uYW1lIjoicm9fY2xpZW50IiwiYWdpZCI6Im"
"4zak1UazdXSDVVSU9FTWNEZEZPSVR5eFZ2VW1XRHVyIiwiT3JnTmFtZSI6IlBpbmcg"
"SWRlbnRpdHkgQ29ycG9yYXRpb24iLCJjbmYiOnsieDV0I1MyNTYiOiJsNnU5S1VDZ0"
"I2UHpHdklpTS0tWEYwTHF3N1ZYejdvQWtoUkhhbEZqOGkwIn0sIlVzZXJuYW1lIjoi"
"am9lIiwiZXhwIjoxNTQyMTI5NzgzfQ.MUghlaVxy5ij3HODBl6spAA-h6W7D-"
"PoKyhDfR5DnODQqwb5zaqba2pWyJ0d6-4AQfQ6dIe0jfwQeUrPTu2DZLtk3H-"
"noCSjtXrFV_RFNfz9kqdEXwkVZAX8H_ySrYFcAx3Ac9C8bZzjRUM6c4emql-"
"I6T1fVGqO_"
"bVUsWbPmPtNanq3UyqTrlDwQ6weO0ZbLH9tcDpZD4ojNCJjkHa3lvjwYzPNwlAI6a_"
"DGng-7rgrobhOiaAgBAwLhq9fvTtM2MWNmWXmUCymq3nGqG_d_t5i_"
"x7Zf28T3ejzEX-ETefpTENX7BJ57-vQbAeECRTIo_LhzKTaDkiZWpf6JgraQg";
json_t *json_payload = NULL;
const char *rv = NULL;
char *url = NULL;
url = oauth2_stradd(NULL, oauth2_check_http_base_url(),
get_jwks_uri_path, NULL);
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "jwks_uri", url,
"verify.exp=skip");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
oauth2_mem_free(url);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_eckey_uri)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt =
"eyJ0eXAiOiJKV1QiLCJraWQiOiIwOWQ0ZmExNy0yMjNlLTQwZmEtYjI4MC04OTRlOD"
"QzZDcwMWYiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vYWNjb3VudHMuZ29v"
"Z2xlLmNvbSIsImNsaWVudCI6IjY4NjMwMzIzMzEzMS1wZjA4b3J2YzVyY3BmaXQwdm"
"xxNW82dWg0N3UyZW5mZy5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInNpZ25l"
"ciI6ImFybjphd3M6ZWxhc3RpY2xvYWRiYWxhbmNpbmc6ZXUtY2VudHJhbC0xOjAwNj"
"E3NTk0MDQ5NDpsb2FkYmFsYW5jZXIvYXBwL2JhbGFuY2VyMS8xODE3NThhZTJiMGMz"
"ZWRlIiwiZXhwIjoxNTQyMDQ1Mzk5fQ==."
"ewogICJzdWIiOiAiMTA5NzE2NDkyNjgxNjg2MTcyOTY5IiwKICAibmFtZSI6ICJIYW"
"5zIFphbmRiZWx0IiwKICAiZ2l2ZW5fbmFtZSI6ICJIYW5zIiwKICAiZmFtaWx5X25h"
"bWUiOiAiWmFuZGJlbHQiLAogICJwcm9maWxlIjogImh0dHBzOi8vcGx1cy5nb29nbG"
"UuY29tLzEwOTcxNjQ5MjY4MTY4NjE3Mjk2OSIsCiAgInBpY3R1cmUiOiAiaHR0cHM6"
"Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1pOUc3U1V2S1FETS9BQUFBQUFBQU"
"FBSS9BQUFBQUFBQUFBQS9zeEFzTk5FVlJWZy9waG90by5qcGciCn0=."
"AlH8PGya9avWoGVkWOFWbMNiLdpSDQZqP-"
"OuGfIXHw1CZWjxfJInXYiRsKRZlvlXJA5fguaeNKZ1Q_RyDjNqRg==";
json_t *json_payload = NULL;
const char *rv = NULL;
char *url = NULL;
url = oauth2_stradd(NULL, oauth2_check_http_base_url(),
get_eckey_url_path, NULL);
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "eckey_uri",
url, NULL);
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
oauth2_mem_free(url);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_introspection)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
json_t *json_payload = NULL;
const char *rv = NULL;
char *url = NULL;
url = oauth2_stradd(NULL, oauth2_check_http_base_url(),
post_introspection_path, NULL);
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "introspect", url, "introspect.ssl_verify=false");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, "bogus", &json_payload);
ck_assert_int_eq(rc, false);
json_decref(json_payload);
rc = oauth2_token_verify(_log, NULL, verify, valid_access_token,
&json_payload);
ck_assert_int_eq(rc, true);
json_decref(json_payload);
// get it from the cache
rc = oauth2_token_verify(_log, NULL, verify, valid_access_token,
&json_payload);
ck_assert_int_eq(rc, true);
json_decref(json_payload);
oauth2_cfg_token_verify_free(_log, verify);
oauth2_mem_free(url);
}
END_TEST
START_TEST(test_oauth2_verify_token_plain)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0."
"sQOVoEtkQlgy8UwlPOi5YWSdGAkRn80JqT53RdktIms";
json_t *json_payload = NULL;
const char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "plain",
"mysecret", "kid=mykid");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_base64)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0."
"kEm7kPCWXNn-p4cRSDAuO-htYx8hpq_7imIhMlig5So";
json_t *json_payload = NULL;
const char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "base64",
"YW5vdGhlcnNlY3JldA==", NULL);
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_base64url)
{
// https://tools.ietf.org/html/rfc7515#appendix-A with iat/exp
// validation set to false
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9."
"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly"
"9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-"
"mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
json_t *json_payload = NULL;
const char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "base64url",
"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-"
"1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow",
"verify.exp=skip");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_hex)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0."
"ZdF3p7DBVz50evb9_eaY6euUtYikb6NTF7QHO6OTbGg";
json_t *json_payload = NULL;
const char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "hex", "6d797468697264736563726574", NULL);
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_pem)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt =
"eyJhbGciOiJSUzI1NiJ9."
"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
"p0igcN_IoypGlUPQGe77Rw";
json_t *json_payload = NULL;
const char *rv = NULL;
char *pem =
"-----BEGIN CERTIFICATE-----\n"
"MIICwzCCAaugAwIBAgIBADANBgkqhkiG9w0BAQQFADAlMQswCQYDVQQGEwJOTDEW\n"
"MBQGA1UEAwwNWm1hcnRab25lIElBTTAeFw0xOTAyMDcxOTI4MTFaFw0yMDAyMDcx\n"
"OTI4MTFaMCUxCzAJBgNVBAYTAk5MMRYwFAYDVQQDDA1abWFydFpvbmUgSUFNMIIB\n"
"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJjNiuS\n"
"fb4p4fAkd/wWJcyQoTbji9k0l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt\n"
"0Bm4d4QlL+yRT+SFd2lZS+pCgNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGz\n"
"jb/7OMg0LOL+bSf63kpaSHSXndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2\n"
"R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK\n"
"+DWNmoudF8NAco9/h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQID\n"
"AQABMA0GCSqGSIb3DQEBBAUAA4IBAQB8USZJ2O2um7QXYKJmI1YpeV1UCoqwl8zs\n"
"Ow6oMxppGRd8ZiOI4N+fYvRkZmdLDlvg/Xww0Z6sNT0HDlS0otbUhiYBg9fQb44v\n"
"Rx3lLXeziHoprzP/SApf5lFUJmzvfbsyKKRFsmkpGWbtkWKDHxk1BA/4symkoifC\n"
"OE8+GbbdaDXthPDEsrLNnTpH5mLrWZ4+Ulp7FQiB3okXnL/wasiMufRZdEhUPLdP\n"
"KS/Ch2wudSukzgin9K0GsvdM64I70tLyHRPtkIAorm5RwgGJvO5lHD/2W1hjIun0\n"
"aItLpLaBsJJKaMxUVbt6pGopRRQnCHscUxKZZEJDm6Qjiuw66iUW\n"
"-----END CERTIFICATE-----\n";
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "pem", pem,
"verify.exp=skip");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_pubkey)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt =
"eyJhbGciOiJSUzI1NiJ9."
"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
"p0igcN_IoypGlUPQGe77Rw";
json_t *json_payload = NULL;
const char *rv = NULL;
char *pubkey =
"-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJj\n"
"NiuSfb4p4fAkd/wWJcyQoTbji9k0l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEz\n"
"P1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo\n"
"9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTB\n"
"EMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxX\n"
"FvUK+DWNmoudF8NAco9/h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXp\n"
"oQIDAQAB\n"
"-----END PUBLIC KEY-----";
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "pubkey",
pubkey, "verify.exp=skip");
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
}
END_TEST
START_TEST(test_oauth2_verify_token_metadata)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
json_t *json_payload = NULL;
const char *rv = NULL;
char *url = NULL;
url = oauth2_stradd(NULL, oauth2_check_http_base_url(), metadata_path,
NULL);
rv = oauth2_cfg_token_verify_add_options(_log, &verify, "metadata", url,
"&verify.exp=skip");
ck_assert_ptr_eq(rv, NULL);
// reference token
rc = oauth2_token_verify(_log, NULL, verify, "bogus", &json_payload);
ck_assert_int_eq(rc, false);
json_decref(json_payload);
rc = oauth2_token_verify(_log, NULL, verify, valid_access_token,
&json_payload);
ck_assert_int_eq(rc, true);
json_decref(json_payload);
// get it from the cache
rc = oauth2_token_verify(_log, NULL, verify, valid_access_token,
&json_payload);
ck_assert_int_eq(rc, true);
json_decref(json_payload);
// jwt token
char *jwt =
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0."
"eyJzY29wZSI6W10sImNsaWVudF9pZF9uYW1lIjoicm9fY2xpZW50IiwiYWdpZCI6Im"
"4zak1UazdXSDVVSU9FTWNEZEZPSVR5eFZ2VW1XRHVyIiwiT3JnTmFtZSI6IlBpbmcg"
"SWRlbnRpdHkgQ29ycG9yYXRpb24iLCJjbmYiOnsieDV0I1MyNTYiOiJsNnU5S1VDZ0"
"I2UHpHdklpTS0tWEYwTHF3N1ZYejdvQWtoUkhhbEZqOGkwIn0sIlVzZXJuYW1lIjoi"
"am9lIiwiZXhwIjoxNTQyMTI5NzgzfQ.MUghlaVxy5ij3HODBl6spAA-h6W7D-"
"PoKyhDfR5DnODQqwb5zaqba2pWyJ0d6-4AQfQ6dIe0jfwQeUrPTu2DZLtk3H-"
"noCSjtXrFV_RFNfz9kqdEXwkVZAX8H_ySrYFcAx3Ac9C8bZzjRUM6c4emql-"
"I6T1fVGqO_"
"bVUsWbPmPtNanq3UyqTrlDwQ6weO0ZbLH9tcDpZD4ojNCJjkHa3lvjwYzPNwlAI6a_"
"DGng-7rgrobhOiaAgBAwLhq9fvTtM2MWNmWXmUCymq3nGqG_d_t5i_"
"x7Zf28T3ejzEX-ETefpTENX7BJ57-vQbAeECRTIo_LhzKTaDkiZWpf6JgraQg";
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
json_decref(json_payload);
// get it from the cache
rc = oauth2_token_verify(_log, NULL, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
json_decref(json_payload);
oauth2_cfg_token_verify_free(_log, verify);
oauth2_mem_free(url);
}
END_TEST
START_TEST(test_oauth2_verify_jwk_mtls)
{
bool rc = false;
oauth2_cfg_token_verify_t *verify = NULL;
char *jwt =
"eyJhbGciOiAiUlMyNTYifQ."
"ewogICJpc3MiOiAiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLAogICJzdWIiOi"
"AidHkud2ViYkBleGFtcGxlLmNvbSIsCiAgImV4cCI6IDE0OTM3MjY0MDAsCiAgIm5i"
"ZiI6IDE0OTM3MjI4MDAsCiAgImNuZiI6eyJ4NXQjUzI1NiI6IkE0RHRMMkptVU1oQX"
"N2Smo1dEt5bjY0U3F6bXVYYk1ySmEwbjc2MXk1djAifQp9.K5g_"
"fMQkl6g9qo2uGDYDLrlQaTk_aEmP3601IYeVWrezftobh5IClflB-"
"9YYvIU1O4Bswpl5bvBdkN7G7UXCwrzI6fFSVMvYqTxe0WlmLAnVH_RPqZ6Gxw_"
"SpgemmXg8EPfDwa-14H5O-4S9a0JIE7Q1PBLsvqMhmO2WhpQFCvxnXJ1ho0m_"
"dDH95y3ukJqvTe1mWwdd7K79GRnf4oFIi_"
"Xm7iHOYYwEXwA1XNQgLcxbaGUKaA8N4yVLj2Q43JtxN7AUDQyfEhfzx5JhcQQ4t8gU"
"GhovGvHh7LAYwpY8Ea6r3y2LSazhTm8cFFix1L6T5vFNH3MUFn2ouR8UDGA87Q";
char *jwk = "{\"kty\":\"RSA\",\"kid\":\"k1\",\"use\":\"sig\",\"n\":"
"\"ym7jipmB37CgdonwGFVRuZmRfCl3lVh91fmm5CXHcNlUFZNR3D6Q9r63"
"PpGRnfSsX3dOweh8BXd2AJ3mxvcE4z9xH--tA5EaOGI7IVF0Ip_"
"i3flGg85xOADlb8rX3ez1NqkqMVJeeJypKhCCDNfvu_"
"MXSdPLglU969YQF5xKAK8VFRfI6EfxxrZ_3Dvt2CKDV4LTPPJe9KI2_"
"LuLQFBJ3MzlCTVxY6gyaljrWaDq7q5Lt3GB1KYS0Yd8COEQwsclOLm0Tdd"
"hg4cle-DfaTMi7xsTZsPKyac5x17Y4N4isHhZULuWHX7o1bs809xcj-_-"
"YCRq6C61je_mzFhuF4pczw\",\"e\":\"AQAB\"}";
json_t *json_payload = NULL;
oauth2_http_request_t *request = NULL;
const char *rv = NULL;
rv = oauth2_cfg_token_verify_add_options(
_log, &verify, "jwk", jwk,
"verify.exp=skip&type=mtls&mtls.policy=required");
ck_assert_ptr_eq(rv, NULL);
request = oauth2_http_request_init(_log);
oauth2_http_request_scheme_set(_log, request, "https");
oauth2_http_request_hostname_set(_log, request, "resource.example.org");
// oauth2_http_request_port_set();
oauth2_http_request_path_set(_log, request, "/protectedresource");
oauth2_http_request_method_set(_log, request, OAUTH2_HTTP_METHOD_GET);
oauth2_http_request_context_set(
_log, request, OAUTH2_TLS_CERT_VAR_NAME,
"-----BEGIN "
"CERTIFICATE-----"
"\nMIIBBjCBrAIBAjAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDDARtdGxzMB4X"
"DTE4MTAxODEyMzcwOVoXDTIyMDUwMjEyMzcwOVowDzENMAsGA1UEAwwEbX"
"RsczBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNcnyxwqV6hY8QnhxxzF"
"Q03C7HKW9OylMbnQZjjJ/Au08/"
"coZwxS7LfA4vOLS9WuneIXhbGGWvsDSb0tH6IxLm8wCgYIKoZIzj0EAwID"
"SQAwRgIhAP0RC1E+vwJD/D1AGHGzuri+hlV/"
"PpQEKTWUVeORWz83AiEA5x2eXZOVbUlJSGQgjwD5vaUaKlLR50Q2DmFfQj"
"1L+SY=\n-----END CERTIFICATE-----");
rc = oauth2_token_verify(_log, request, verify, jwt, &json_payload);
ck_assert_int_eq(rc, true);
oauth2_http_request_free(_log, request);
oauth2_cfg_token_verify_free(_log, verify);
json_decref(json_payload);
/*
oauth2_nv_list_free(_log, params);
*/
}
END_TEST
Suite *oauth2_check_oauth2_suite()
{
Suite *s = suite_create("oauth2");
TCase *c = tcase_create("core");
liboauth2_check_register_http_callbacks(oauth2_check_http_base_path(),
oauth2_check_oauth2_serve_get,
oauth2_check_oauth2_serve_post);
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_oauth2_auth_client_secret_basic);
tcase_add_test(c, test_oauth2_auth_client_secret_post);
tcase_add_test(c, test_oauth2_auth_client_secret_jwt);
tcase_add_test(c, test_oauth2_auth_private_key_jwt);
tcase_add_test(c, test_oauth2_auth_client_cert);
tcase_add_test(c, test_oauth2_auth_http_basic);
tcase_add_test(c, test_oauth2_auth_none);
tcase_add_test(c, test_oauth2_verify_clone);
tcase_add_test(c, test_oauth2_verify_jwks_uri);
tcase_add_test(c, test_oauth2_verify_jwk);
tcase_add_test(c, test_oauth2_verify_jwk_dpop);
tcase_add_test(c, test_oauth2_verify_eckey_uri);
tcase_add_test(c, test_oauth2_verify_token_introspection);
tcase_add_test(c, test_oauth2_verify_token_plain);
tcase_add_test(c, test_oauth2_verify_token_base64);
tcase_add_test(c, test_oauth2_verify_token_base64url);
tcase_add_test(c, test_oauth2_verify_token_hex);
tcase_add_test(c, test_oauth2_verify_token_pem);
tcase_add_test(c, test_oauth2_verify_token_pubkey);
tcase_add_test(c, test_oauth2_verify_token_metadata);
tcase_add_test(c, test_oauth2_verify_jwk_mtls);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_openidc.c 0000664 0000000 0000000 00000105426 14161030125 0017313 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/jose.h"
#include "oauth2/mem.h"
#include "oauth2/openidc.h"
#include "oauth2/util.h"
#include "cfg_int.h"
#include "openidc_int.h"
#include
#include
#include
#include
static oauth2_log_t *_log = 0;
static char *_openidc_metadata = NULL;
OAUTH2_CHECK_HTTP_PATHS
static void setup(void)
{
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
}
void oauth2_check_openidc_cleanup()
{
oauth2_check_http_base_free();
}
static void teardown(void)
{
oauth2_shutdown(_log);
if (_openidc_metadata != NULL) {
oauth2_mem_free(_openidc_metadata);
_openidc_metadata = NULL;
}
}
static char *jwk_rsa_str =
"{"
"\"kty\": \"RSA\","
"\"kid\": \"bilbo.baggins@hobbiton.example\","
"\"use\": \"sig\","
"\"n\": \"n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT"
"-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV"
"wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-"
"oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde"
"3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC"
"LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g"
"HdrNP5zw\","
"\"e\": \"AQAB\","
"\"d\": \"bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e"
"iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld"
"Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b"
"MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU"
"6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj"
"d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc"
"OpBrQzwQ\","
"\"p\": \"3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR"
"aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG"
"peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8"
"bUq0k\","
"\"q\": \"uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT"
"8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an"
"V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0"
"s7pFc\","
"\"dp\": \"B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q"
"1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn"
"-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX"
"59ehik\","
"\"dq\": \"CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr"
"AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK"
"bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK"
"T1cYF8\","
"\"qi\": \"3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N"
"ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh"
"jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP"
"z8aaI4\""
"}";
static cjose_jwk_t *jwk_rsa = NULL;
static char *jwks_uri_path = "/jwks_uri";
static char *token_endpoint_path = "/token";
static char *userinfo_endpoint_path = "/userinfo";
static cjose_jwk_t *oauth2_jwk_rsa_get()
{
cjose_err err;
if (jwk_rsa == NULL) {
jwk_rsa =
cjose_jwk_import(jwk_rsa_str, strlen(jwk_rsa_str), &err);
if (jwk_rsa == NULL) {
fprintf(
stderr,
"## cjose_jwk_import failed: %s (%s:%s:%ld) \n%s\n",
err.message, err.file, err.function, err.line,
jwk_rsa_str);
}
}
return jwk_rsa;
}
static char *oauth2_check_openidc_serve_get(const char *request)
{
char *rv = NULL, *s = NULL;
cjose_err err;
if (strncmp(request, jwks_uri_path, strlen(jwks_uri_path)) == 0) {
// TODO: static
s = cjose_jwk_to_json(oauth2_jwk_rsa_get(), false, &err);
rv = oauth2_stradd(NULL, "{ \"keys\": [ ", s, " ] }");
cjose_get_dealloc()(s);
} else if (strncmp(request, userinfo_endpoint_path,
strlen(userinfo_endpoint_path)) == 0) {
rv = oauth2_strdup("{ \"sub\": \"myclient\", "
"\"myuserinfoclaim\": \"somevalue\" }");
} else {
rv = oauth2_strdup("problem");
}
return rv;
}
static bool
_oauth2_check_openidc_idtoken_create(oauth2_log_t *log, cjose_jwk_t *jwk,
const char *alg, const char *client_id,
const char *aud, char **id_token)
{
bool rc = false;
char *payload = NULL;
json_t *json = NULL;
cjose_header_t *hdr = NULL;
cjose_jws_t *jws = NULL;
const char *jwt = NULL;
cjose_err err;
oauth2_debug(_log, "## enter");
json = json_object();
json_object_set_new(json, OAUTH2_JOSE_JWT_ISS, json_string(client_id));
json_object_set_new(json, OAUTH2_JOSE_JWT_SUB, json_string(client_id));
json_object_set_new(json, OAUTH2_JOSE_JWT_AUD, json_string(aud));
json_object_set_new(json, OAUTH2_JOSE_JWT_EXP,
json_integer(oauth2_time_now_sec() + 60));
json_object_set_new(json, OAUTH2_JOSE_JWT_IAT,
json_integer(oauth2_time_now_sec()));
payload = json_dumps(json, JSON_PRESERVE_ORDER | JSON_COMPACT);
hdr = cjose_header_new(&err);
if (hdr == NULL) {
oauth2_error(_log, "cjose_header_new failed: %s", err.message);
goto end;
}
if (cjose_header_set(hdr, CJOSE_HDR_ALG, alg, &err) == false) {
oauth2_error(_log, "cjose_header_set %s:%s failed: %s",
CJOSE_HDR_ALG, alg, err.message);
goto end;
}
if (cjose_header_set(hdr, OAUTH2_JOSE_HDR_TYP, OAUTH2_JOSE_HDR_TYP_JWT,
&err) == false) {
oauth2_error(_log, "cjose_header_set %s:%s failed: %s",
OAUTH2_JOSE_HDR_TYP, OAUTH2_JOSE_HDR_TYP_JWT,
err.message);
goto end;
}
jws = cjose_jws_sign(jwk, hdr, (const uint8_t *)payload,
strlen(payload), &err);
if (jws == NULL) {
oauth2_error(_log, "cjose_jws_sign failed: %s", err.message);
goto end;
}
if (cjose_jws_export(jws, &jwt, &err) == false) {
oauth2_error(_log, "cjose_jws_export failed: %s", err.message);
goto end;
}
*id_token = oauth2_strdup(jwt);
rc = true;
end:
if (json)
json_decref(json);
if (payload)
free(payload);
if (hdr)
cjose_header_release(hdr);
if (jws)
cjose_jws_release(jws);
oauth2_debug(_log, "## return: %d", rc);
return rc;
}
static char *oauth2_check_openidc_serve_post(const char *request)
{
oauth2_nv_list_t *params = NULL;
char *data = NULL;
const char *code = NULL;
const char *sep = "****";
char *rv = NULL;
char *id_token = NULL;
if (strncmp(request, token_endpoint_path,
strlen(token_endpoint_path)) == 0) {
request += strlen(token_endpoint_path) + 5;
data = strstr(request, sep);
if (data == NULL)
goto error;
data += strlen(sep);
if (oauth2_parse_form_encoded_params(_log, data, ¶ms) ==
false)
goto error;
code = oauth2_nv_list_get(_log, params, "code");
if (code == NULL)
goto error;
if (_oauth2_check_openidc_idtoken_create(
_log, oauth2_jwk_rsa_get(), "RS256", "myclient",
"myclient", &id_token) == false)
goto error;
rv =
oauth2_stradd(NULL, "{ \"id_token\": \"", id_token, "\", ");
rv = oauth2_stradd(rv, "\"access_token\": \"", "xxxx", "\" }");
oauth2_mem_free(id_token);
oauth2_nv_list_free(_log, params);
goto end;
}
error:
rv = oauth2_strdup("problem");
end:
return rv;
}
START_TEST(test_openidc_cfg)
{
bool rc = false;
oauth2_cfg_openidc_t *c = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL,
*c5 = NULL;
oauth2_http_request_t *r = NULL;
oauth2_openidc_provider_t *p = NULL;
oauth2_cfg_openidc_provider_resolver_t *pr = NULL;
char *value = NULL, *rv = NULL;
c = oauth2_cfg_openidc_init(_log);
r = oauth2_http_request_init(_log);
rc = oauth2_cfg_openidc_redirect_uri_set(
_log, c, "https://example.org/redirect_uri");
ck_assert_int_eq(rc, true);
value = oauth2_cfg_openidc_redirect_uri_get(_log, c, r);
ck_assert_str_eq(value, "https://example.org/redirect_uri");
oauth2_mem_free(value);
rc = oauth2_cfg_openidc_redirect_uri_set(
_log, c, "https://example.com/redirect_uri");
ck_assert_int_eq(rc, true);
value = oauth2_cfg_openidc_redirect_uri_get(_log, c, r);
ck_assert_str_eq(value, "https://example.com/redirect_uri");
oauth2_mem_free(value);
rc = oauth2_cfg_openidc_redirect_uri_set(_log, c, "/redirect_uri");
ck_assert_int_eq(rc, true);
value = oauth2_cfg_openidc_redirect_uri_get(_log, c, r);
ck_assert_ptr_eq(value, NULL);
rc = oauth2_http_request_header_set(_log, r, "Host", "example.com");
ck_assert_int_eq(rc, true);
value = oauth2_cfg_openidc_redirect_uri_get(_log, c, r);
ck_assert_str_eq(value, "https://example.com/redirect_uri");
oauth2_mem_free(value);
p = oauth2_openidc_provider_init(_log);
ck_assert_ptr_ne(p, NULL);
value = oauth2_cfg_openidc_redirect_uri_get_iss(_log, c, r, p);
// ck_assert_ptr_eq(value, NULL);
ck_assert_str_eq(value, "https://example.com/redirect_uri");
oauth2_mem_free(value);
rc = oauth2_openidc_provider_issuer_set(_log, p, "jan");
ck_assert_int_eq(rc, true);
value = oauth2_cfg_openidc_redirect_uri_get_iss(_log, c, r, p);
ck_assert_str_eq(value, "https://example.com/redirect_uri?iss=jan");
oauth2_mem_free(value);
c2 = oauth2_cfg_openidc_clone(_log, c);
ck_assert_ptr_ne(c2, NULL);
value = oauth2_cfg_openidc_redirect_uri_get_iss(_log, c2, r, p);
ck_assert_str_eq(value, "https://example.com/redirect_uri?iss=jan");
oauth2_mem_free(value);
rv = oauth2_cfg_openidc_provider_resolver_set_options(
_log, c, "string", "issuer=https://localhost", NULL);
ck_assert_ptr_eq(rv, NULL);
rv = oauth2_openidc_client_set_options(_log, c, "string",
"client_id=mycc", NULL);
ck_assert_ptr_eq(rv, NULL);
c3 = oauth2_cfg_openidc_clone(_log, c);
c4 = oauth2_cfg_openidc_init(_log);
pr = oauth2_cfg_openidc_provider_resolver_init(_log);
ck_assert_ptr_ne(pr, NULL);
rc = oauth2_cfg_openidc_provider_resolver_set(_log, c4, pr);
ck_assert_uint_eq(rc, true);
rc = oauth2_cfg_openidc_handler_path_set(_log, c4, "/mypath");
ck_assert_uint_eq(rc, true);
rc = oauth2_cfg_openidc_state_cookie_name_prefix_set(_log, c4,
"mycookie-");
ck_assert_uint_eq(rc, true);
c5 = oauth2_cfg_openidc_init(_log);
oauth2_cfg_openidc_merge(_log, c5, c4, c);
value = oauth2_cfg_openidc_redirect_uri_get_iss(_log, c5, r, p);
ck_assert_str_eq(value, "https://example.com/redirect_uri?iss=jan");
oauth2_mem_free(value);
ck_assert_ptr_ne(oauth2_cfg_openidc_provider_resolver_get(_log, c5),
NULL);
ck_assert_str_eq(oauth2_cfg_openidc_handler_path_get(_log, c5),
"/mypath");
ck_assert_str_eq(
oauth2_cfg_openidc_state_cookie_name_prefix_get(_log, c5),
"mycookie-");
oauth2_cfg_openidc_free(_log, c5);
c5 = oauth2_cfg_openidc_init(_log);
oauth2_cfg_openidc_merge(_log, c5, c, c4);
ck_assert_str_eq(
oauth2_cfg_openidc_state_cookie_name_prefix_get(_log, c5),
"mycookie-");
ck_assert_str_eq(oauth2_openidc_client_client_id_get(
_log, oauth2_cfg_openidc_client_get(_log, c5)),
"mycc");
oauth2_cfg_openidc_free(_log, c5);
oauth2_cfg_openidc_free(_log, c4);
oauth2_cfg_openidc_free(_log, c3);
oauth2_cfg_openidc_free(_log, c2);
oauth2_openidc_provider_free(_log, p);
oauth2_http_request_free(_log, r);
oauth2_cfg_openidc_free(_log, c);
}
END_TEST
static char *test_openidc_metadata_get()
{
if (_openidc_metadata)
goto end;
char *token_endpoint = oauth2_stradd(NULL, oauth2_check_http_base_url(),
token_endpoint_path, NULL);
char *userinfo_endpoint = oauth2_stradd(
NULL, oauth2_check_http_base_url(), userinfo_endpoint_path, NULL);
char *jwks_uri = oauth2_stradd(NULL, oauth2_check_http_base_url(),
jwks_uri_path, NULL);
_openidc_metadata =
oauth2_strdup("{ "
"\"issuer\": \"https://op.example.org\","
"\"authorization_endpoint\": "
"\"https://op.example.org/authorize\",");
_openidc_metadata = oauth2_stradd(
_openidc_metadata, "\"token_endpoint\": \"", token_endpoint, "\",");
_openidc_metadata =
oauth2_stradd(_openidc_metadata, "\"userinfo_endpoint\": \"",
userinfo_endpoint, "\",");
_openidc_metadata = oauth2_stradd(_openidc_metadata, "\"jwks_uri\": \"",
jwks_uri, "\" }");
oauth2_mem_free(token_endpoint);
oauth2_mem_free(jwks_uri);
oauth2_mem_free(userinfo_endpoint);
end:
return _openidc_metadata;
}
START_TEST(test_openidc_proto_state)
{
bool rc = false;
json_t *json = NULL;
oauth2_cfg_openidc_t *c = NULL;
oauth2_http_request_t *r = oauth2_http_request_init(_log);
oauth2_http_response_t *response = NULL;
oauth2_openidc_provider_t *provider = NULL;
char *value = NULL;
const char *cookie = NULL;
c = oauth2_cfg_openidc_init(_log);
oauth2_cfg_openidc_provider_resolver_set_options(
_log, c, "string", test_openidc_metadata_get(), NULL);
oauth2_openidc_proto_state_t *p1 =
oauth2_openidc_proto_state_init(_log);
ck_assert_ptr_ne(p1, NULL);
rc = oauth2_openidc_proto_state_set(_log, p1, "one", "string");
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_proto_state_set_int(_log, p1, "two", 2);
ck_assert_int_eq(rc, true);
json = oauth2_openidc_proto_state_json_get(p1);
ck_assert_ptr_ne(json, NULL);
ck_assert_str_eq(json_string_value(json_object_get(json, "one")),
"string");
ck_assert_int_eq(json_integer_value(json_object_get(json, "two")), 2);
rc = oauth2_openidc_proto_state_set(
_log, p1, _OAUTH2_OPENIDC_PROTO_STATE_KEY_ISSUER,
"https://op.example.org");
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_proto_state_set_int(
_log, p1, _OAUTH2_OPENIDC_PROTO_STATE_KEY_TIMESTAMP,
oauth2_time_now_sec());
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_proto_state_set(
_log, p1, _OAUTH2_OPENIDC_PROTO_STATE_KEY_TARGET_LINK_URI,
"https://example.org/secure");
rc = _oauth2_openidc_state_validate(_log, c, r, p1, &provider);
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_proto_state_target_link_uri_get(_log, p1, &value);
ck_assert_int_eq(rc, true);
ck_assert_str_eq(value, "https://example.org/secure");
oauth2_mem_free(value);
value = NULL;
oauth2_openidc_proto_state_t *p2 =
oauth2_openidc_proto_state_clone(_log, p1);
ck_assert_ptr_ne(c, NULL);
json = oauth2_openidc_proto_state_json_get(p2);
ck_assert_ptr_ne(json, NULL);
ck_assert_str_eq(json_string_value(json_object_get(json, "one")),
"string");
ck_assert_int_eq(json_integer_value(json_object_get(json, "two")), 2);
oauth2_openidc_proto_state_free(_log, p2);
oauth2_openidc_proto_state_free(_log, p1);
oauth2_http_request_scheme_set(_log, r, "https");
oauth2_http_request_hostname_set(_log, r, "example.org");
oauth2_http_request_path_set(_log, r, "/secure");
response = oauth2_http_response_init(_log);
rc = _oauth2_openidc_state_cookie_set(_log, c, provider, r, response,
"1234", "4321");
ck_assert_int_eq(rc, true);
cookie = oauth2_http_response_header_get(_log, response, "Set-Cookie");
ck_assert_ptr_ne(strstr(cookie, "openidc_state_1234="), NULL);
oauth2_openidc_proto_state_t *p3 = NULL;
oauth2_http_request_header_set(_log, r, "Cookie", cookie);
rc =
_oauth2_openidc_state_cookie_get(_log, c, r, response, "1234", &p3);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(p3, NULL);
rc = oauth2_openidc_proto_state_target_link_uri_get(_log, p3, &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(value, NULL);
ck_assert_str_eq(value, "https://example.org/secure");
oauth2_mem_free(value);
value = NULL;
rc = oauth2_openidc_proto_state_pkce_get(_log, p3, &value);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(value, NULL);
ck_assert_str_eq(value, "4321");
oauth2_mem_free(value);
value = NULL;
oauth2_openidc_proto_state_free(_log, p3);
oauth2_openidc_provider_free(_log, provider);
oauth2_http_response_free(_log, response);
oauth2_http_request_free(_log, r);
oauth2_cfg_openidc_free(_log, c);
}
END_TEST
static void _test_openidc_resolve_to_false(oauth2_cfg_openidc_t *c,
oauth2_http_request_t *r,
const char *metadata)
{
bool rc = false;
char *rv = NULL;
oauth2_openidc_provider_t *provider = NULL;
rv = oauth2_cfg_openidc_provider_resolver_set_options(_log, c, "string",
metadata, NULL);
ck_assert_ptr_eq(rv, NULL);
rc = _oauth2_openidc_provider_resolve(_log, c, r, NULL, &provider);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(NULL, provider);
}
START_TEST(test_openidc_resolver)
{
bool rc = false;
char *rv = NULL;
oauth2_cfg_openidc_t *c = NULL;
oauth2_http_request_t *r = NULL;
oauth2_openidc_provider_t *provider = NULL;
char filename[512];
c = oauth2_cfg_openidc_init(_log);
r = oauth2_http_request_init(_log);
rv = oauth2_cfg_openidc_provider_resolver_set_options(
_log, c, "string", test_openidc_metadata_get(), NULL);
ck_assert_ptr_eq(rv, NULL);
rc = _oauth2_openidc_provider_resolve(_log, c, r, NULL, &provider);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(NULL, provider);
ck_assert_str_eq("https://op.example.org",
oauth2_openidc_provider_issuer_get(_log, provider));
oauth2_openidc_provider_free(_log, provider);
provider = NULL;
sprintf((char *)filename, "%s/%s",
getenv("srcdir") ? getenv("srcdir") : ".",
"test/provider.json");
rv = oauth2_cfg_openidc_provider_resolver_set_options(_log, c, "file",
filename, NULL);
ck_assert_ptr_eq(rv, NULL);
rc = _oauth2_openidc_provider_resolve(_log, c, r, NULL, &provider);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(NULL, provider);
ck_assert_str_eq("https://pingfed:9031",
oauth2_openidc_provider_issuer_get(_log, provider));
ck_assert_ptr_ne(
NULL,
oauth2_openidc_provider_authorization_endpoint_get(_log, provider));
ck_assert_ptr_ne(
NULL, oauth2_openidc_provider_token_endpoint_get(_log, provider));
/*
ck_assert_ptr_ne(NULL, oauth2_openidc_provider_token_endpoint_auth_get(
_log, provider));
ck_assert_int_eq(
false, oauth2_openidc_provider_ssl_verify_get(_log, provider));
*/
ck_assert_ptr_ne(NULL,
oauth2_openidc_provider_jwks_uri_get(_log, provider));
/*
ck_assert_ptr_ne(NULL,
oauth2_openidc_provider_scope_get(_log, provider));
ck_assert_ptr_ne(NULL,
oauth2_openidc_provider_client_id_get(_log, provider));
ck_assert_ptr_ne(
NULL, oauth2_openidc_provider_client_secret_get(_log, provider));
ck_assert_int_eq(
true, oauth2_openidc_provider_ssl_verify_set(_log, provider, true));
*/
ck_assert_int_eq(true,
oauth2_openidc_provider_authorization_endpoint_set(
_log, provider, "https://other.org/authorize"));
ck_assert_int_eq(true, oauth2_openidc_provider_token_endpoint_set(
_log, provider, "https://other.org/token"));
ck_assert_int_eq(true,
oauth2_openidc_provider_jwks_uri_set(
_log, provider, "https://other.org/jwks_uri"));
/*
ck_assert_int_eq(true, oauth2_openidc_provider_scope_set(
_log, provider, "openid profile other"));
ck_assert_int_eq(true, oauth2_openidc_provider_client_id_set(
_log, provider, "someclientid"));
ck_assert_int_eq(true, oauth2_openidc_provider_client_secret_set(
_log, provider, "someclientsecret"));
*/
oauth2_openidc_provider_free(_log, provider);
provider = NULL;
_test_openidc_resolve_to_false(c, r, NULL);
_test_openidc_resolve_to_false(c, r, "");
_test_openidc_resolve_to_false(c, r, "{");
_test_openidc_resolve_to_false(c, r, "{}");
_test_openidc_resolve_to_false(c, r, "{ \"issuer\": 0 }");
_test_openidc_resolve_to_false(c, r,
"{ \"authorization_endpoint\": 1, "
"\"issuer\": \"https://example.org\" }");
_test_openidc_resolve_to_false(
c, r,
"{ \"token_endpoint\": 1, \"authorization_endpoint\": "
"\"https://example.org/authorize\", \"issuer\": "
"\"https://example.org\" }");
_test_openidc_resolve_to_false(
c, r,
"{ \"jwks_uri\": 0, \"token_endpoint\": "
"\"https://example.org/authorize\", \"authorization_endpoint\": "
"\"https://example.org/authorize\", \"issuer\": "
"\"https://example.org\" }");
rv = oauth2_cfg_openidc_provider_resolver_set_options(_log, c, "dir",
NULL, NULL);
ck_assert_ptr_eq(rv, NULL);
rc = _oauth2_openidc_provider_resolve(_log, c, r, NULL, &provider);
ck_assert_int_eq(rc, false);
oauth2_http_request_free(_log, r);
oauth2_cfg_openidc_free(_log, c);
}
END_TEST
START_TEST(test_openidc_client)
{
char *rv = NULL;
oauth2_cfg_openidc_t *cfg = NULL;
oauth2_openidc_client_t *client = NULL;
char filename[512];
cfg = oauth2_cfg_openidc_init(_log);
ck_assert_ptr_ne(cfg, NULL);
sprintf((char *)filename, "%s/%s",
getenv("srcdir") ? getenv("srcdir") : ".", "test/client.json");
rv = oauth2_openidc_client_set_options(_log, cfg, "file", filename,
"ssl_verify=false");
ck_assert_ptr_eq(rv, NULL);
client = oauth2_cfg_openidc_client_get(_log, cfg);
ck_assert_ptr_ne(client, NULL);
ck_assert_str_eq(oauth2_openidc_client_client_id_get(_log, client),
"a_client");
ck_assert_uint_eq(oauth2_openidc_client_ssl_verify_get(_log, client),
false);
rv = oauth2_openidc_client_set_options(
_log, cfg, "json", "{ \"client_id\": \"b_client\" }",
"ssl_verify=true");
ck_assert_str_eq(oauth2_openidc_client_client_id_get(_log, client),
"b_client");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(oauth2_openidc_client_ssl_verify_get(_log, client),
true);
rv = oauth2_openidc_client_set_options(
_log, cfg, "string",
"client_id=bla&client_secret=bla&token_endpoint_auth_method=bogus",
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(_log, cfg, "json", NULL, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(_log, cfg, "json", "{", NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(_log, cfg, "json",
"{ \"client_id\": 0 }", NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(
_log, cfg, "json",
"{ \"client_id\": \"valid\", \"client_secret\": 0 }", NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(
_log, cfg, "json",
"{ \"client_id\": \"valid\", \"client_secret\": \"valid\", "
"\"scope\": 0 }",
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(
_log, cfg, "json",
"{ \"client_id\": \"valid\", \"client_secret\": \"valid\", "
"\"scope\": \"valid\", \"token_endpoint_auth_method\": 0 }",
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(
_log, cfg, "json",
"{ \"client_id\": \"valid\", \"client_secret\": \"valid\", "
"\"scope\": \"valid\", \"token_endpoint_auth_method\": \"bogus\" }",
NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_openidc_client_set_options(
_log, cfg, "json",
"{ \"client_id\": \"valid\", \"client_secret\": \"valid\", "
"\"scope\": \"valid\", \"token_endpoint_auth_method\": "
"\"client_secret_post\" }",
NULL);
ck_assert_ptr_eq(rv, NULL);
oauth2_cfg_openidc_free(_log, cfg);
}
END_TEST
typedef struct _openidc_set_cookie_find_ctx_t {
const char *name;
const char *match;
const char *found;
} _openidc_set_cookie_find_ctx_t;
static bool _test_openidc_response_set_cookie_match(oauth2_log_t *log,
void *rec, const char *name,
const char *value)
{
_openidc_set_cookie_find_ctx_t *ctx =
(_openidc_set_cookie_find_ctx_t *)rec;
if (strncasecmp("Set-Cookie", name, strlen("Set-Cookie")) == 0) {
if (strstr(value, ctx->name) == value) {
if (ctx->match != NULL) {
if (strstr(value, ctx->match) != NULL) {
ctx->found = value;
}
} else {
ctx->found = value;
}
}
}
return (ctx->found == NULL);
}
static const char *
_test_openidc_response_set_cookie_find(const oauth2_http_response_t *response,
const char *name, const char *match)
{
_openidc_set_cookie_find_ctx_t ctx;
ctx.name = name;
ctx.match = match;
ctx.found = NULL;
oauth2_http_response_headers_loop(
_log, response, _test_openidc_response_set_cookie_match, &ctx);
return ctx.found;
}
static bool _test_openidc_response_header_find_cookie_expire(
const oauth2_http_response_t *response, const char *name)
{
return (_test_openidc_response_set_cookie_find(
response, name,
"Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0") !=
NULL);
}
static void _openidc_verify_authentication_request_state(
const oauth2_http_response_t *response, char **r_state,
char **state_cookie_name, char **state_cookie)
{
bool rc = false;
const char *location = NULL;
char *state = NULL;
ck_assert_ptr_ne(NULL, response);
ck_assert_uint_eq(oauth2_http_response_status_code_get(_log, response),
302);
location = oauth2_http_response_header_get(_log, response, "Location");
ck_assert_ptr_ne(NULL, strstr(location, "response_type=code"));
ck_assert_ptr_ne(NULL,
strstr(location, "https://op.example.org/authorize"));
state = strstr(location, "state=");
ck_assert_ptr_ne(NULL, state);
state += strlen("state=");
*r_state = oauth2_strdup(state);
char *p = strstr(*r_state, "&");
if (p)
*p = '\0';
*state_cookie_name =
oauth2_stradd(NULL, "openidc_state_", *r_state, NULL);
rc = _test_openidc_response_header_find_cookie_expire(
response, *state_cookie_name);
ck_assert_int_eq(rc, false);
*state_cookie = oauth2_strdup(_test_openidc_response_set_cookie_find(
response, *state_cookie_name, NULL));
ck_assert_ptr_ne(NULL, *state_cookie);
}
static void _test_openidc_handle(oauth2_cfg_openidc_t *c)
{
bool rc = false;
oauth2_http_request_t *r = NULL;
oauth2_http_response_t *response = NULL;
const char *location = NULL;
char *state = NULL, *state_cookie_name = NULL, *state_cookie = NULL;
char *query_str = NULL, *session_cookie = NULL;
json_t *claims = NULL;
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_path_set(_log, r, "/secure");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Host", "app.example.org");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Accept", "text/html");
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_handle(_log, c, r, &response, &claims);
ck_assert_int_eq(rc, true);
_openidc_verify_authentication_request_state(
response, &state, &state_cookie_name, &state_cookie);
json_decref(claims);
oauth2_http_response_free(_log, response);
response = NULL;
oauth2_http_request_free(_log, r);
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_path_set(_log, r,
"/openid-connect/redirect_uri");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Host", "app.example.org");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Accept", "text/html");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Cookie", state_cookie);
ck_assert_int_eq(rc, true);
query_str = oauth2_stradd(NULL, "code=4321&state", "=", state);
rc = oauth2_http_request_query_set(_log, r, query_str);
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_handle(_log, c, r, &response, &claims);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(NULL, response);
ck_assert_uint_eq(oauth2_http_response_status_code_get(_log, response),
302);
oauth2_mem_free(state_cookie);
state_cookie =
oauth2_strdup(oauth2_http_response_header_set_cookie_prefix_get(
_log, response, state_cookie_name));
ck_assert_ptr_ne(NULL, state_cookie);
rc = _test_openidc_response_header_find_cookie_expire(
response, state_cookie_name);
ck_assert_int_eq(rc, true);
location = oauth2_http_response_header_get(_log, response, "Location");
ck_assert_ptr_ne(NULL, response);
ck_assert_int_eq(strcmp(location, "https://app.example.org/secure"), 0);
session_cookie =
oauth2_strdup(oauth2_http_response_header_set_cookie_prefix_get(
_log, response, "openidc_session"));
ck_assert_ptr_ne(NULL, session_cookie);
json_decref(claims);
oauth2_http_response_free(_log, response);
oauth2_http_request_free(_log, r);
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_path_set(_log, r, "/secure");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Host", "app.example.org");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Accept", "text/html");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Cookie", session_cookie);
ck_assert_int_eq(rc, true);
rc = oauth2_openidc_handle(_log, c, r, &response, &claims);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(NULL, response);
// TODO:
ck_assert_uint_eq(oauth2_http_response_status_code_get(_log, response),
0);
// ck_assert_ptr_ne(NULL,
// oauth2_http_response_header_set_cookie_prefix_get(_log, response,
// "openidc_session"));
ck_assert_str_eq(
json_string_value(json_object_get(claims, "myuserinfoclaim")),
"somevalue");
json_decref(claims);
oauth2_http_request_free(_log, r);
oauth2_http_response_free(_log, response);
sleep(2);
r = oauth2_http_request_init(_log);
rc = oauth2_http_request_path_set(_log, r, "/secure");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Host", "app.example.org");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Accept", "text/html");
ck_assert_int_eq(rc, true);
rc = oauth2_http_request_header_set(_log, r, "Cookie", session_cookie);
ck_assert_int_eq(rc, true);
response = NULL;
rc = oauth2_openidc_handle(_log, c, r, &response, &claims);
ck_assert_int_eq(rc, true);
ck_assert_ptr_ne(NULL, response);
ck_assert_uint_eq(oauth2_http_response_status_code_get(_log, response),
302);
oauth2_http_request_free(_log, r);
oauth2_http_response_free(_log, response);
oauth2_mem_free(state);
oauth2_mem_free(query_str);
oauth2_mem_free(state_cookie_name);
oauth2_mem_free(state_cookie);
oauth2_mem_free(session_cookie);
}
START_TEST(test_openidc_handle_cookie)
{
oauth2_cfg_openidc_t *c = NULL;
oauth2_cfg_session_t *session_cfg = NULL;
c = oauth2_cfg_openidc_init(_log);
session_cfg = oauth2_cfg_session_init(_log);
oauth2_cfg_session_set_options(
_log, session_cfg, "cookie",
"name=short_cookie&inactivity_timeout=1");
oauth2_cfg_openidc_provider_resolver_set_options(
_log, c, "string", test_openidc_metadata_get(),
"session=short_cookie");
oauth2_openidc_client_set_options(
_log, c, "string",
"token_endpoint_auth_method=client_secret_post&client_id=myclient&"
"client_secret="
"mysecret&scope=openid%20profile",
"ssl_verify=false");
_test_openidc_handle(c);
oauth2_cfg_openidc_free(_log, c);
}
END_TEST
START_TEST(test_openidc_handle_cache)
{
oauth2_cache_t *cache = NULL;
oauth2_cfg_session_t *session_cfg = NULL;
oauth2_cfg_openidc_t *c = NULL;
char *rv = NULL;
c = oauth2_cfg_openidc_init(_log);
rv = oauth2_cfg_set_cache(_log, NULL, "shm",
"name=memory&max_entries=5");
ck_assert_ptr_eq(rv, NULL);
cache = oauth2_cache_obtain(_log, NULL);
ck_assert_ptr_ne(cache, NULL);
session_cfg = oauth2_cfg_session_init(_log);
oauth2_cfg_session_set_options(
_log, session_cfg, "cache",
"name=short_memory&cache=memory&inactivity_timeout=1");
oauth2_cfg_openidc_provider_resolver_set_options(
_log, c, "string", test_openidc_metadata_get(),
"session=short_memory");
oauth2_openidc_client_set_options(
_log, c, "string",
"token_endpoint_auth_method=client_secret_post&client_id=myclient&"
"client_secret="
"mysecret&scope=openid%20profile",
"ssl_verify=false");
_test_openidc_handle(c);
oauth2_cfg_openidc_free(_log, c);
}
END_TEST
START_TEST(test_openidc_state_cookie)
{
bool rc = false;
oauth2_cfg_openidc_t *c = NULL;
oauth2_http_request_t *request = NULL;
oauth2_http_response_t *response = NULL;
char *state = NULL;
char *state_cookie_name1 = NULL, *state_cookie_name2 = NULL,
*state_cookie_name3 = NULL;
char *state_cookie1 = NULL, *state_cookie2 = NULL,
*state_cookie3 = NULL;
c = oauth2_cfg_openidc_init(_log);
oauth2_cfg_openidc_set_options(
_log, c, "state.cookie.max=1&state.cookie.delete.oldest=true");
oauth2_cfg_openidc_provider_resolver_set_options(
_log, c, "string", test_openidc_metadata_get(), NULL);
oauth2_openidc_client_set_options(
_log, c, "string", "client_id=myclient&client_secret=mysecret",
NULL);
request = oauth2_http_request_init(_log);
oauth2_http_request_path_set(_log, request, "/secure");
oauth2_http_request_header_set(_log, request, "Host",
"app.example.org");
oauth2_http_request_header_set(_log, request, "Accept", "text/html");
rc = oauth2_openidc_handle(_log, c, request, &response, NULL);
ck_assert_int_eq(rc, true);
_openidc_verify_authentication_request_state(
response, &state, &state_cookie_name1, &state_cookie1);
oauth2_mem_free(state);
oauth2_http_response_free(_log, response);
oauth2_http_request_header_add(_log, request, "Cookie", state_cookie1);
rc = oauth2_openidc_handle(_log, c, request, &response, NULL);
ck_assert_int_eq(rc, true);
_openidc_verify_authentication_request_state(
response, &state, &state_cookie_name2, &state_cookie2);
oauth2_mem_free(state);
oauth2_http_response_free(_log, response);
oauth2_http_request_header_add(_log, request, "Cookie", state_cookie2);
rc = oauth2_openidc_handle(_log, c, request, &response, NULL);
ck_assert_int_eq(rc, true);
_openidc_verify_authentication_request_state(
response, &state, &state_cookie_name3, &state_cookie3);
oauth2_mem_free(state);
rc = _test_openidc_response_header_find_cookie_expire(
response, state_cookie_name3);
ck_assert_int_eq(rc, false);
rc = _test_openidc_response_header_find_cookie_expire(
response, state_cookie_name2);
ck_assert_int_eq(rc, false);
rc = _test_openidc_response_header_find_cookie_expire(
response, state_cookie_name1);
ck_assert_int_eq(rc, true);
oauth2_mem_free(state_cookie_name1);
oauth2_mem_free(state_cookie_name2);
oauth2_mem_free(state_cookie_name3);
oauth2_mem_free(state_cookie1);
oauth2_mem_free(state_cookie2);
oauth2_mem_free(state_cookie3);
oauth2_http_request_free(_log, request);
oauth2_http_response_free(_log, response);
oauth2_cfg_openidc_free(_log, c);
}
END_TEST
Suite *oauth2_check_openidc_suite()
{
Suite *s = suite_create("openidc");
TCase *c = tcase_create("core");
liboauth2_check_register_http_callbacks(
oauth2_check_http_base_path(), oauth2_check_openidc_serve_get,
oauth2_check_openidc_serve_post);
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_openidc_cfg);
tcase_add_test(c, test_openidc_proto_state);
tcase_add_test(c, test_openidc_resolver);
tcase_add_test(c, test_openidc_client);
tcase_add_test(c, test_openidc_handle_cookie);
tcase_add_test(c, test_openidc_handle_cache);
tcase_add_test(c, test_openidc_state_cookie);
tcase_set_timeout(c, 8);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_proto.c 0000664 0000000 0000000 00000030621 14161030125 0017027 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/mem.h"
#include "oauth2/proto.h"
#include
#include
static oauth2_log_t *_log = 0;
OAUTH2_CHECK_HTTP_PATHS
void oauth2_check_proto_cleanup()
{
oauth2_check_http_base_free();
}
static void setup(void)
{
_log = oauth2_log_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_log_free(_log);
}
static const char *my_token_name = "access_token";
static const char *my_token_name2 = "access_token2";
static const char *my_env_var_token = "my_env_var_token";
static const char *my_env_var_token2 = "my_env_var_token2";
static const char *my_post_token = "my_post_token";
static const char *my_post_token2 = "my_post_token2";
static bool _oauth2_check_proto_env_get_cb(oauth2_log_t *log, void *ctx,
const char *name, char **value)
{
if (strcmp(name, my_token_name) == 0)
*value = oauth2_strdup(my_env_var_token);
if (strcmp(name, my_token_name2) == 0)
*value = oauth2_strdup(my_env_var_token2);
return true;
}
static bool _oauth2_check_proto_env_set_cb(oauth2_log_t *log, void *ctx,
const char *name, const char *value)
{
return true;
}
static bool _oauth2_check_proto_read_form_post(oauth2_log_t *log, void *ctx,
oauth2_nv_list_t **params)
{
*params = oauth2_nv_list_init(_log);
oauth2_nv_list_add(_log, *params, my_token_name, my_post_token);
oauth2_nv_list_add(_log, *params, my_token_name2, my_post_token2);
return true;
}
static oauth2_cfg_server_callback_funcs_t _oauth2_check_proto_callbacks = {
_oauth2_check_proto_env_get_cb, _oauth2_check_proto_env_set_cb,
_oauth2_check_proto_read_form_post};
START_TEST(test_proto_get_source_token_environment)
{
char *token = NULL;
char *rv = NULL;
oauth2_cfg_source_token_t *cfg = NULL, *cfg2 = NULL;
oauth2_http_request_t *request = NULL;
request = oauth2_http_request_init(_log);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, my_env_var_token);
oauth2_mem_free(token);
cfg2 = oauth2_cfg_source_token_clone(_log, cfg);
oauth2_cfg_source_token_free(_log, cfg);
rv = oauth2_cfg_source_token_set_accept_in(_log, NULL, NULL, NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_cfg_token_in_set(_log, NULL, NULL, NULL,
OAUTH2_CFG_TOKEN_IN_ENVVAR);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg2, "bogus", NULL);
ck_assert_ptr_ne(rv, NULL);
oauth2_mem_free(rv);
rv = oauth2_cfg_source_token_set_accept_in(
_log, cfg2, "environment", "name=access_token2&strip=false");
ck_assert_ptr_eq(rv, NULL);
ck_assert_uint_eq(oauth2_cfg_source_token_get_strip(cfg2), false);
token = oauth2_get_source_token(_log, cfg2, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, my_env_var_token2);
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg2);
oauth2_http_request_free(_log, request);
}
END_TEST
START_TEST(test_proto_get_source_token_header)
{
char *rv = NULL;
char *token = NULL;
oauth2_cfg_source_token_t *cfg = NULL, *cfg2 = NULL;
oauth2_http_request_t *request = NULL;
request = oauth2_http_request_init(_log);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "header", NULL);
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_header_set(_log, request, "Authorization",
"bearer my_header_token");
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_header_token");
oauth2_mem_free(token);
cfg2 = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg2, NULL);
oauth2_cfg_source_token_merge(_log, cfg2, NULL, cfg);
oauth2_cfg_source_token_free(_log, cfg);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg2, "header",
"type=other&name=MyHeader");
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_header_set(_log, request, "MyHeader",
"other my_other_token");
token = oauth2_get_source_token(_log, cfg2, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_other_token");
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg2);
oauth2_http_request_free(_log, request);
}
END_TEST
START_TEST(test_proto_get_source_token_query)
{
char *rv = NULL;
char *token = NULL;
oauth2_cfg_source_token_t *cfg = NULL, *cfg2 = NULL;
oauth2_http_request_t *request = NULL;
request = oauth2_http_request_init(_log);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "query", NULL);
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_query_set(_log, request,
"access_token=my_query_token");
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_query_token");
oauth2_mem_free(token);
oauth2_http_request_free(_log, request);
cfg2 = oauth2_cfg_source_token_clone(_log, cfg);
oauth2_cfg_source_token_free(_log, cfg);
request = oauth2_http_request_init(_log);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg2, "query",
"name=access_token2");
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_query_set(_log, request,
"access_token2=my_query_token2");
token = oauth2_get_source_token(_log, cfg2, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_query_token2");
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg2);
oauth2_http_request_free(_log, request);
}
END_TEST
START_TEST(test_proto_get_source_token_cookie)
{
char *rv = NULL;
char *token = NULL;
oauth2_cfg_source_token_t *cfg = NULL;
oauth2_http_request_t *request = NULL;
request = oauth2_http_request_init(_log);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "cookie", NULL);
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_cookie_set(_log, request, "access_token",
"my_cookie_token");
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_cookie_token");
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "cookie",
"name=access_token_cookie");
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_cookie_set(_log, request, "access_token_cookie",
"my_cookie_token2");
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_cookie_token2");
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg);
oauth2_http_request_free(_log, request);
}
END_TEST
START_TEST(test_proto_get_source_token_post)
{
const char *rv = NULL;
char *token = NULL;
oauth2_cfg_source_token_t *cfg = NULL;
oauth2_http_request_t *request = NULL;
request = oauth2_http_request_init(_log);
oauth2_http_request_method_set(_log, request, OAUTH2_HTTP_METHOD_POST);
oauth2_http_request_header_set(_log, request, "Content-Type",
"application/x-www-form-urlencoded");
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "post", NULL);
ck_assert_ptr_eq(rv, NULL);
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, my_post_token);
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "post",
"name=access_token2");
ck_assert_ptr_eq(rv, NULL);
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, my_post_token2);
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg);
oauth2_http_request_free(_log, request);
}
END_TEST
START_TEST(test_proto_get_source_token_basic)
{
char *rv = NULL;
char *token = NULL;
oauth2_cfg_source_token_t *cfg = NULL;
oauth2_http_request_t *request = NULL;
request = oauth2_http_request_init(_log);
cfg = oauth2_cfg_source_token_init(_log);
ck_assert_ptr_ne(cfg, NULL);
rv = oauth2_cfg_source_token_set_accept_in(_log, cfg, "basic", NULL);
ck_assert_ptr_eq(rv, NULL);
oauth2_http_request_header_set(_log, request, "Authorization",
"Basic ZHVtbXk6bXlfYmFzaWNfdG9rZW4=");
token = oauth2_get_source_token(_log, cfg, request,
&_oauth2_check_proto_callbacks, NULL);
ck_assert_ptr_ne(token, NULL);
ck_assert_str_eq(token, "my_basic_token");
oauth2_mem_free(token);
oauth2_cfg_source_token_free(_log, cfg);
oauth2_http_request_free(_log, request);
}
END_TEST
static char *token_endpoint_path = "/token";
static char *ropc_result_json = "{ \"access_token\": \"my_ropc_token\" }";
static char *oauth2_check_proto_serve_post(const char *request)
{
oauth2_nv_list_t *params = NULL;
char *data = NULL;
const char *grant_type = NULL;
const char *sep = "****";
char *rv = NULL;
if (strncmp(request, token_endpoint_path,
strlen(token_endpoint_path)) == 0) {
request += strlen(token_endpoint_path) + 5;
data = strstr(request, sep);
if (data == NULL)
goto error;
data += strlen(sep);
if (oauth2_parse_form_encoded_params(_log, data, ¶ms) ==
false)
goto error;
grant_type = oauth2_nv_list_get(_log, params, "grant_type");
if (grant_type == NULL)
goto error;
if ((grant_type) && (strcmp(grant_type, "password") == 0)) {
// TODO: check username password
rv = oauth2_strdup(ropc_result_json);
} else {
rv = oauth2_strdup(
"{ \"error\": \"unsupported grant_type\" }");
}
oauth2_nv_list_free(_log, params);
goto end;
}
error:
rv = oauth2_strdup("problem");
end:
return rv;
}
START_TEST(test_proto_ropc)
{
bool rc = false;
oauth2_cfg_ropc_t *cfg = NULL;
char *token = NULL;
oauth2_uint_t status_code = 0;
char *rv = NULL;
char *url = NULL;
url = oauth2_stradd(NULL, NULL, oauth2_check_http_base_url(),
token_endpoint_path);
cfg = oauth2_cfg_ropc_init(_log);
rv = oauth2_cfg_set_ropc(_log, cfg, url, NULL);
ck_assert_ptr_eq(rv, NULL);
rc = oauth2_ropc_exec(_log, cfg, "joe", "2Federate", &token,
&status_code);
ck_assert_int_eq(rc, true);
ck_assert_str_eq(token, "my_ropc_token");
oauth2_mem_free(token);
oauth2_cfg_ropc_free(_log, cfg);
oauth2_mem_free(url);
}
END_TEST
Suite *oauth2_check_proto_suite()
{
Suite *s = suite_create("proto");
TCase *c = tcase_create("core");
liboauth2_check_register_http_callbacks(
oauth2_check_http_base_path(), NULL, oauth2_check_proto_serve_post);
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_proto_get_source_token_environment);
tcase_add_test(c, test_proto_get_source_token_header);
tcase_add_test(c, test_proto_get_source_token_query);
tcase_add_test(c, test_proto_get_source_token_cookie);
tcase_add_test(c, test_proto_get_source_token_post);
tcase_add_test(c, test_proto_get_source_token_basic);
tcase_add_test(c, test_proto_ropc);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_util.c 0000664 0000000 0000000 00000023617 14161030125 0016650 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include
#include
#include
static oauth2_mem_alloc_fn_t _save_alloc = NULL;
static oauth2_mem_realloc_fn_t _save_realloc = NULL;
static oauth2_mem_dealloc_fn_t _save_dealloc = NULL;
static void *test_alloc(size_t amt)
{
return malloc(amt);
}
static void *test_realloc(void *ptr, size_t amt)
{
return realloc(ptr, amt);
}
static void test_dealloc(void *ptr)
{
free(ptr);
}
static void test_mem_functions_set()
{
_save_alloc = oauth2_mem_get_alloc();
_save_realloc = oauth2_mem_get_realloc();
_save_dealloc = oauth2_mem_get_dealloc();
oauth2_mem_set_alloc_funcs(test_alloc, test_realloc, test_dealloc);
}
static void test_mem_functions_reset()
{
oauth2_mem_set_alloc_funcs(_save_alloc, _save_realloc, _save_dealloc);
_save_alloc = NULL;
_save_realloc = NULL;
_save_dealloc = NULL;
}
static void *test_alloc3(size_t amt, const char *file, int line)
{
return malloc(amt);
}
static void *test_realloc3(void *ptr, size_t amt, const char *file, int line)
{
return realloc(ptr, amt);
}
static void test_dealloc3(void *ptr, const char *file, int line)
{
free(ptr);
}
static oauth2_mem_alloc3_fn_t _save_alloc3 = NULL;
static oauth2_mem_realloc3_fn_t _save_realloc3 = NULL;
static oauth2_mem_dealloc3_fn_t _save_dealloc3 = NULL;
static void test_mem_functions_set3()
{
_save_alloc3 = oauth2_mem_get_alloc3();
_save_realloc3 = oauth2_mem_get_realloc3();
_save_dealloc3 = oauth2_mem_get_dealloc3();
oauth2_mem_set_alloc_ex_funcs(test_alloc3, test_realloc3,
test_dealloc3);
}
/*
* TODO: why does this result in a timeout?
* probably we can call this only once anyhow, but would it affect the
other check_util tests?
* perhaps separate it out in a different suite then?
static void test_mem_functions_reset3() {
cjose_set_alloc_ex_funcs(_save_alloc3, _save_realloc3, _save_dealloc3);
oauth2_mem_set_alloc_ex_funcs(_save_alloc3, _save_realloc3,
_save_dealloc3);
_save_alloc3 = NULL;
_save_realloc3 = NULL;
_save_dealloc3 = NULL;
}
*/
static oauth2_log_t *_log = 0;
static void setup(void)
{
// provide coverage for oauth2_mem_calloc_callback
// NB: the setup for cURL can only be initialized once and stays this
// way
test_mem_functions_set();
CURL *curl1 = NULL, *curl2 = NULL;
curl1 = curl_easy_init();
curl2 = curl_easy_duphandle(curl1);
curl_easy_cleanup(curl2);
curl_easy_cleanup(curl1);
// for coverage
oauth2_log_free(NULL);
_log = oauth2_init(OAUTH2_LOG_TRACE1, 0);
}
static void teardown(void)
{
oauth2_shutdown(_log);
}
START_TEST(test_log)
{
// mostly to complete coverage
// TODO: could return bytes written from oauth2_log statements
oauth2_debug(NULL, NULL);
// TOOD: could return bool from oauth2_log_sink_add
oauth2_log_sink_add(_log, &oauth2_log_sink_stderr);
oauth2_info(_log, NULL);
oauth2_info(_log, "");
oauth2_log_sink_level_set(&oauth2_log_sink_stderr, OAUTH2_LOG_ERROR);
}
END_TEST
START_TEST(test_mem)
{
void *ptr = NULL;
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
test_mem_functions_set();
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
ptr = oauth2_mem_alloc(8);
ptr = oauth2_mem_get_realloc()(ptr, 8);
oauth2_mem_free(ptr);
test_mem_functions_reset();
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
test_mem_functions_set3();
ck_assert(NULL != oauth2_mem_get_alloc());
ck_assert(NULL != oauth2_mem_get_realloc());
ck_assert(NULL != oauth2_mem_get_dealloc());
ck_assert(NULL != oauth2_mem_get_alloc3());
ck_assert(NULL != oauth2_mem_get_realloc3());
ck_assert(NULL != oauth2_mem_get_dealloc3());
ptr = oauth2_mem_alloc(8);
ptr = oauth2_mem_get_realloc()(ptr, 8);
oauth2_mem_free(ptr);
// test_mem_functions_reset3();
}
END_TEST
START_TEST(test_strdup)
{
char *src = NULL, *dst = NULL;
src = "bla";
dst = NULL;
dst = oauth2_strdup(src);
ck_assert_ptr_ne(dst, NULL);
ck_assert_str_eq(src, dst);
oauth2_mem_free(dst);
src = NULL;
dst = NULL;
dst = oauth2_strdup(src);
ck_assert_ptr_eq(dst, NULL);
oauth2_mem_free(dst);
}
END_TEST
START_TEST(test_base64url_encode)
{
size_t dst_len;
char *dst;
const char *plain = "Node.js is awesome.";
const char *encoded = "Tm9kZS5qcyBpcyBhd2Vzb21lLg";
dst_len = oauth2_base64url_encode(_log, (const uint8_t *)plain,
strlen(plain), &dst);
ck_assert_int_eq(dst_len, strlen(encoded));
ck_assert_str_eq(dst, encoded);
oauth2_mem_free(dst);
dst = NULL;
dst_len = oauth2_base64url_encode(_log, NULL, 0, &dst);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
dst_len = oauth2_base64url_encode(_log, NULL, 0, NULL);
ck_assert_int_eq(dst_len, 0);
dst_len = oauth2_base64url_encode(_log, (const uint8_t *)"", 0, NULL);
ck_assert_int_eq(dst_len, 0);
}
END_TEST
START_TEST(test_base64url_decode)
{
uint8_t *dst;
size_t dst_len;
bool rc;
const char *encoded = "Tm9kZS5qcyBpcyBhd2Vzb21lLg";
const char *plain = "Node.js is awesome.";
rc = oauth2_base64url_decode(_log, encoded, &dst, &dst_len);
ck_assert_int_eq(rc, true);
ck_assert_int_eq(dst_len, strlen(plain));
ck_assert(strncmp((const char *)dst, plain, dst_len) == 0);
oauth2_mem_free(dst);
dst = NULL;
dst_len = 0;
rc = oauth2_base64url_decode(_log, NULL, &dst, &dst_len);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
rc = oauth2_base64url_decode(_log, NULL, NULL, 0);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
rc = oauth2_base64url_decode(_log, "", NULL, 0);
ck_assert_int_eq(rc, false);
ck_assert_ptr_eq(dst, NULL);
ck_assert_int_eq(dst_len, 0);
}
END_TEST
START_TEST(test_url_encode)
{
char *src = NULL, *dst = NULL, *enc = NULL;
src = "bla bla";
enc = "bla%20bla";
dst = oauth2_url_encode(_log, src);
ck_assert_str_eq(dst, enc);
oauth2_mem_free(dst);
src = "Hello Günter";
enc = "Hello%20G%C3%BCnter";
dst = oauth2_url_encode(_log, src);
ck_assert_str_eq(dst, enc);
oauth2_mem_free(dst);
dst = NULL;
src = NULL;
dst = oauth2_url_encode(_log, src);
ck_assert_ptr_eq(dst, NULL);
}
END_TEST
START_TEST(test_url_decode)
{
char *dst = NULL, *src = NULL, *dec = NULL;
src = "bla%20bla";
dec = "bla bla";
dst = oauth2_url_decode(_log, src);
ck_assert_str_eq(dst, dec);
oauth2_mem_free(dst);
dst = NULL;
src = "http://www.example.com/path/foo+bar/path?query+name=query+value";
dec = "http://www.example.com/path/foo bar/path?query name=query value";
dst = oauth2_url_decode(_log, src);
ck_assert_str_eq(dst, dec);
oauth2_mem_free(dst);
src = "Hello%20G%C3%BCnter";
dec = "Hello Günter";
dst = oauth2_url_decode(_log, src);
ck_assert_str_eq(dst, dec);
oauth2_mem_free(dst);
dst = NULL;
src = NULL;
dst = oauth2_url_decode(_log, src);
ck_assert_ptr_eq(dst, NULL);
}
END_TEST
START_TEST(test_html_encode)
{
char *src = NULL, *dst = NULL, *enc = NULL;
src = "bla&bla";
enc = "bla&bla";
dst = oauth2_html_escape(_log, src);
ck_assert_str_eq(dst, enc);
oauth2_mem_free(dst);
dst = NULL;
// https://www.w3schools.com/php/func_string_htmlentities.asp
src = "Go to w3schools.com";
enc = "<a href="https://www.w3schools.com">Go to "
"w3schools.com</a>";
dst = oauth2_html_escape(_log, src);
ck_assert_str_eq(dst, enc);
oauth2_mem_free(dst);
dst = NULL;
src = NULL;
dst = oauth2_html_escape(_log, src);
ck_assert_ptr_eq(dst, NULL);
}
END_TEST
START_TEST(test_random)
{
char *rv = NULL;
rv = oauth2_rand_str(_log, 8);
ck_assert_ptr_ne(rv, NULL);
ck_assert_str_ne(rv, "");
ck_assert_int_eq(strlen(rv), 8);
oauth2_mem_free(rv);
rv = oauth2_rand_str(_log, 16);
ck_assert_ptr_ne(rv, NULL);
ck_assert_str_ne(rv, "");
ck_assert_int_eq(strlen(rv), 16);
oauth2_mem_free(rv);
rv = oauth2_rand_str(_log, 7);
ck_assert_ptr_ne(rv, NULL);
ck_assert_str_ne(rv, "");
ck_assert_int_eq(strlen(rv), 7);
oauth2_mem_free(rv);
}
END_TEST
Suite *oauth2_check_util_suite()
{
Suite *s = suite_create("util");
TCase *c = tcase_create("core");
tcase_add_checked_fixture(c, setup, teardown);
tcase_add_test(c, test_log);
tcase_add_test(c, test_mem);
tcase_add_test(c, test_strdup);
tcase_add_test(c, test_base64url_encode);
tcase_add_test(c, test_base64url_decode);
tcase_add_test(c, test_url_encode);
tcase_add_test(c, test_url_decode);
tcase_add_test(c, test_html_encode);
tcase_add_test(c, test_random);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/check_version.c 0000664 0000000 0000000 00000003422 14161030125 0017350 0 ustar 00root root 0000000 0000000 /***************************************************************************
*
* Copyright (C) 2018-2021 - ZmartZone Holding BV - www.zmartzone.eu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
*
**************************************************************************/
#include "check_liboauth2.h"
#include "oauth2/mem.h"
#include "oauth2/util.h"
#include "oauth2/version.h"
#include
START_TEST(test_version_defines)
{
ck_assert_str_eq(OAUTH2_PACKAGE_VERSION, PACKAGE_VERSION);
ck_assert_str_eq(OAUTH2_PACKAGE_NAME, PACKAGE_NAME);
}
END_TEST
START_TEST(test_version_function)
{
const char *version = oauth2_version();
ck_assert_str_eq(version, PACKAGE_VERSION);
}
END_TEST
START_TEST(test_package_string)
{
const char *pkg_str = oauth2_package_string();
ck_assert_str_eq(pkg_str, PACKAGE_NAME "-" PACKAGE_VERSION);
}
END_TEST
Suite *oauth2_check_version_suite()
{
Suite *s = suite_create("version");
TCase *c = tcase_create("core");
tcase_add_test(c, test_version_defines);
tcase_add_test(c, test_version_function);
tcase_add_test(c, test_package_string);
suite_add_tcase(s, c);
return s;
}
liboauth2-1.4.4/test/client.json 0000664 0000000 0000000 00000000100 14161030125 0016521 0 ustar 00root root 0000000 0000000 {
"client_id": "a_client",
"client_secret": "a_secret"
}
liboauth2-1.4.4/test/provider.json 0000664 0000000 0000000 00000000664 14161030125 0017114 0 ustar 00root root 0000000 0000000 {
"issuer": "https://pingfed:9031",
"authorization_endpoint": "https://pingfed:9031/as/authorization.oauth2",
"token_endpoint": "https://pingfed:9031/as/token.oauth2",
"revocation_endpoint": "https://pingfed:9031/as/revoke_token.oauth2",
"userinfo_endpoint": "https://pingfed:9031/idp/userinfo.openid",
"introspection_endpoint": "https://pingfed:9031/as/introspect.oauth2",
"jwks_uri": "https://pingfed:9031/pf/JWKS"
}
liboauth2-1.4.4/test/server_stubs.c 0000664 0000000 0000000 00000006167 14161030125 0017265 0 ustar 00root root 0000000 0000000 #ifdef HAVE_APACHE
#include
#include
static char *substring_conf(apr_pool_t *p, const char *start, int len,
char quote)
{
char *result = apr_palloc(p, len + 1);
char *resp = result;
int i;
for (i = 0; i < len; ++i) {
if (start[i] == '\\' &&
(start[i + 1] == '\\' || (quote && start[i + 1] == quote)))
*resp++ = start[++i];
else
*resp++ = start[i];
}
*resp++ = '\0';
#if RESOLVE_ENV_PER_TOKEN
return (char *)ap_resolve_env(p, result);
#else
return result;
#endif
}
AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
{
const char *str = *line, *strend;
char *res;
char quote;
while (apr_isspace(*str))
++str;
if (!*str) {
*line = str;
return "";
}
if ((quote = *str) == '"' || quote == '\'') {
strend = str + 1;
while (*strend && *strend != quote) {
if (*strend == '\\' && strend[1] &&
(strend[1] == quote || strend[1] == '\\')) {
strend += 2;
} else {
++strend;
}
}
res = substring_conf(p, str + 1, strend - str - 1, quote);
if (*strend == quote)
++strend;
} else {
strend = str;
while (*strend && !apr_isspace(*strend))
++strend;
res = substring_conf(p, str, strend - str, 0);
}
while (apr_isspace(*strend))
++strend;
*line = strend;
return res;
}
AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line)
{
return ap_getword_conf(p, (const char **)line);
}
AP_DECLARE(int) ap_should_client_block(request_rec *r)
{
return 0;
}
AP_DECLARE(long)
ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz)
{
return 0;
}
AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
{
return "www.example.com";
}
AP_DECLARE(const char *) ap_get_server_name_for_url(request_rec *r)
{
return "www.example.com";
}
AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
{
return 0;
}
AP_DECLARE(const char *) ap_auth_type(request_rec *r)
{
return "oauth2";
}
AP_DECLARE(const char *) ap_auth_name(request_rec *r)
{
return "oauth2";
}
const char *ap_run_http_scheme(const request_rec *r)
{
return "https";
}
#endif
#ifdef HAVE_NGINX
#undef LF
#undef CR
#undef CRLF
#include
#include
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
const char *fmt, ...)
{
}
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *pool = (ngx_pool_t *)oauth2_mem_alloc(size);
return pool;
}
void ngx_destroy_pool(ngx_pool_t *pool)
{
oauth2_mem_free(pool);
}
void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
void *p = (void *)oauth2_mem_alloc(size);
return p;
}
void *ngx_list_push(ngx_list_t *l)
{
void *elt;
ngx_list_part_t *last;
last = l->last;
if (last->nelts == l->nalloc) {
/* the last part is full, allocate a new list part */
last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
if (last == NULL) {
return NULL;
}
last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
if (last->elts == NULL) {
return NULL;
}
last->nelts = 0;
last->next = NULL;
l->last->next = last;
l->last = last;
}
elt = (char *)last->elts + l->size * last->nelts;
last->nelts++;
return elt;
}
#endif