Initial import of version 0.0.6
git-svn-id: https://modmellon.googlecode.com/svn/trunk@3 a716ebb1-153a-0410-b759-cfb97c6a1b53
This commit is contained in:
commit
1fa6146abe
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
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
|
||||
this service 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.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE 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.
|
||||
|
||||
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
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
# Source files. mod_auth_mellon.c must be the first file.
|
||||
SRC=mod_auth_mellon.c \
|
||||
auth_mellon_cache.c auth_mellon_config.c \
|
||||
auth_mellon_cookie.c auth_mellon_handler.c \
|
||||
auth_mellon_util.c \
|
||||
auth_mellon_session.c \
|
||||
auth_mellon_httpclient.c
|
||||
|
||||
# Files to include when making a .tar.gz-file for distribution
|
||||
DISTFILES=$(SRC) \
|
||||
auth_mellon.h \
|
||||
configure \
|
||||
configure.ac \
|
||||
Makefile.in \
|
||||
autogen.sh \
|
||||
TODO \
|
||||
README \
|
||||
COPYING \
|
||||
debian/auth_mellon.conf \
|
||||
debian/auth_mellon.load \
|
||||
debian/changelog \
|
||||
debian/compat \
|
||||
debian/control \
|
||||
debian/copyright \
|
||||
debian/dirs \
|
||||
debian/docs \
|
||||
debian/install \
|
||||
debian/rules
|
||||
|
||||
|
||||
all: mod_auth_mellon.la
|
||||
|
||||
mod_auth_mellon.la: $(SRC) auth_mellon.h
|
||||
@APXS2@ @OPENSSL_CFLAGS@ @LASSO_CFLAGS@ @CURL_CFLAGS@ @OPENSSL_LIBS@ @LASSO_LIBS@ @CURL_LIBS@ -Wc,-Wall -c $(SRC)
|
||||
|
||||
|
||||
# Building configure (for distribution)
|
||||
configure: configure.ac
|
||||
./autogen.sh
|
||||
|
||||
@NAMEVER@.tar.gz: $(DISTFILES)
|
||||
tar -c --transform="s#^#@NAMEVER@/#" -vzf $@ $(DISTFILES)
|
||||
|
||||
|
||||
.PHONY: install
|
||||
install: mod_auth_mellon.la
|
||||
@APXS2@ -i -n auth_mellon $<
|
||||
|
||||
.PHONY: distfile
|
||||
distfile: @NAMEVER@.tar.gz
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f mod_auth_mellon.la
|
||||
rm -f $(SRC:%.c=%.lo)
|
||||
rm -f $(SRC:%.c=%.slo)
|
||||
rm -rf .libs/
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -f Makefile config.log config.status @NAMEVER@.tar.gz *~ \
|
||||
build-stamp config.guess config.sub
|
||||
rm -rf debian/mod-auth-mellon
|
||||
rm -f debian/files
|
||||
|
||||
.PHONY: fullclean
|
||||
fullclean: distclean
|
||||
rm -f configure aclocal.m4
|
|
@ -0,0 +1,334 @@
|
|||
===========================================================================
|
||||
README file for mod_auth_mellon
|
||||
===========================================================================
|
||||
|
||||
mod_auth_mellon is a authentication module for apache. It authenticates
|
||||
the user against a SAML 2.0 IdP, and and grants access to directories
|
||||
depending on attributes received from the IdP.
|
||||
|
||||
|
||||
===========================================================================
|
||||
Dependencies
|
||||
===========================================================================
|
||||
|
||||
mod_auth_mellon has four dependencies:
|
||||
* pkg-config
|
||||
* Apache (>=2.0)
|
||||
* OpenSSL
|
||||
* lasso (>=2.1)
|
||||
|
||||
You will also require developement headers and tools for all of the
|
||||
dependencies.
|
||||
|
||||
If OpenSSL or lasso are installed in a "strange" directory, then you may
|
||||
have to specify the directory containing "lasso.pc" and/or "openssl.pc" in
|
||||
the PKG_CONFIG_PATH environment variable. For example, if openssl is
|
||||
installed in /usr/local/openssl (with openssl.pc in
|
||||
/usr/local/openssl/lib/pkgconfig/) and lasso is installed in /opt/lasso
|
||||
(lasso.pc in /opt/lasso/lib/pkgconfig/), then you can set PKG_CONFIG_PATH
|
||||
before running configure like this:
|
||||
|
||||
PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig:/opt/lasso/lib/pkgconfig
|
||||
export PKG_CONFIG_PATH
|
||||
|
||||
|
||||
If Apache is installed in a "strange" directory, then you may have to
|
||||
specify the path to apxs2 using the --with-apxs2=/full/path/to/apxs2
|
||||
option to configure. If, for example, Apache is installed in /opt/apache,
|
||||
with apxs2 in /opt/apache/bin, then you run
|
||||
|
||||
./configure --with-apxs2=/opt/apache2/bin/apxs2
|
||||
|
||||
Note that, depending on your distribution, apxs2 may be named apxs.
|
||||
|
||||
|
||||
===========================================================================
|
||||
Installing mod_auth_mellon
|
||||
===========================================================================
|
||||
|
||||
mod_auth_mellon uses autoconf, and can be installed by running the
|
||||
following commands:
|
||||
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
|
||||
===========================================================================
|
||||
Configuring mod_auth_mellon
|
||||
===========================================================================
|
||||
|
||||
Here we are going to assume that your web servers hostname is
|
||||
'example.com', and that the directory you are going to protect is
|
||||
'http://example.com/secret/'. We are also going to assume that you have
|
||||
configured your web site to use SSL.
|
||||
|
||||
You need to edit the configuration file for your web server. Depending on
|
||||
your distribution, it may be named '/etc/apache/httpd.conf' or something
|
||||
different.
|
||||
|
||||
|
||||
You need to add a LoadModule directove for mod_auth_mellon. This will
|
||||
look similar to this:
|
||||
|
||||
LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so
|
||||
|
||||
To find the full path to mod_auth_mellon.so, you may run:
|
||||
|
||||
apxs2 -q LIBEXECDIR
|
||||
|
||||
This will print the path where Apache stores modules. mod_auth_mellon.so
|
||||
will be stored in that directory.
|
||||
|
||||
|
||||
After you have added the LoadModule directive, you must add configuration
|
||||
for mod_auth_mellon. The following is an example configuration:
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Global configuration for mod_auth_mellon. This configuration is shared by
|
||||
# every virtual server and location in this instance of apache.
|
||||
###########################################################################
|
||||
|
||||
# MellonCacheSize sets the maximum number of sessions which can be active
|
||||
# at once. When mod_auth_mellon reaches this limit, it will begin removing
|
||||
# the least recently used sessions. The server must be restarted before any
|
||||
# changes to this option takes effect.
|
||||
# Default: MellonCacheSize 100
|
||||
MellonCacheSize 100
|
||||
|
||||
# MellonLockFile is the full path to a file used for synchronizing access
|
||||
# to the session data. The path should only be used by one instance of
|
||||
# apache at a time. The server must be restarted before any changes to this
|
||||
# option takes effect.
|
||||
# Default: MellonLockFile "/tmp/mellonLock"
|
||||
MellonLockFile "/tmp/mellonLock"
|
||||
|
||||
###########################################################################
|
||||
# End of global configuration for mod_auth_mellon.
|
||||
###########################################################################
|
||||
|
||||
|
||||
# This defines a directory where mod_auth_mellon should do access control.
|
||||
<Location /secret>
|
||||
|
||||
# These are standard Apache apache configuration directives.
|
||||
# See http://httpd.apache.org/docs/2.2/mod/core.html for information
|
||||
# about them.
|
||||
Require valid-user
|
||||
AuthType "Mellon"
|
||||
|
||||
|
||||
# MellonEnable is used to enable auth_mellon on a location.
|
||||
# It has three possible values: "off", "info" and "auth".
|
||||
# They have the following meanings:
|
||||
# "off": mod_auth_mellon will not do anything in this location.
|
||||
# This is the default state.
|
||||
# "info": If the user is authorized to access the resource, then
|
||||
# we will populate the environment with information about
|
||||
# the user. If the user isn't authorized, then we won't
|
||||
# populate the environment, but we won't deny the user
|
||||
# access either.
|
||||
# "auth": We will populate the environment with information about
|
||||
# the user if he is authorized. If he is authenticated
|
||||
# (logged in), but not authorized (according to the
|
||||
# MellonRequire directives, then we will return a 403
|
||||
# Forbidden error. If he isn't authenticated then we will
|
||||
# redirect him to the login page of the IdP.
|
||||
#
|
||||
# Default: MellonEnable "off"
|
||||
MellonEnable "auth"
|
||||
|
||||
# MellonDecoder is used to select which decoder mod_auth_mellon
|
||||
# will use when decoding attribute values.
|
||||
# There are two possible values: "none" and "feide". "none" is the
|
||||
# default.
|
||||
# They have the following meanings:
|
||||
# "none": mod_auth_mellon will store the attribute as it is
|
||||
# received from the IdP. This is the default behaviour.
|
||||
# "feide": FEIDE currently stores several values in a single
|
||||
# AttributeValue element. The values are base64 encoded
|
||||
# and separated by a underscore. This decoder reverses
|
||||
# this encoding.
|
||||
# Default: MellonDecoder "none"
|
||||
MellonDecoder "none"
|
||||
|
||||
# MellonVariable is used to select the name of the cookie which
|
||||
# mod_auth_mellon should use to remember the session id. If you
|
||||
# want to have different sites running on the same host, then
|
||||
# you will have to choose a different name for the cookie for each
|
||||
# site.
|
||||
# Default: "cookie"
|
||||
MellonVariable "cookie"
|
||||
|
||||
# MellonUser selects which attribute we should use for the username.
|
||||
# The username is passed on to other apache modules and to the web
|
||||
# page the user visits. NAME_ID is an attribute which we set to
|
||||
# the id we get from the IdP.
|
||||
# Default: MellonUser "NAME_ID"
|
||||
MellonUser "NAME_ID"
|
||||
|
||||
# MellonSetEnv configuration directives allows you to map
|
||||
# attribute names received from the IdP to names you choose
|
||||
# yourself. The syntax is 'MellonSetEnv <local name> <IdP name>'.
|
||||
# You can list multiple MellonSetEnv directives.
|
||||
# Default. None set.
|
||||
MellonSetEnv "e-mail" "mail"
|
||||
|
||||
# MellonRequire allows you to limit access to those with specific
|
||||
# attributes. The syntax is
|
||||
# 'MellonRequire <attribute name> <list of valid values>'.
|
||||
# Note that the attribute name is the name we received from the
|
||||
# IdP.
|
||||
#
|
||||
# If you don't list any MellonRequire directives, then any user
|
||||
# authenticated by the IdP will have access to this service. If
|
||||
# you list several MellonRequire directives, then all of them
|
||||
# will have to match.
|
||||
#
|
||||
# Default: None set.
|
||||
MellonRequire "eduPersonAffiliation" "student" "employee"
|
||||
|
||||
# MellonEndpointPath selects which directory mod_auth_mellon
|
||||
# should assume contains the SAML 2.0 endpoints. Any request to
|
||||
# this directory will be handled by mod_auth_mellon.
|
||||
#
|
||||
# The path is the full path (from the root of the web server) to
|
||||
# the directory. The directory must be a sub-directory of this
|
||||
# <Location ...>.
|
||||
# Default: MellonEndpointPath "/mellon"
|
||||
MellonEndpointPath "/secret/endpoint"
|
||||
|
||||
# MellonSessionLength sets the maximum lifetime of a session, in
|
||||
# seconds. The actual lifetime may be shorter, depending on the
|
||||
# conditions received from the IdP. The default length is 86400
|
||||
# seconds, which is one day.
|
||||
# Default: MellonSessionLength 86400
|
||||
MellonSessionLength 86400
|
||||
|
||||
# MellonNoCookieErrorPage is the full path to a page which
|
||||
# mod_auth_mellon will redirect the user to if he returns from the
|
||||
# IdP without a cookie with a session id.
|
||||
# Note that the user may also get this error if he for some reason
|
||||
# loses the cookie between being redirected to the IdPs login page
|
||||
# and returning from it.
|
||||
# If this option is unset, then mod_auth_mellon will return a
|
||||
# 400 Bad Request error if the cookie is missing.
|
||||
# Default: unset
|
||||
MellonNoCookieErrorPage "https://example.com/no_cookie.html"
|
||||
|
||||
# MellonSPMetadataFile is the full path to the file containing
|
||||
# the metadata for this service provider. You must configure this
|
||||
# before you can use this module.
|
||||
# Default: None set.
|
||||
MellonSPMetadataFile /etc/apache2/mellon/sp-metadata.xml
|
||||
|
||||
# MellonSPPrivateKeyFile is a .pem file which contains the private
|
||||
# key of the service provider. The .pem-file cannot be encrypted
|
||||
# with a password. This directive is optional.
|
||||
# Default: None set.
|
||||
MellonSPPrivateKeyFile /etc/apache2/mellon/sp-private-key.pem
|
||||
|
||||
# MellonIdPMetadataFile is the full path to the file which contains
|
||||
# metadata for the IdP you are authenticating against. This
|
||||
# directive is required.
|
||||
# Default: None set.
|
||||
MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml
|
||||
|
||||
# MellonIdpPublicKeyFile is the full path of the public key of the
|
||||
# IdP. This parameter is optional if the public key is embedded
|
||||
# in the IdP's metadata file.
|
||||
# Default: None set.
|
||||
MellonIdPPublicKeyFile /etc/apache2/mellon/idp-public-key.pem
|
||||
</Location>
|
||||
|
||||
|
||||
===========================================================================
|
||||
Service provider metadata
|
||||
===========================================================================
|
||||
|
||||
The contents of the metadata will depend on your hostname and on what path
|
||||
you selected with the MellonEndpointPath configuration directive.
|
||||
|
||||
The following is an example of metadata for the example configuration:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<EntityDescriptor
|
||||
entityID="examlpe.com"
|
||||
xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
|
||||
<SPSSODescriptor
|
||||
AuthnRequestsSigned="false"
|
||||
WantAssertionsSigned="false"
|
||||
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<SingleLogoutService
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
||||
Location="https://example.com/secret/endpoint/logoutRequest" />
|
||||
<NameIDFormat>
|
||||
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||
</NameIDFormat>
|
||||
<AssertionConsumerService
|
||||
index="0"
|
||||
isDefault="true"
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="https://example.com/secret/endpoint/postResponse" />
|
||||
</SPSSODescriptor>
|
||||
</EntityDescriptor>
|
||||
|
||||
|
||||
You should update entityID="example.com" and the two Location attributes.
|
||||
Note that '/secret/endpoint' in the two Location attributes matches the
|
||||
path set in MellonEndpointPath.
|
||||
|
||||
To use HTTP-Artifact binding instead of the HTTP-POST binding, change
|
||||
the AssertionConsumerService-element to something like this:
|
||||
|
||||
<AssertionConsumerService
|
||||
index="0"
|
||||
isDefault="true"
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
|
||||
Location="https://example.com/secret/endpoint/artifactResponse" />
|
||||
|
||||
|
||||
===========================================================================
|
||||
Using mod_auth_mellon
|
||||
===========================================================================
|
||||
|
||||
After you have set up mod_auth_mellon, you should be able to visit (in our
|
||||
example) https://example.com/secret/, and be redirected to the IdP's login
|
||||
page. After logging in you should be returned to
|
||||
https://example.com/secret/, and get the contents of that page.
|
||||
|
||||
When authenticating a user, mod_auth_mellon will set some environment
|
||||
variables to the attributes it received from the IdP. The name of the
|
||||
variables will be MELLON_<attribute name>. If you have specified a
|
||||
different name with the MellonSetEnv configuration directive, then that
|
||||
name will be used instead. The name will still be prefixed by 'MELLON_'.
|
||||
|
||||
The value of the attribute will be base64 decoded.
|
||||
|
||||
mod_auth_mellon supports multivalued attributes with the following format:
|
||||
<base64 encoded value>_<base64 encoded value>_<base 64 encoded value>...
|
||||
|
||||
If an attribute has multiple values, then they will be stored as
|
||||
MELLON_<name>_0, MELLON_<name>_1, MELLON_<name>_2, ...
|
||||
|
||||
Since mod_auth_mellon doesn't know which attributes may have multiple
|
||||
values, it will store every attribute at least twice. Once named
|
||||
MELLON_<name>, and once named <MELLON_<name>_0.
|
||||
|
||||
In the case of multivalued attributes MELLON_<name> will contain the first
|
||||
value.
|
||||
|
||||
|
||||
The following code is a simple php-script which prints out all the
|
||||
variables:
|
||||
|
||||
<?php
|
||||
header('Content-Type: text/plain');
|
||||
|
||||
foreach($_SERVER as $key=>$value) {
|
||||
if(substr($key, 0, 7) == 'MELLON_') {
|
||||
echo($key . '=' . $value . "\r\n");
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,4 @@
|
|||
TODO for auth_mellon:
|
||||
|
||||
* Change session storage to use less than 64KiB/session.
|
||||
* Optimize session lookup.
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
*
|
||||
* auth_mellon.h: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MOD_AUTH_MELLON_H
|
||||
#define MOD_AUTH_MELLON_H
|
||||
|
||||
#include <lasso/lasso.h>
|
||||
#include <lasso/xml/saml-2.0/samlp2_authn_request.h>
|
||||
#include <lasso/xml/saml-2.0/samlp2_response.h>
|
||||
#include <lasso/xml/saml-2.0/saml2_assertion.h>
|
||||
#include <lasso/xml/saml-2.0/saml2_attribute_statement.h>
|
||||
#include <lasso/xml/saml-2.0/saml2_attribute.h>
|
||||
#include <lasso/xml/saml-2.0/saml2_attribute_value.h>
|
||||
#include <lasso/xml/misc_text_node.h>
|
||||
|
||||
/* The following are redefined in ap_config_auto.h */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
#undef PACKAGE_NAME
|
||||
#undef PACKAGE_STRING
|
||||
#undef PACKAGE_TARNAME
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
#undef HAVE_TIMEGM /* is redefined again in ap_config.h */
|
||||
|
||||
#include "apr_base64.h"
|
||||
#include "apr_time.h"
|
||||
#include "apr_strings.h"
|
||||
#include "apr_shm.h"
|
||||
#include "apr_md5.h"
|
||||
|
||||
#include "ap_config.h"
|
||||
#include "httpd.h"
|
||||
#include "http_config.h"
|
||||
#include "http_core.h"
|
||||
#include "http_log.h"
|
||||
#include "http_protocol.h"
|
||||
#include "http_request.h"
|
||||
|
||||
|
||||
/* Size definitions for the session cache.
|
||||
*/
|
||||
#define AM_CACHE_KEYSIZE 120
|
||||
#define AM_CACHE_VARSIZE 128
|
||||
#define AM_CACHE_VALSIZE 512-AM_CACHE_VARSIZE
|
||||
#define AM_CACHE_ENVSIZE 128
|
||||
#define AM_CACHE_USERSIZE 512
|
||||
#define AM_CACHE_MAX_LASSO_IDENTITY_SIZE 1024
|
||||
#define AM_CACHE_MAX_LASSO_SESSION_SIZE 3074
|
||||
|
||||
|
||||
/* This is the length of the session id we use.
|
||||
*/
|
||||
#define AM_SESSION_ID_LENGTH 32
|
||||
|
||||
|
||||
#define am_get_srv_cfg(s) (am_srv_cfg_rec *)ap_get_module_config((s)->module_config, &auth_mellon_module)
|
||||
|
||||
#define am_get_mod_cfg(s) (am_get_srv_cfg((s)))->mc
|
||||
|
||||
#define am_get_dir_cfg(r) (am_dir_cfg_rec *)ap_get_module_config((r)->per_dir_config, &auth_mellon_module)
|
||||
|
||||
|
||||
typedef struct am_mod_cfg_rec {
|
||||
int cache_size;
|
||||
const char *lock_file;
|
||||
|
||||
/* These variables can't be allowed to change after the session store
|
||||
* has been initialized. Therefore we copy them before initializing
|
||||
* the session store.
|
||||
*/
|
||||
int init_cache_size;
|
||||
const char *init_lock_file;
|
||||
|
||||
apr_shm_t *cache;
|
||||
apr_global_mutex_t *lock;
|
||||
} am_mod_cfg_rec;
|
||||
|
||||
|
||||
typedef struct am_srv_cfg_rec {
|
||||
am_mod_cfg_rec *mc;
|
||||
} am_srv_cfg_rec;
|
||||
|
||||
typedef enum {
|
||||
am_enable_default,
|
||||
am_enable_off,
|
||||
am_enable_info,
|
||||
am_enable_auth
|
||||
} am_enable_t;
|
||||
|
||||
typedef enum {
|
||||
am_decoder_default,
|
||||
am_decoder_none,
|
||||
am_decoder_feide,
|
||||
} am_decoder_t;
|
||||
|
||||
|
||||
typedef struct am_dir_cfg_rec {
|
||||
/* enable_mellon is used to enable auth_mellon for a location.
|
||||
*/
|
||||
am_enable_t enable_mellon;
|
||||
|
||||
/* The decoder attribute is used to specify which decoder we should use
|
||||
* when parsing attributes.
|
||||
*/
|
||||
am_decoder_t decoder;
|
||||
|
||||
const char *varname;
|
||||
apr_hash_t *require;
|
||||
apr_hash_t *envattr;
|
||||
const char *userattr;
|
||||
|
||||
/* The "root directory" of our SAML2 endpoints. This path is relative
|
||||
* to the root of the web server.
|
||||
*
|
||||
* This path will always end with '/'.
|
||||
*/
|
||||
const char *endpoint_path;
|
||||
|
||||
/* Lasso configuration variables. */
|
||||
const char *sp_metadata_file;
|
||||
const char *sp_private_key_file;
|
||||
const char *idp_metadata_file;
|
||||
const char *idp_public_key_file;
|
||||
|
||||
/* Maximum number of seconds a session is valid for. */
|
||||
int session_length;
|
||||
|
||||
/* No cookie error page. */
|
||||
const char *no_cookie_error_page;
|
||||
|
||||
/* Mutex to prevent us from creating several lasso server objects. */
|
||||
apr_thread_mutex_t *server_mutex;
|
||||
/* Cached lasso server object. */
|
||||
LassoServer *server;
|
||||
} am_dir_cfg_rec;
|
||||
|
||||
|
||||
typedef struct am_cache_env_t {
|
||||
char varname[AM_CACHE_VARSIZE];
|
||||
char value[AM_CACHE_VALSIZE];
|
||||
} am_cache_env_t;
|
||||
|
||||
typedef struct am_cache_entry_t {
|
||||
char key[AM_CACHE_KEYSIZE];
|
||||
apr_time_t access;
|
||||
apr_time_t expires;
|
||||
int logged_in;
|
||||
unsigned short size;
|
||||
char user[AM_CACHE_USERSIZE];
|
||||
|
||||
/* Variables used to store lasso state between login requests
|
||||
*and logout requests.
|
||||
*/
|
||||
char lasso_identity[AM_CACHE_MAX_LASSO_IDENTITY_SIZE];
|
||||
char lasso_session[AM_CACHE_MAX_LASSO_SESSION_SIZE];
|
||||
|
||||
am_cache_env_t env[AM_CACHE_ENVSIZE];
|
||||
} am_cache_entry_t;
|
||||
|
||||
|
||||
|
||||
extern const command_rec auth_mellon_commands[];
|
||||
|
||||
void *auth_mellon_dir_config(apr_pool_t *p, char *d);
|
||||
void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add);
|
||||
void *auth_mellon_server_config(apr_pool_t *p, server_rec *s);
|
||||
|
||||
|
||||
const char *am_cookie_get(request_rec *r);
|
||||
void am_cookie_set(request_rec *r, const char *id);
|
||||
void am_cookie_delete(request_rec *r);
|
||||
|
||||
|
||||
am_cache_entry_t *am_cache_lock(server_rec *s, const char *key);
|
||||
am_cache_entry_t *am_cache_new(server_rec *s, const char *key);
|
||||
void am_cache_unlock(server_rec *s, am_cache_entry_t *entry);
|
||||
|
||||
void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires);
|
||||
|
||||
void am_cache_env_populate(request_rec *r, am_cache_entry_t *session);
|
||||
int am_cache_env_append(am_cache_entry_t *session,
|
||||
const char *var, const char *val);
|
||||
void am_cache_delete(server_rec *s, am_cache_entry_t *session);
|
||||
|
||||
int am_cache_set_lasso_state(am_cache_entry_t *session,
|
||||
const char *lasso_identity,
|
||||
const char *lasso_session);
|
||||
const char *am_cache_get_lasso_identity(am_cache_entry_t *session);
|
||||
const char *am_cache_get_lasso_session(am_cache_entry_t *session);
|
||||
|
||||
|
||||
am_cache_entry_t *am_get_request_session(request_rec *r);
|
||||
am_cache_entry_t *am_new_request_session(request_rec *r);
|
||||
void am_release_request_session(request_rec *r, am_cache_entry_t *session);
|
||||
void am_delete_request_session(request_rec *r, am_cache_entry_t *session);
|
||||
|
||||
|
||||
const char *am_reconstruct_url(request_rec *r);
|
||||
int am_check_permissions(request_rec *r, am_cache_entry_t *session);
|
||||
void am_set_nocache(request_rec *r);
|
||||
int am_read_post_data(request_rec *r, char **data, apr_size_t *length);
|
||||
char *am_extract_query_parameter(apr_pool_t *pool,
|
||||
const char *query_string,
|
||||
const char *name);
|
||||
char *am_urlencode(apr_pool_t *pool, const char *str);
|
||||
int am_urldecode(char *data);
|
||||
char *am_generate_session_id(request_rec *r);
|
||||
|
||||
|
||||
int am_auth_mellon_user(request_rec *r);
|
||||
int am_check_uid(request_rec *r);
|
||||
|
||||
|
||||
int am_httpclient_get(request_rec *r, const char *uri,
|
||||
void **buffer, apr_size_t *size);
|
||||
int am_httpclient_post(request_rec *r, const char *uri,
|
||||
const void *post_data, apr_size_t post_length,
|
||||
const char *content_type,
|
||||
void **buffer, apr_size_t *size);
|
||||
int am_httpclient_post_str(request_rec *r, const char *uri,
|
||||
const char *post_data,
|
||||
const char *content_type,
|
||||
void **buffer, apr_size_t *size);
|
||||
|
||||
|
||||
extern module AP_MODULE_DECLARE_DATA auth_mellon_module;
|
||||
|
||||
#endif /* MOD_AUTH_MELLON_H */
|
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
*
|
||||
* auth_mellon_cache.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
/* This function locks the session table and locates a session entry.
|
||||
* Unlocks the table and returns NULL if the entry wasn't found.
|
||||
* If a entry was found, then you _must_ unlock it with am_cache_unlock
|
||||
* after you are done with it.
|
||||
*
|
||||
* Parameters:
|
||||
* server_rec *s The current server.
|
||||
* const char *key The session key.
|
||||
*
|
||||
* Returns:
|
||||
* The session entry on success or NULL on failure.
|
||||
*/
|
||||
am_cache_entry_t *am_cache_lock(server_rec *s, const char *key)
|
||||
{
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
am_cache_entry_t *table;
|
||||
int i;
|
||||
|
||||
|
||||
/* Check if we have a valid session key. We abort if we don't. */
|
||||
if(key == NULL || strlen(key) != AM_SESSION_ID_LENGTH) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
mod_cfg = am_get_mod_cfg(s);
|
||||
|
||||
|
||||
/* Lock the table. */
|
||||
apr_global_mutex_lock(mod_cfg->lock);
|
||||
table = apr_shm_baseaddr_get(mod_cfg->cache);
|
||||
|
||||
|
||||
for(i = 0; i < mod_cfg->init_cache_size; i++) {
|
||||
if(strcmp(table[i].key, key) == 0) {
|
||||
/* We found the entry. */
|
||||
if(table[i].expires > apr_time_now()) {
|
||||
/* And it hasn't expired. */
|
||||
return &table[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* We didn't find a entry matching the key. Unlock the table and
|
||||
* return NULL;
|
||||
*/
|
||||
apr_global_mutex_unlock(mod_cfg->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function locks the session table and creates a new session entry.
|
||||
* It will first attempt to locate a free session. If it doesn't find a
|
||||
* free session, then it will take the least recentry used session.
|
||||
*
|
||||
* Remember to unlock the table with am_cache_unlock(...) afterwards.
|
||||
*
|
||||
* Parameters:
|
||||
* server_rec *s The current server.
|
||||
* const char *key The key of the session to allocate.
|
||||
*
|
||||
* Returns:
|
||||
* The new session entry on success. NULL if key is a invalid session
|
||||
* key.
|
||||
*/
|
||||
am_cache_entry_t *am_cache_new(server_rec *s, const char *key)
|
||||
{
|
||||
am_cache_entry_t *t;
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
am_cache_entry_t *table;
|
||||
apr_time_t current_time;
|
||||
int i;
|
||||
apr_time_t age;
|
||||
|
||||
/* Check if we have a valid session key. We abort if we don't. */
|
||||
if(key == NULL || strlen(key) != AM_SESSION_ID_LENGTH) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* First we try to find another session with the given key. */
|
||||
t = am_cache_lock(s, key);
|
||||
|
||||
|
||||
if(t == NULL) {
|
||||
/* We didn't find a previous session with the key. We will search
|
||||
* for the least recently used entry or a free entry in stead.
|
||||
*/
|
||||
|
||||
mod_cfg = am_get_mod_cfg(s);
|
||||
|
||||
|
||||
/* Lock the table. */
|
||||
apr_global_mutex_lock(mod_cfg->lock);
|
||||
table = apr_shm_baseaddr_get(mod_cfg->cache);
|
||||
|
||||
/* Get current time. If we find a entry with expires <= the current
|
||||
* time, then we can use it.
|
||||
*/
|
||||
current_time = apr_time_now();
|
||||
|
||||
/* We will use 't' to remember the best/oldest entry. We
|
||||
* initalize it to the first entry in the table to simplify the
|
||||
* following code (saves test for t == NULL).
|
||||
*/
|
||||
t = &table[0];
|
||||
|
||||
/* Iterate over the session table. Update 't' to match the "best"
|
||||
* entry (the least recently used). 't' will point a free entry
|
||||
* if we find one. Otherwise, 't' will point to the least recently
|
||||
* used entry.
|
||||
*/
|
||||
for(i = 0; i < mod_cfg->init_cache_size; i++) {
|
||||
if(table[i].key[0] == '\0') {
|
||||
/* This entry is free. Update 't' to this entry
|
||||
* and exit loop.
|
||||
*/
|
||||
t = &table[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if(table[i].expires <= current_time) {
|
||||
/* This entry is expired, and is therefore free.
|
||||
* Update 't' and exit loop.
|
||||
*/
|
||||
t = &table[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if(table[i].access < t->access) {
|
||||
/* This entry is older than 't' - update 't'. */
|
||||
t = &table[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(t->key[0] != '\0' && t->expires > current_time) {
|
||||
/* We dropped a LRU entry. Calculate the age in seconds. */
|
||||
age = (current_time - t->access) / 1000000;
|
||||
|
||||
if(age < 3600) {
|
||||
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
|
||||
"Dropping LRU entry entry with age = %llis,"
|
||||
" which is less than one hour. It may be a good"
|
||||
" idea to increase MellonCacheSize.",
|
||||
age);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now 't' points to the entry we are going to use. We initialize
|
||||
* it and returns it.
|
||||
*/
|
||||
|
||||
strcpy(t->key, key);
|
||||
|
||||
/* Far far into the future. */
|
||||
t->expires = 0x7fffffffffffffffLL;
|
||||
|
||||
t->logged_in = 0;
|
||||
t->size = 0;
|
||||
t->user[0] = '\0';
|
||||
|
||||
t->lasso_identity[0] = '\0';
|
||||
t->lasso_session[0] = '\0';
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
/* This function unlocks a session entry.
|
||||
*
|
||||
* Parameters:
|
||||
* server_rec *s The current server.
|
||||
* am_cache_entry_t *entry The session entry.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_cache_unlock(server_rec *s, am_cache_entry_t *entry)
|
||||
{
|
||||
am_mod_cfg_rec *mod_cfg;
|
||||
|
||||
/* Update access time. */
|
||||
entry->access = apr_time_now();
|
||||
|
||||
mod_cfg = am_get_mod_cfg(s);
|
||||
apr_global_mutex_unlock(mod_cfg->lock);
|
||||
}
|
||||
|
||||
|
||||
/* This function updates the expire-timestamp of a session, if the new
|
||||
* timestamp is earlier than the previous.
|
||||
*
|
||||
* Parameters:
|
||||
* am_cache_entry_t *t The current session.
|
||||
* apr_time_t expires The new timestamp.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_cache_update_expires(am_cache_entry_t *t, apr_time_t expires)
|
||||
{
|
||||
/* Check if we should update the expires timestamp. */
|
||||
if(t->expires == 0 || t->expires > expires) {
|
||||
t->expires = expires;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This function appends a name-value pair to a session. It is possible to
|
||||
* store several values with the same name. This is the method used to store
|
||||
* multivalued fields.
|
||||
*
|
||||
* Parameters:
|
||||
* am_cache_entry_t *t The current session.
|
||||
* const char *var The name of the value to be stored.
|
||||
* const char *val The value which should be stored in the session.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success or HTTP_INTERNAL_SERVER_ERROR on failure.
|
||||
*/
|
||||
int am_cache_env_append(am_cache_entry_t *t,
|
||||
const char *var, const char *val)
|
||||
{
|
||||
/* Make sure that the name and value will fit inside the
|
||||
* fixed size buffer.
|
||||
*/
|
||||
if(strlen(val) >= AM_CACHE_VALSIZE ||
|
||||
strlen(var) >= AM_CACHE_VARSIZE) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
|
||||
"Unable to store session data because it is to big. "
|
||||
"Name = \"%s\"; Value = \"%s\".", var, val);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if(t->size >= AM_CACHE_ENVSIZE) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
|
||||
"Unable to store attribute value because we have"
|
||||
" reached the maximum number of name-value pairs for"
|
||||
" this session.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
strcpy(t->env[t->size].varname, var);
|
||||
strcpy(t->env[t->size].value, val);
|
||||
t->size++;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function populates the subprocess environment with data received
|
||||
* from the IdP.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we should add the data to.
|
||||
* am_cache_entry_t *t The session data.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_cache_env_populate(request_rec *r, am_cache_entry_t *t)
|
||||
{
|
||||
am_dir_cfg_rec *d;
|
||||
int i;
|
||||
apr_hash_t *counters;
|
||||
const char *varname;
|
||||
const char *env_varname;
|
||||
const char *value;
|
||||
int *count;
|
||||
|
||||
d = am_get_dir_cfg(r);
|
||||
|
||||
/* Check if the user attribute has been set, and set it if it
|
||||
* hasn't been set. */
|
||||
if(t->user[0] == '\0') {
|
||||
for(i = 0; i < t->size; ++i) {
|
||||
if(strcmp(t->env[i].varname, d->userattr) == 0) {
|
||||
strcpy(t->user, t->env[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(t->user[0] != '\0') {
|
||||
/* We have a user-"name". Set r->user and r->ap_auth_type. */
|
||||
r->user = apr_pstrdup(r->pool, t->user);
|
||||
r->ap_auth_type = apr_pstrdup(r->pool, "Mellon");
|
||||
} else {
|
||||
/* We don't have a user-"name". Log error. */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
|
||||
"Didn't find the attribute \"%s\" in the attributes"
|
||||
" which were received from the IdP. Cannot set a user"
|
||||
" for this request without a valid user attribute.",
|
||||
d->userattr);
|
||||
}
|
||||
|
||||
/* Allocate a set of counters for duplicate variables in the list. */
|
||||
counters = apr_hash_make(r->pool);
|
||||
|
||||
/* Populate the subprocess environment with the attributes we
|
||||
* received from the IdP.
|
||||
*/
|
||||
for(i = 0; i < t->size; ++i) {
|
||||
varname = t->env[i].varname;
|
||||
|
||||
/* Check if we should map this name into another name. */
|
||||
env_varname = (const char*)apr_hash_get(
|
||||
d->envattr, varname, APR_HASH_KEY_STRING);
|
||||
if(env_varname != NULL) {
|
||||
varname = env_varname;
|
||||
}
|
||||
|
||||
value = t->env[i].value;
|
||||
|
||||
|
||||
/* Find the number of times this variable has been set. */
|
||||
count = apr_hash_get(counters, varname, APR_HASH_KEY_STRING);
|
||||
if(count == NULL) {
|
||||
|
||||
/* This is the first time. Create a counter for this variable. */
|
||||
count = apr_palloc(r->pool, sizeof(int));
|
||||
*count = 0;
|
||||
apr_hash_set(counters, varname, APR_HASH_KEY_STRING, count);
|
||||
|
||||
/* Add the variable without a suffix. */
|
||||
apr_table_set(r->subprocess_env,
|
||||
apr_pstrcat(r->pool, "MELLON_", varname, NULL),
|
||||
value);
|
||||
}
|
||||
|
||||
/* Add the variable with a suffix indicating how many times it has
|
||||
* been added before.
|
||||
*/
|
||||
apr_table_set(r->subprocess_env,
|
||||
apr_psprintf(r->pool, "MELLON_%s_%d", varname, *count),
|
||||
value);
|
||||
|
||||
/* Increase the count. */
|
||||
++(*count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This function deletes a given key from the session store.
|
||||
*
|
||||
* Parameters:
|
||||
* server_rec *s The current server.
|
||||
* am_cache_entry_t *cache The entry we are deleting.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_cache_delete(server_rec *s, am_cache_entry_t *cache)
|
||||
{
|
||||
/* We write a null-byte at the beginning of the key to
|
||||
* mark this slot as unused.
|
||||
*/
|
||||
cache->key[0] = '\0';
|
||||
|
||||
/* Unlock the entry. */
|
||||
am_cache_unlock(s, cache);
|
||||
}
|
||||
|
||||
|
||||
/* This function stores a lasso identity dump and a lasso session dump in
|
||||
* the given session object.
|
||||
*
|
||||
* Parameters:
|
||||
* am_cache_entry_t *session The session object.
|
||||
* const char *lasso_identity The identity dump.
|
||||
* const char *lasso_session The session dump.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success or HTTP_INTERNAL_SERVER_ERROR if the lasso state information
|
||||
* is to big to fit in our session.
|
||||
*/
|
||||
int am_cache_set_lasso_state(am_cache_entry_t *session,
|
||||
const char *lasso_identity,
|
||||
const char *lasso_session)
|
||||
{
|
||||
if(lasso_identity != NULL) {
|
||||
if(strlen(lasso_identity) < AM_CACHE_MAX_LASSO_IDENTITY_SIZE) {
|
||||
strcpy(session->lasso_identity, lasso_identity);
|
||||
} else {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
|
||||
"Lasso identity is to big for storage. Size of lasso"
|
||||
" identity is %u, max size is %u.", strlen(lasso_identity),
|
||||
AM_CACHE_MAX_LASSO_IDENTITY_SIZE - 1);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* No identity dump to save. */
|
||||
strcpy(session->lasso_identity, "");
|
||||
}
|
||||
|
||||
|
||||
if(lasso_session != NULL) {
|
||||
if(strlen(lasso_session) < AM_CACHE_MAX_LASSO_SESSION_SIZE) {
|
||||
strcpy(session->lasso_session, lasso_session);
|
||||
} else {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
|
||||
"Lasso session is to big for storage. Size of lasso"
|
||||
" session is %u, max size is %u.", strlen(lasso_session),
|
||||
AM_CACHE_MAX_LASSO_SESSION_SIZE - 1);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* No session dump to save. */
|
||||
strcpy(session->lasso_session, "");
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function retrieves a lasso identity dump from the session object.
|
||||
*
|
||||
* Parameters:
|
||||
* am_cache_entry_t *session The session object.
|
||||
*
|
||||
* Returns:
|
||||
* The identity dump, or NULL if we don't have a session dump.
|
||||
*/
|
||||
const char *am_cache_get_lasso_identity(am_cache_entry_t *session)
|
||||
{
|
||||
if(strlen(session->lasso_identity) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return session->lasso_identity;
|
||||
}
|
||||
|
||||
|
||||
/* This function retrieves a lasso session dump from the session object.
|
||||
*
|
||||
* Parameters:
|
||||
* am_cache_entry_t *session The session object.
|
||||
*
|
||||
* Returns:
|
||||
* The session dump, or NULL if we don't have a session dump.
|
||||
*/
|
||||
const char *am_cache_get_lasso_session(am_cache_entry_t *session)
|
||||
{
|
||||
if(strlen(session->lasso_session) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return session->lasso_session;
|
||||
}
|
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
*
|
||||
* auth_mellon_config.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
/* This is the default endpoint path. Remember to update the description of
|
||||
* the MellonEndpointPath configuration directive if you change this.
|
||||
*/
|
||||
static const char *default_endpoint_path = "/mellon/";
|
||||
|
||||
/* This is the default name of the attribute we use as a username. Remember
|
||||
* to update the description of the MellonUser configuration directive if
|
||||
* you change this.
|
||||
*/
|
||||
static const char *default_user_attribute = "NAME_ID";
|
||||
|
||||
/* This is the default name of the cookie which mod_auth_mellon will set.
|
||||
* If you change this, then you should also update the description of the
|
||||
* MellonVar configuration directive.
|
||||
*/
|
||||
static const char *default_cookie_name = "cookie";
|
||||
|
||||
|
||||
/* This function handles configuration directives which set a string
|
||||
* slot in the module configuration.
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for this configuration
|
||||
* directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* NULL if we are not in a directory configuration.
|
||||
* This value isn't used by this function.
|
||||
* const char *arg The string argument following this configuration
|
||||
* directive in the configuraion file.
|
||||
*
|
||||
* Returns:
|
||||
* NULL on success or an error string on failure.
|
||||
*/
|
||||
static const char *am_set_module_config_string_slot(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *arg)
|
||||
{
|
||||
return ap_set_string_slot(cmd, am_get_mod_cfg(cmd->server), arg);
|
||||
}
|
||||
|
||||
/* This function handles configuration directives which set an int
|
||||
* slot in the module configuration.
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for this configuration
|
||||
* directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* NULL if we are not in a directory configuration.
|
||||
* This value isn't used by this function.
|
||||
* const char *arg The string argument following this configuration
|
||||
* directive in the configuraion file.
|
||||
*
|
||||
* Returns:
|
||||
* NULL on success or an error string on failure.
|
||||
*/
|
||||
static const char *am_set_module_config_int_slot(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *arg)
|
||||
{
|
||||
return ap_set_int_slot(cmd, am_get_mod_cfg(cmd->server), arg);
|
||||
}
|
||||
|
||||
|
||||
/* This function handles the MellonEnable configuration directive.
|
||||
* This directive can be set to "off", "info" or "auth".
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for this configuration
|
||||
* directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* const char *arg The string argument following this configuration
|
||||
* directive in the configuraion file.
|
||||
*
|
||||
* Returns:
|
||||
* NULL on success or an error string if the argument is wrong.
|
||||
*/
|
||||
static const char *am_set_enable_slot(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *arg)
|
||||
{
|
||||
am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr;
|
||||
|
||||
if(!strcasecmp(arg, "auth")) {
|
||||
d->enable_mellon = am_enable_auth;
|
||||
} else if(!strcasecmp(arg, "info")) {
|
||||
d->enable_mellon = am_enable_info;
|
||||
} else if(!strcasecmp(arg, "off")) {
|
||||
d->enable_mellon = am_enable_off;
|
||||
} else {
|
||||
return "parameter must be 'off', 'info' or 'auth'";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function handles the MellonDecoder configuration directive.
|
||||
* This directive can be set to "none" or "feide".
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for this configuration
|
||||
* directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* const char *arg The string argument following this configuration
|
||||
* directive in the configuraion file.
|
||||
*
|
||||
* Returns:
|
||||
* NULL on success or an error string if the argument is wrong.
|
||||
*/
|
||||
static const char *am_set_decoder_slot(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *arg)
|
||||
{
|
||||
am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr;
|
||||
|
||||
if(!strcasecmp(arg, "none")) {
|
||||
d->decoder = am_decoder_none;
|
||||
} else if(!strcasecmp(arg, "feide")) {
|
||||
d->decoder = am_decoder_feide;
|
||||
} else {
|
||||
return "MellonDecoder must be 'none' or 'feide'";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function handles the MellonEndpointPath configuration directive.
|
||||
* If the path doesn't end with a '/', then we will append one.
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for the MellonEndpointPath
|
||||
* configuration directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* NULL if we are not in a directory configuration.
|
||||
* const char *arg The string argument containing the path of the
|
||||
* endpoint directory.
|
||||
*
|
||||
* Returns:
|
||||
* This function will always return NULL.
|
||||
*/
|
||||
static const char *am_set_endpoint_path(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *arg)
|
||||
{
|
||||
am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr;
|
||||
|
||||
/* Make sure that the path ends with '/'. */
|
||||
if(strlen(arg) == 0 || arg[strlen(arg) - 1] != '/') {
|
||||
d->endpoint_path = apr_pstrcat(cmd->pool, arg, "/", 0);
|
||||
} else {
|
||||
d->endpoint_path = arg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function handles the MellonSetEnv configuration directive.
|
||||
* This directive allows the user to change the name of attributes.
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for the MellonSetEnv
|
||||
* configuration directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* const char *newName The new name of the attribute.
|
||||
* const char *oldName The old name of the attribute.
|
||||
*
|
||||
* Returns:
|
||||
* This function will always return NULL.
|
||||
*/
|
||||
static const char *am_set_setenv_slot(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *newName,
|
||||
const char *oldName)
|
||||
{
|
||||
am_dir_cfg_rec *d = (am_dir_cfg_rec *)struct_ptr;
|
||||
apr_hash_set(d->envattr, oldName, APR_HASH_KEY_STRING, newName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function handles the MellonRequire configuration directive, which
|
||||
* allows the user to restrict access based on attributes received from
|
||||
* the IdP.
|
||||
*
|
||||
* Parameters:
|
||||
* cmd_parms *cmd The command structure for the MellonRequire
|
||||
* configuration directive.
|
||||
* void *struct_ptr Pointer to the current directory configuration.
|
||||
* const char *arg Pointer to the configuration string.
|
||||
*
|
||||
* Returns:
|
||||
* NULL on success or an error string on failure.
|
||||
*/
|
||||
static const char *am_set_require_slot(cmd_parms *cmd,
|
||||
void *struct_ptr,
|
||||
const char *arg)
|
||||
{
|
||||
apr_array_header_t *r;
|
||||
am_dir_cfg_rec *d = struct_ptr;
|
||||
char *attribute, *value;
|
||||
const char **element;
|
||||
|
||||
attribute = ap_getword_conf(cmd->pool, &arg);
|
||||
value = ap_getword_conf(cmd->pool, &arg);
|
||||
|
||||
if (*attribute == '\0' || *value == '\0') {
|
||||
return apr_pstrcat(cmd->pool, cmd->cmd->name,
|
||||
" takes at least two arguments", NULL);
|
||||
}
|
||||
|
||||
do {
|
||||
r = (apr_array_header_t *)apr_hash_get(d->require, attribute,
|
||||
APR_HASH_KEY_STRING);
|
||||
|
||||
if (r == NULL) {
|
||||
r = apr_array_make(cmd->pool, 2, sizeof(const char *));
|
||||
apr_hash_set(d->require, attribute, APR_HASH_KEY_STRING, r);
|
||||
}
|
||||
|
||||
element = (const char **)apr_array_push(r);
|
||||
*element = value;
|
||||
|
||||
} while (*(value = ap_getword_conf(cmd->pool, &arg)) != '\0');
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This array contains all the configuration directive which are handled
|
||||
* by auth_mellon.
|
||||
*/
|
||||
const command_rec auth_mellon_commands[] = {
|
||||
|
||||
/* Global configuration directives. */
|
||||
|
||||
AP_INIT_TAKE1(
|
||||
"MellonCacheSize",
|
||||
am_set_module_config_int_slot,
|
||||
(void *)APR_OFFSETOF(am_mod_cfg_rec, cache_size),
|
||||
RSRC_CONF,
|
||||
"The number of sessions we can keep track of at once. You must"
|
||||
" restart the server before any changes to this directive will"
|
||||
" take effect. The default value is 100."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonLockFile",
|
||||
am_set_module_config_string_slot,
|
||||
(void *)APR_OFFSETOF(am_mod_cfg_rec, lock_file),
|
||||
RSRC_CONF,
|
||||
"The lock file for session synchronization."
|
||||
" Default value is \"/tmp/mellonLock\"."
|
||||
),
|
||||
|
||||
|
||||
/* Per-location configuration directives. */
|
||||
|
||||
AP_INIT_TAKE1(
|
||||
"MellonEnable",
|
||||
am_set_enable_slot,
|
||||
NULL,
|
||||
OR_AUTHCFG,
|
||||
"Enable auth_mellon on a location. This can be set to 'off', 'info'"
|
||||
" and 'auth'. 'off' disables auth_mellon for a location, 'info'"
|
||||
" will only populate the environment with attributes if the user"
|
||||
" has logged in already. 'auth' will redirect the user to the IdP"
|
||||
" if he hasn't logged in yet, but otherwise behaves like 'info'."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonDecoder",
|
||||
am_set_decoder_slot,
|
||||
NULL,
|
||||
OR_AUTHCFG,
|
||||
"Select which decoder mod_auth_mellon should use to decode attribute"
|
||||
" values. This option can be se to either 'none' or 'feide'. 'none'"
|
||||
" is the default, and will store the attributes as they are received"
|
||||
" from the IdP. 'feide' is for decoding base64-encoded values which"
|
||||
" are separated by a underscore."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonVariable",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, varname),
|
||||
OR_AUTHCFG,
|
||||
"The name of the cookie which auth_mellon will set. Defaults to"
|
||||
" 'cookie'. This string is appended to 'mellon-' to create the"
|
||||
" cookie name, and the default name of the cookie will therefore"
|
||||
" be 'mellon-cookie'."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonUser",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, userattr),
|
||||
OR_AUTHCFG,
|
||||
"Attribute to set as r->user. Defaults to NAME_ID, which is the"
|
||||
" attribute we set to the identifier we receive from the IdP."
|
||||
),
|
||||
AP_INIT_TAKE2(
|
||||
"MellonSetEnv",
|
||||
am_set_setenv_slot,
|
||||
NULL,
|
||||
OR_AUTHCFG,
|
||||
"Renames attributes received from the server. The format is"
|
||||
" MellonSetEnv <old name> <new name>."
|
||||
),
|
||||
AP_INIT_RAW_ARGS(
|
||||
"MellonRequire",
|
||||
am_set_require_slot,
|
||||
NULL,
|
||||
OR_AUTHCFG,
|
||||
"Attribute requirements for authorization. Allows you to restrict"
|
||||
" access based on attributes received from the IdP. If you list"
|
||||
" several MellonRequire configuration directives, then all of them"
|
||||
" must match. Every MellonRequire can list several allowed values"
|
||||
" for the attribute. The syntax is:"
|
||||
" MellonRequire <attribute> <value1> [value2....]."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonSessionLength",
|
||||
ap_set_int_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, session_length),
|
||||
OR_AUTHCFG,
|
||||
"Maximum number of seconds a session will be valid for. Defaults"
|
||||
" to 86400 seconds (1 day)."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonNoCookieErrorPage",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, no_cookie_error_page),
|
||||
OR_AUTHCFG,
|
||||
"Web page to display if the user has disabled cookies. We will"
|
||||
" return a 400 Bad Request error if this is unset and the user"
|
||||
" ha disabled cookies."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonSPMetadataFile",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, sp_metadata_file),
|
||||
OR_AUTHCFG,
|
||||
"Full path to xml file with metadata for the SP."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonSPPrivateKeyFile",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, sp_private_key_file),
|
||||
OR_AUTHCFG,
|
||||
"Full path to pem file with the private key for the SP."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonIdPMetadataFile",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, idp_metadata_file),
|
||||
OR_AUTHCFG,
|
||||
"Full path to xml metadata file for the IdP."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonIdPPublicKeyFile",
|
||||
ap_set_string_slot,
|
||||
(void *)APR_OFFSETOF(am_dir_cfg_rec, idp_public_key_file),
|
||||
OR_AUTHCFG,
|
||||
"Full path to pem file with the public key for the IdP."
|
||||
),
|
||||
AP_INIT_TAKE1(
|
||||
"MellonEndpointPath",
|
||||
am_set_endpoint_path,
|
||||
NULL,
|
||||
OR_AUTHCFG,
|
||||
"The root directory of the SAML2 endpoints, relative to the root"
|
||||
" of the web server. Default value is \"/mellon/\", which will"
|
||||
" make mod_mellon to the handler for every request to"
|
||||
" \"http://<servername>/mellon/*\". The path you specify must"
|
||||
" be contained within the current Location directive."
|
||||
),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
/* This function creates and initializes a directory configuration
|
||||
* object for auth_mellon.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *p The pool we should allocate memory from.
|
||||
* char *d Unused, always NULL.
|
||||
*
|
||||
* Returns:
|
||||
* The new directory configuration object.
|
||||
*/
|
||||
void *auth_mellon_dir_config(apr_pool_t *p, char *d)
|
||||
{
|
||||
am_dir_cfg_rec *dir = apr_palloc(p, sizeof(*dir));
|
||||
|
||||
dir->enable_mellon = am_enable_default;
|
||||
|
||||
dir->decoder = am_decoder_default;
|
||||
|
||||
dir->varname = default_cookie_name;
|
||||
dir->require = apr_hash_make(p);
|
||||
dir->envattr = apr_hash_make(p);
|
||||
dir->userattr = default_user_attribute;
|
||||
|
||||
dir->endpoint_path = default_endpoint_path;
|
||||
|
||||
dir->session_length = -1; /* -1 means use default. */
|
||||
|
||||
dir->no_cookie_error_page = NULL;
|
||||
|
||||
dir->sp_metadata_file = NULL;
|
||||
dir->sp_private_key_file = NULL;
|
||||
dir->idp_metadata_file = NULL;
|
||||
dir->idp_public_key_file = NULL;
|
||||
|
||||
|
||||
apr_thread_mutex_create(&dir->server_mutex, APR_THREAD_MUTEX_DEFAULT, p);
|
||||
|
||||
dir->server = NULL;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
/* This function merges two am_dir_cfg_rec structures.
|
||||
* It will try to inherit from the base where possible.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *p The pool we should allocate memory from.
|
||||
* void *base The original structure.
|
||||
* void *add The structure we should add to base.
|
||||
*
|
||||
* Returns:
|
||||
* The merged structure.
|
||||
*/
|
||||
void *auth_mellon_dir_merge(apr_pool_t *p, void *base, void *add)
|
||||
{
|
||||
am_dir_cfg_rec *base_cfg = (am_dir_cfg_rec *)base;
|
||||
am_dir_cfg_rec *add_cfg = (am_dir_cfg_rec *)add;
|
||||
am_dir_cfg_rec *new_cfg;
|
||||
|
||||
new_cfg = (am_dir_cfg_rec *)apr_palloc(p, sizeof(*new_cfg));
|
||||
|
||||
|
||||
new_cfg->enable_mellon = (add_cfg->enable_mellon != am_enable_default ?
|
||||
add_cfg->enable_mellon :
|
||||
base_cfg->enable_mellon);
|
||||
|
||||
|
||||
new_cfg->decoder = (add_cfg->decoder != am_decoder_default ?
|
||||
add_cfg->decoder :
|
||||
base_cfg->decoder);
|
||||
|
||||
|
||||
new_cfg->varname = (add_cfg->varname != default_cookie_name ?
|
||||
add_cfg->varname :
|
||||
base_cfg->varname);
|
||||
|
||||
new_cfg->require = apr_hash_copy(p,
|
||||
(apr_hash_count(add_cfg->require) > 0) ?
|
||||
add_cfg->require :
|
||||
base_cfg->require);
|
||||
|
||||
new_cfg->envattr = apr_hash_copy(p,
|
||||
(apr_hash_count(add_cfg->envattr) > 0) ?
|
||||
add_cfg->envattr :
|
||||
base_cfg->envattr);
|
||||
|
||||
new_cfg->userattr = (add_cfg->userattr != default_user_attribute ?
|
||||
add_cfg->userattr :
|
||||
base_cfg->userattr);
|
||||
|
||||
|
||||
new_cfg->endpoint_path = (
|
||||
add_cfg->endpoint_path != default_endpoint_path ?
|
||||
add_cfg->endpoint_path :
|
||||
base_cfg->endpoint_path
|
||||
);
|
||||
|
||||
new_cfg->session_length = (add_cfg->session_length != -1 ?
|
||||
add_cfg->session_length :
|
||||
base_cfg->session_length);
|
||||
|
||||
new_cfg->no_cookie_error_page = (add_cfg->no_cookie_error_page != NULL ?
|
||||
add_cfg->no_cookie_error_page :
|
||||
base_cfg->no_cookie_error_page);
|
||||
|
||||
|
||||
new_cfg->sp_metadata_file = (add_cfg->sp_metadata_file ?
|
||||
add_cfg->sp_metadata_file :
|
||||
base_cfg->sp_metadata_file);
|
||||
|
||||
new_cfg->sp_private_key_file = (add_cfg->sp_private_key_file ?
|
||||
add_cfg->sp_private_key_file :
|
||||
base_cfg->sp_private_key_file);
|
||||
|
||||
new_cfg->idp_metadata_file = (add_cfg->idp_metadata_file ?
|
||||
add_cfg->idp_metadata_file :
|
||||
base_cfg->idp_metadata_file);
|
||||
|
||||
new_cfg->idp_public_key_file = (add_cfg->idp_public_key_file ?
|
||||
add_cfg->idp_public_key_file :
|
||||
base_cfg->idp_public_key_file);
|
||||
|
||||
|
||||
apr_thread_mutex_create(&new_cfg->server_mutex,
|
||||
APR_THREAD_MUTEX_DEFAULT, p);
|
||||
new_cfg->server = NULL;
|
||||
|
||||
return new_cfg;
|
||||
}
|
||||
|
||||
|
||||
/* This function creates a new per-server configuration.
|
||||
* auth_mellon uses the server configuration to store a pointer
|
||||
* to the global module configuration.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *p The pool we should allocate memory from.
|
||||
* server_rec *s The server we should add our configuration to.
|
||||
*
|
||||
* Returns:
|
||||
* The new per-server configuration.
|
||||
*/
|
||||
void *auth_mellon_server_config(apr_pool_t *p, server_rec *s)
|
||||
{
|
||||
am_srv_cfg_rec *srv;
|
||||
am_mod_cfg_rec *mod;
|
||||
const char key[] = "auth_mellon_server_config";
|
||||
|
||||
srv = apr_palloc(p, sizeof(*srv));
|
||||
|
||||
/* we want to keeep our global configuration of shared memory and
|
||||
* mutexes, so we try to find it in the userdata before doing anything
|
||||
* else */
|
||||
apr_pool_userdata_get((void **)&mod, key, p);
|
||||
if (mod) {
|
||||
srv->mc = mod;
|
||||
return srv;
|
||||
}
|
||||
|
||||
/* the module has not been initiated at all */
|
||||
mod = apr_palloc(p, sizeof(*mod));
|
||||
|
||||
mod->cache_size = 100; /* ought to be enough for everybody */
|
||||
mod->lock_file = "/tmp/mellonLock";
|
||||
|
||||
mod->init_cache_size = 0;
|
||||
mod->init_lock_file = NULL;
|
||||
|
||||
mod->cache = NULL;
|
||||
mod->lock = NULL;
|
||||
|
||||
apr_pool_userdata_set(mod, key, apr_pool_cleanup_null, p);
|
||||
|
||||
srv->mc = mod;
|
||||
return srv;
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
*
|
||||
* auth_mellon_cookie.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
|
||||
/* This function retrieves the name of our cookie.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request. Used to find the identifier of
|
||||
* the cookie. We also allocate memory from r->pool.
|
||||
*
|
||||
* Returns:
|
||||
* The name of the cookie.
|
||||
*/
|
||||
static const char *am_cookie_name(request_rec *r)
|
||||
{
|
||||
am_dir_cfg_rec *dir_cfg;
|
||||
|
||||
dir_cfg = am_get_dir_cfg(r);
|
||||
|
||||
return apr_pstrcat(r->pool, "mellon-", dir_cfg->varname, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* This functions finds the value of our cookie.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we should find the cookie in.
|
||||
*
|
||||
* Returns:
|
||||
* The value of the cookie, or NULL if we don't find the cookie.
|
||||
*/
|
||||
const char *am_cookie_get(request_rec *r)
|
||||
{
|
||||
const char *name;
|
||||
const char *value;
|
||||
const char *cookie;
|
||||
char *buffer, *end;
|
||||
|
||||
/* don't run for subrequests */
|
||||
if (r->main) {
|
||||
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
||||
"cookie_get: Subrequest, so return NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = am_cookie_name(r);
|
||||
|
||||
cookie = apr_table_get(r->headers_in, "Cookie");
|
||||
if(cookie == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(value = ap_strstr_c(cookie, name); value != NULL;
|
||||
value = ap_strstr_c(value + 1, name)) {
|
||||
|
||||
if(value != cookie) {
|
||||
/* value isn't pointing to the start of the string. */
|
||||
switch(value[-1]) {
|
||||
/* We allow the name in the cookie-string to be
|
||||
* preceeded by [\t; ]. Note that only ' ' should be used
|
||||
* by browsers. We test against the others just to be sure.
|
||||
*/
|
||||
case '\t':
|
||||
case ';':
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
/* value isn't preceeded by one of the listed characters, and
|
||||
* therefore we assume that it is part of another cookie.
|
||||
*/
|
||||
continue; /* Search for the next instance of the name. */
|
||||
}
|
||||
}
|
||||
|
||||
if(value[strlen(name)] != '=') {
|
||||
/* We don't have an equal-sign right after the name. Therefore we
|
||||
* assume that what we have matched is only part of a longer name.
|
||||
* We continue searching.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Now we have something that matches /[^ ,\t]<name>=/. The value
|
||||
* (following the equal-sign) can be found at value + strlen(name) + 1.
|
||||
*/
|
||||
value += strlen(name) + 1;
|
||||
|
||||
buffer = apr_pstrdup(r->pool, value);
|
||||
end = strchr(buffer, ';');
|
||||
if(end) {
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* We didn't find the cookie. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function sets the value of our cookie.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we should set the cookie in.
|
||||
* const char *id The value ve should store in the cookie.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_cookie_set(request_rec *r, const char *id)
|
||||
{
|
||||
const char *name;
|
||||
char *cookie;
|
||||
|
||||
if (id == NULL)
|
||||
return;
|
||||
|
||||
name = am_cookie_name(r);
|
||||
|
||||
cookie = apr_psprintf(r->pool, "%s=%s; Version=1; Path=/", name, id);
|
||||
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
||||
"cookie_set: %s", cookie);
|
||||
|
||||
/* For now we're setting the cookie in both header tables since
|
||||
* it is unclear which the user will be sent. After a minor release
|
||||
* this suddenly changed from headers_out to err_headers_out, so to
|
||||
* be on the safe side...
|
||||
*/
|
||||
apr_table_addn(r->headers_out, "Set-Cookie", cookie);
|
||||
apr_table_addn(r->err_headers_out, "Set-Cookie", cookie);
|
||||
}
|
||||
|
||||
|
||||
/* This function deletes the cookie.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we should clear the cookie in. We will
|
||||
* allocate any neccesary memory from r->pool.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_cookie_delete(request_rec *r)
|
||||
{
|
||||
const char *name;
|
||||
char *cookie;
|
||||
|
||||
name = am_cookie_name(r);
|
||||
|
||||
|
||||
/* Format a cookie. To delete a cookie we set the expires-timestamp
|
||||
* to the past.
|
||||
*/
|
||||
cookie = apr_psprintf(r->pool, "%s=NULL;"
|
||||
" version=1;"
|
||||
" expires=Thu, 01-Jan-1970 00:00:00 GMT;"
|
||||
" path=/",
|
||||
name);
|
||||
|
||||
apr_table_addn(r->headers_out, "Set-Cookie", cookie);
|
||||
apr_table_addn(r->err_headers_out, "Set-Cookie", cookie);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
*
|
||||
* mod_auth_mellon.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
||||
/* The size of the blocks we will allocate. */
|
||||
#define AM_HC_BLOCK_SIZE 1000
|
||||
|
||||
|
||||
/* This structure describes a single-linked list of downloaded blocks. */
|
||||
typedef struct am_hc_block_s {
|
||||
/* The next block we have allocated. */
|
||||
struct am_hc_block_s *next;
|
||||
|
||||
/* The number of bytes written to this block. */
|
||||
apr_size_t used;
|
||||
|
||||
/* The data stored in this block. */
|
||||
uint8_t data[AM_HC_BLOCK_SIZE];
|
||||
} am_hc_block_t;
|
||||
|
||||
|
||||
/* This structure describes a header for the block list. */
|
||||
typedef struct {
|
||||
/* The pool we will allocate memory for new blocks from. */
|
||||
apr_pool_t *pool;
|
||||
|
||||
/* The first block in the linked list of blocks. */
|
||||
am_hc_block_t *first;
|
||||
|
||||
/* The last block in the linked list of blocks. */
|
||||
am_hc_block_t *last;
|
||||
} am_hc_block_header_t;
|
||||
|
||||
|
||||
/* This function allocates and initializes a block for data copying.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *pool The pool we should allocate the block from.
|
||||
*
|
||||
* Returns:
|
||||
* The new block we allocated.
|
||||
*/
|
||||
static am_hc_block_t *am_hc_block_alloc(apr_pool_t *pool)
|
||||
{
|
||||
am_hc_block_t *blk;
|
||||
|
||||
blk = (am_hc_block_t *)apr_palloc(pool, sizeof(am_hc_block_t));
|
||||
|
||||
blk->next = NULL;
|
||||
blk->used = 0;
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
/* This function adds data to the end of a block, and allocates new blocks
|
||||
* if the data doesn't fit in one block.
|
||||
*
|
||||
* Parameters:
|
||||
* am_hc_block_t *block The block we should begin by appending data to.
|
||||
* apr_pool_t *pool The pool we should allocate memory for new blocks
|
||||
* from.
|
||||
* const uint8_t *data The data we should append to the blocks.
|
||||
* apr_size_t size The length of the data we should append.
|
||||
*
|
||||
* Returns:
|
||||
* The last block written to (i.e. the next block we should write to).
|
||||
*/
|
||||
static am_hc_block_t *am_hc_block_write(
|
||||
am_hc_block_t *block,
|
||||
apr_pool_t *pool,
|
||||
const uint8_t *data, apr_size_t size
|
||||
)
|
||||
{
|
||||
apr_size_t num_cpy;
|
||||
|
||||
/* Find the number of bytes we should write to this block. */
|
||||
num_cpy = AM_HC_BLOCK_SIZE - block->used;
|
||||
if(num_cpy > size) {
|
||||
num_cpy = size;
|
||||
}
|
||||
|
||||
/* Copy data to this block. */
|
||||
memcpy(&block->data[block->used], data, num_cpy);
|
||||
block->used += num_cpy;
|
||||
|
||||
if(block->used == AM_HC_BLOCK_SIZE) {
|
||||
/* This block is full. Allocate a new block, and continue
|
||||
* filling it.
|
||||
*/
|
||||
block->next = am_hc_block_alloc(pool);
|
||||
|
||||
return am_hc_block_write(block->next, pool, &data[num_cpy],
|
||||
size - num_cpy);
|
||||
}
|
||||
|
||||
/* The next write should be to this block. */
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
/* This function initializes a am_hc_block_header_t structure, which
|
||||
* contains information about the linked list of data blocks.
|
||||
*
|
||||
* Parameters:
|
||||
* am_hc_block_header_t *bh Pointer to the data header whcih we
|
||||
* should initialize.
|
||||
* apr_pool_t *pool The pool we should allocate data from.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
static void am_hc_block_header_init(am_hc_block_header_t *bh,
|
||||
apr_pool_t *pool)
|
||||
{
|
||||
bh->pool = pool;
|
||||
|
||||
bh->first = am_hc_block_alloc(pool);
|
||||
bh->last = bh->first;
|
||||
}
|
||||
|
||||
|
||||
/* This function writes data to the linked list of blocks identified by
|
||||
* the stream-parameter. It matches the prototype required by curl.
|
||||
*
|
||||
* Parameters:
|
||||
* void *data The data that should be written. It is size*nmemb
|
||||
* bytes long.
|
||||
* size_t size The size of each block of data that should
|
||||
* be written.
|
||||
* size_t nmemb The number of blocks of data that should be written.
|
||||
* void *block_header A pointer to a am_hc_block_header_t structure which
|
||||
* identifies the linked list we should store data in.
|
||||
*
|
||||
* Returns:
|
||||
* The number of bytes that have been written.
|
||||
*/
|
||||
static size_t am_hc_data_write(void *data, size_t size, size_t nmemb,
|
||||
void *data_header)
|
||||
{
|
||||
am_hc_block_header_t *bh;
|
||||
|
||||
bh = (am_hc_block_header_t *)data_header;
|
||||
|
||||
bh->last = am_hc_block_write(bh->last, bh->pool, (const uint8_t *)data,
|
||||
size * nmemb);
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
||||
/* This function fetches the data which was written to the databuffers
|
||||
* in the linked list which the am_hc_data_t structure keeps track of.
|
||||
*
|
||||
* Parameters:
|
||||
* am_hc_block_header_t *bh The header telling us which data buffers
|
||||
* we should extract data from.
|
||||
* apr_pool_t *pool The pool we should allocate the data
|
||||
* buffer from.
|
||||
* void **buffer A pointer to where we should store a pointer
|
||||
* to the data buffer we allocate. We will
|
||||
* always add a null-terminator to the end of
|
||||
* data buffer. This parameter can't be NULL.
|
||||
* apr_size_t *size This is a pointer to where we will store the
|
||||
* length of the data, not including the
|
||||
* null-terminator we add. This parameter can
|
||||
* be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
static void am_hc_data_extract(am_hc_block_header_t *bh, apr_pool_t *pool,
|
||||
void **buffer, apr_size_t *size)
|
||||
{
|
||||
am_hc_block_t *blk;
|
||||
apr_size_t length;
|
||||
uint8_t *buf;
|
||||
apr_size_t pos;
|
||||
|
||||
/* First we find the length of the data. */
|
||||
length = 0;
|
||||
for(blk = bh->first; blk != NULL; blk = blk->next) {
|
||||
length += blk->used;
|
||||
}
|
||||
|
||||
/* Allocate memory for the data. Add one to the size in order to
|
||||
* have space for the null-terminator.
|
||||
*/
|
||||
buf = (uint8_t *)apr_palloc(pool, length + 1);
|
||||
|
||||
/* Copy the data into the buffer. */
|
||||
pos = 0;
|
||||
for(blk = bh->first; blk != NULL; blk = blk->next) {
|
||||
memcpy(&buf[pos], blk->data, blk->used);
|
||||
pos += blk->used;
|
||||
}
|
||||
|
||||
/* Add the null-terminator. */
|
||||
buf[length] = 0;
|
||||
|
||||
/* Set up the return values. */
|
||||
*buffer = (void *)buf;
|
||||
if(size != NULL) {
|
||||
*size = length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This function creates a curl object and performs generic initialization
|
||||
* of it.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we should log errors against.
|
||||
* const char *uri The URI we should request.
|
||||
* am_hc_block_header_t *bh The buffer curl will write response data to.
|
||||
* char *curl_error A buffer of size CURL_ERROR_SIZE where curl
|
||||
* will store error messages.
|
||||
*
|
||||
* Returns:
|
||||
* A initialized curl object on succcess, or NULL on error.
|
||||
*/
|
||||
static CURL *am_httpclient_init_curl(request_rec *r, const char *uri,
|
||||
am_hc_block_header_t *bh,
|
||||
char *curl_error)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
|
||||
/* Initialize the curl object. */
|
||||
curl = curl_easy_init();
|
||||
if(curl == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to initialize a curl object.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Set up error reporting. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set curl error buffer: [%u]\n", res);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Disable progress reporting. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to disable curl progress reporting: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Disable use of signals. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to disable signals in curl: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Set the timeout of the transfer. It is currently set to two minutes. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set the timeout of the curl download:"
|
||||
" [%u] %s", res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Enable SSL peer certificate verification. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to enable SSL peer certificate verification:"
|
||||
" [%u] %s", res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Enable SSL peer hostname verification. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to enable SSL peer hostname verification:"
|
||||
" [%u] %s", res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Enable fail on http error. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to enable failure on http error: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Select which uri we should download. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_URL, uri);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set curl download uri to \"%s\": [%u] %s",
|
||||
uri, res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
|
||||
/* Set up data writing. */
|
||||
|
||||
/* Set curl write function. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, am_hc_data_write);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set the curl write function: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Set the curl write function parameter. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, bh);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set the curl write function data: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
return curl;
|
||||
|
||||
|
||||
cleanup_fail:
|
||||
curl_easy_cleanup(curl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function downloads data from a specified URI.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The apache request this download is associated
|
||||
* with. It is used for memory allocation and logging.
|
||||
* const char *uri The URI we should download.
|
||||
* void **buffer A pointer to where we should store a pointer to the
|
||||
* downloaded data. We will always add a null-terminator
|
||||
* to the data. This parameter can't be NULL.
|
||||
* apr_size_t *size This is a pointer to where we will store the length
|
||||
* of the downloaded data, not including the
|
||||
* null-terminator we add. This parameter can be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, or HTTP_INTERNAL_SERVER_ERROR on failure. On failure we
|
||||
* will write a log message describing the error.
|
||||
*/
|
||||
int am_httpclient_get(request_rec *r, const char *uri,
|
||||
void **buffer, apr_size_t *size)
|
||||
{
|
||||
am_hc_block_header_t bh;
|
||||
CURL *curl;
|
||||
char curl_error[CURL_ERROR_SIZE];
|
||||
CURLcode res;
|
||||
|
||||
/* Initialize the data storage. */
|
||||
am_hc_block_header_init(&bh, r->pool);
|
||||
|
||||
/* Initialize the curl object. */
|
||||
curl = am_httpclient_init_curl(r, uri, &bh, curl_error);
|
||||
if(curl == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/* Do the download. */
|
||||
res = curl_easy_perform(curl);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to download data from the uri \"%s\": [%u] %s",
|
||||
uri, res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Free the curl object. */
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
/* Copy the data. */
|
||||
am_hc_data_extract(&bh, r->pool, buffer, size);
|
||||
|
||||
return OK;
|
||||
|
||||
|
||||
cleanup_fail:
|
||||
curl_easy_cleanup(curl);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* This function downloads data from a specified URI by issuing a POST
|
||||
* request.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The apache request this download is
|
||||
* associated with. It is used for memory
|
||||
* allocation and logging.
|
||||
* const char *uri The URI we should post data to.
|
||||
* const void *post_data The POST data we should send.
|
||||
* apr_size_t post_length The length of the POST data.
|
||||
* const char *content_type The content type of the POST data. This
|
||||
* parameter can be NULL, in which case the
|
||||
* content type will be
|
||||
* "application/x-www-form-urlencoded".
|
||||
* void **buffer A pointer to where we should store a pointer
|
||||
* to the downloaded data. We will always add a
|
||||
* null-terminator to the data. This parameter
|
||||
* can't be NULL.
|
||||
* apr_size_t *size This is a pointer to where we will store the
|
||||
* length of the downloaded data, not including
|
||||
* the null-terminator we add. This parameter
|
||||
* can be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success. On failure we will write a log message describing the
|
||||
* error, and return HTTP_INTERNAL_SERVER_ERROR.
|
||||
*/
|
||||
int am_httpclient_post(request_rec *r, const char *uri,
|
||||
const void *post_data, apr_size_t post_length,
|
||||
const char *content_type,
|
||||
void **buffer, apr_size_t *size)
|
||||
{
|
||||
am_hc_block_header_t bh;
|
||||
CURL *curl;
|
||||
char curl_error[CURL_ERROR_SIZE];
|
||||
CURLcode res;
|
||||
struct curl_slist *ctheader;
|
||||
|
||||
/* Initialize the data storage. */
|
||||
am_hc_block_header_init(&bh, r->pool);
|
||||
|
||||
/* Initialize the curl object. */
|
||||
curl = am_httpclient_init_curl(r, uri, &bh, curl_error);
|
||||
if(curl == NULL) {
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
/* Enable POST request. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to enable POST request: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Set POST data size. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_length);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set the POST data length: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Set POST data. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set the POST data: [%u] %s",
|
||||
res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
|
||||
/* Set the content-type header. */
|
||||
|
||||
/* Set default content type if content_type is NULL. */
|
||||
if(content_type == NULL) {
|
||||
content_type = "application/x-www-form-urlencoded";
|
||||
}
|
||||
|
||||
/* Create header list. */
|
||||
ctheader = NULL;
|
||||
ctheader = curl_slist_append(ctheader, apr_pstrcat(
|
||||
r->pool,
|
||||
"Content-Type: ",
|
||||
content_type,
|
||||
NULL
|
||||
));
|
||||
|
||||
/* Set headers. */
|
||||
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, ctheader);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to set content-type header to \"%s\": [%u] %s",
|
||||
content_type, res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
|
||||
/* Do the download. */
|
||||
res = curl_easy_perform(curl);
|
||||
if(res != CURLE_OK) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to download data from the uri \"%s\": [%u] %s",
|
||||
uri, res, curl_error);
|
||||
goto cleanup_fail;
|
||||
}
|
||||
|
||||
/* Free the curl object. */
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
/* Free the content-type header. */
|
||||
curl_slist_free_all(ctheader);
|
||||
|
||||
/* Copy the data. */
|
||||
am_hc_data_extract(&bh, r->pool, buffer, size);
|
||||
|
||||
return OK;
|
||||
|
||||
|
||||
cleanup_fail:
|
||||
curl_easy_cleanup(curl);
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* This function downloads data from a specified URI by issuing a POST
|
||||
* request.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The apache request this download is
|
||||
* associated with. It is used for memory
|
||||
* allocation and logging.
|
||||
* const char *uri The URI we should post data to.
|
||||
* const char *post_data The POST data we should send.
|
||||
* const char *content_type The content type of the POST data. This
|
||||
* parameter can be NULL, in which case the
|
||||
* content type will be
|
||||
* "application/x-www-form-urlencoded".
|
||||
* void **buffer A pointer to where we should store a pointer
|
||||
* to the downloaded data. We will always add a
|
||||
* null-terminator to the data. This parameter
|
||||
* can't be NULL.
|
||||
* apr_size_t *size This is a pointer to where we will store the
|
||||
* length of the downloaded data, not including
|
||||
* the null-terminator we add. This parameter
|
||||
* can be NULL.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success. On failure we will write a log message describing the
|
||||
* error, and return HTTP_INTERNAL_SERVER_ERROR.
|
||||
*/
|
||||
int am_httpclient_post_str(request_rec *r, const char *uri,
|
||||
const char *post_data,
|
||||
const char *content_type,
|
||||
void **buffer, apr_size_t *size)
|
||||
{
|
||||
return am_httpclient_post(r, uri, post_data, strlen(post_data),
|
||||
content_type, buffer, size);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
*
|
||||
* auth_mellon_session.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
|
||||
/* This function gets the session associated with a user.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we received from the user.
|
||||
*
|
||||
* Returns:
|
||||
* The session associated with the user who places the request, or
|
||||
* NULL if we don't have a session yes.
|
||||
*/
|
||||
am_cache_entry_t *am_get_request_session(request_rec *r)
|
||||
{
|
||||
const char *session_id;
|
||||
|
||||
/* Get session id from cookie. */
|
||||
session_id = am_cookie_get(r);
|
||||
if(session_id == NULL) {
|
||||
/* Cookie is unset - we don't have a session. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return am_cache_lock(r->server, session_id);
|
||||
}
|
||||
|
||||
|
||||
/* This function creates a new session.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we are processing.
|
||||
*
|
||||
* Returns:
|
||||
* The new session, or NULL if we have an internal error.
|
||||
*/
|
||||
am_cache_entry_t *am_new_request_session(request_rec *r)
|
||||
{
|
||||
const char *session_id;
|
||||
|
||||
/* Generate session id. */
|
||||
session_id = am_generate_session_id(r);
|
||||
if(session_id == NULL) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Error creating session id.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Set session id. */
|
||||
am_cookie_set(r, session_id);
|
||||
|
||||
return am_cache_new(r->server, session_id);
|
||||
}
|
||||
|
||||
|
||||
/* This function releases the session which was returned from
|
||||
* am_get_request_session.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we are processing.
|
||||
* am_cache_entry_t *session The session we are releasing.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_release_request_session(request_rec *r, am_cache_entry_t *session)
|
||||
{
|
||||
am_cache_unlock(r->server, session);
|
||||
}
|
||||
|
||||
|
||||
/* This function releases and deletes the session which was returned from
|
||||
* am_get_request_session.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we are processing.
|
||||
* am_cache_entry_t *session The session we are deleting.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_delete_request_session(request_rec *r, am_cache_entry_t *session)
|
||||
{
|
||||
/* Delete the cookie. */
|
||||
am_cookie_delete(r);
|
||||
|
||||
if(session == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Delete session from the session store. */
|
||||
am_cache_delete(r->server, session);
|
||||
}
|
|
@ -0,0 +1,518 @@
|
|||
/*
|
||||
*
|
||||
* auth_mellon_util.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
/* This function is used to get the url of the current request.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request.
|
||||
*
|
||||
* Returns:
|
||||
* A string containing the full url of the current request.
|
||||
* The string is allocated from r->pool.
|
||||
*/
|
||||
const char *am_reconstruct_url(request_rec *r)
|
||||
{
|
||||
const char *url;
|
||||
|
||||
/* This function will construct an full url for a given path relative to
|
||||
* the root of the web site. To configure what hostname and port this
|
||||
* function will use, see the UseCanonicalName configuration directive.
|
||||
*/
|
||||
url = ap_construct_url(r->pool, r->unparsed_uri, r);
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
||||
"reconstruct_url: url==\"%s\", unparsed_uri==\"%s\"", url,
|
||||
r->unparsed_uri);
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
/* This function checks if the user has access according
|
||||
* to the MellonRequire directives.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The current request.
|
||||
* am_cache_entry_t *session The current session.
|
||||
*
|
||||
* Returns:
|
||||
* OK if the user has access and HTTP_FORBIDDEN if he doesn't.
|
||||
*/
|
||||
int am_check_permissions(request_rec *r, am_cache_entry_t *session)
|
||||
{
|
||||
am_dir_cfg_rec *dir_cfg;
|
||||
apr_hash_index_t *idx;
|
||||
const char *key;
|
||||
apr_array_header_t *rlist;
|
||||
int i, j;
|
||||
int rlist_ok;
|
||||
const char **re;
|
||||
|
||||
dir_cfg = am_get_dir_cfg(r);
|
||||
|
||||
/* Iterate over all require-directives. */
|
||||
for(idx = apr_hash_first(r->pool, dir_cfg->require);
|
||||
idx != NULL;
|
||||
idx = apr_hash_next(idx)) {
|
||||
|
||||
/* Get current require directive. key will be the name
|
||||
* of the attribute, and rlist is a list of all allowed values.
|
||||
*/
|
||||
apr_hash_this(idx, (const void **)&key, NULL, (void **)&rlist);
|
||||
|
||||
/* Reset status to 0 before search. */
|
||||
rlist_ok = 0;
|
||||
|
||||
re = (const char **)rlist->elts;
|
||||
|
||||
/* rlist is an array of all the valid values for this attribute. */
|
||||
for(i = 0; i < rlist->nelts && !rlist_ok; i++) {
|
||||
|
||||
/* Search for a matching attribute in the session data. */
|
||||
for(j = 0; j < session->size && !rlist_ok; j++) {
|
||||
if(strcmp(session->env[j].varname, key) == 0 &&
|
||||
strcmp(session->env[j].value, re[i]) == 0) {
|
||||
/* We found a attribute with the correct name
|
||||
* and value.
|
||||
*/
|
||||
rlist_ok = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!rlist_ok) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
|
||||
"Client failed to match required attribute \"%s\".",
|
||||
key);
|
||||
return HTTP_FORBIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function disables caching of the response to this request. It does
|
||||
* this by setting the Pragme: no-cache and Cache-Control: no-cache headers.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we are handling.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
void am_set_nocache(request_rec *r)
|
||||
{
|
||||
/* We set headers in both r->headers_out and r->err_headers_out, so that
|
||||
* we can be sure that they will be included.
|
||||
*/
|
||||
|
||||
apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
|
||||
apr_table_setn(r->err_headers_out, "Cache-Control", "no-cache");
|
||||
|
||||
apr_table_setn(r->headers_out, "Pragma", "no-cache");
|
||||
apr_table_setn(r->err_headers_out, "Pragma", "no-cache");
|
||||
}
|
||||
|
||||
|
||||
/* This function reads the post data for a request.
|
||||
*
|
||||
* The data is stored in a buffer allocated from the request pool.
|
||||
* After successful operation *data contains a pointer to the data and
|
||||
* *length contains the length of the data.
|
||||
* The data will always be null-terminated.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we read the form data from.
|
||||
* char **data Pointer to where we will store the pointer
|
||||
* to the data we read.
|
||||
* apr_size_t *length Pointer to where we will store the length
|
||||
* of the data we read. Pass NULL if you don't
|
||||
* need to know the length of the data.
|
||||
*
|
||||
* Returns:
|
||||
* OK if we successfully read the POST data.
|
||||
* An error if we fail to read the data.
|
||||
*/
|
||||
int am_read_post_data(request_rec *r, char **data, apr_size_t *length)
|
||||
{
|
||||
apr_size_t bytes_read;
|
||||
apr_size_t bytes_left;
|
||||
apr_size_t len;
|
||||
long read_length;
|
||||
int rc;
|
||||
|
||||
/* Prepare to receive data from the client. We request that apache
|
||||
* dechunks data if it is chunked.
|
||||
*/
|
||||
rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
|
||||
if (rc != OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* This function will send a 100 Continue response if the client is
|
||||
* waiting for that. If the client isn't going to send data, then this
|
||||
* function will return 0.
|
||||
*/
|
||||
if (!ap_should_client_block(r)) {
|
||||
len = 0;
|
||||
} else {
|
||||
len = r->remaining;
|
||||
}
|
||||
|
||||
if (length != NULL) {
|
||||
*length = len;
|
||||
}
|
||||
|
||||
*data = (char *)apr_palloc(r->pool, len + 1);
|
||||
|
||||
/* Make sure that the data is null-terminated. */
|
||||
(*data)[len] = '\0';
|
||||
|
||||
bytes_read = 0;
|
||||
bytes_left = len;
|
||||
|
||||
while (bytes_left > 0) {
|
||||
/* Read data from the client. Returns 0 on EOF or error, the
|
||||
* number of bytes otherwise.
|
||||
*/
|
||||
read_length = ap_get_client_block(r, &(*data)[bytes_read],
|
||||
bytes_left);
|
||||
if (read_length == 0) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Failed to read POST data from client.");
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
bytes_read += read_length;
|
||||
bytes_left -= read_length;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* extract_query_parameter is a function which extracts the value of
|
||||
* a given parameter in a query string. The query string can be the
|
||||
* query_string parameter of a GET request, or it can be the data
|
||||
* passed to the web server in a POST request.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *pool The memory pool which the memory for
|
||||
* the value will be allocated from.
|
||||
* const char *query_string Either the query_string from a GET
|
||||
* request, or the data from a POST
|
||||
* request.
|
||||
* const char *name The name of the parameter to extract.
|
||||
* Note that the search for this name is
|
||||
* case sensitive.
|
||||
*
|
||||
* Returns:
|
||||
* The value of the parameter or NULL if we don't find the parameter.
|
||||
*/
|
||||
char *am_extract_query_parameter(apr_pool_t *pool,
|
||||
const char *query_string,
|
||||
const char *name)
|
||||
{
|
||||
const char *ip;
|
||||
const char *value_end;
|
||||
apr_size_t namelen;
|
||||
|
||||
if (query_string == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ip = query_string;
|
||||
namelen = strlen(name);
|
||||
|
||||
/* Find parameter. Searches for /[^&]<name>[&=$]/.
|
||||
* Moves ip to the first character after the name (either '&', '='
|
||||
* or '\0').
|
||||
*/
|
||||
for (;;) {
|
||||
/* First we find the name of the parameter. */
|
||||
ip = strstr(ip, name);
|
||||
if (ip == NULL) {
|
||||
/* Parameter not found. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Then we check what is before the parameter name. */
|
||||
if (ip != query_string && ip[-1] != '&') {
|
||||
/* Name not preceded by [^&]. */
|
||||
ip++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* And last we check what follows the parameter name. */
|
||||
if (ip[namelen] != '=' && ip[namelen] != '&'
|
||||
&& ip[namelen] != '\0') {
|
||||
/* Name not followed by [&=$]. */
|
||||
ip++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* We have found the pattern. */
|
||||
ip += namelen;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now ip points to the first character after the name. If this
|
||||
* character is '&' or '\0', then this field doesn't have a value.
|
||||
* If this character is '=', then this field has a value.
|
||||
*/
|
||||
if (ip[0] == '=') {
|
||||
ip += 1;
|
||||
}
|
||||
|
||||
/* The value is from ip to '&' or to the end of the string, whichever
|
||||
* comes first. */
|
||||
value_end = strchr(ip, '&');
|
||||
if (value_end != NULL) {
|
||||
/* '&' comes first. */
|
||||
return apr_pstrndup(pool, ip, value_end - ip);
|
||||
} else {
|
||||
/* Value continues until the end of the string. */
|
||||
return apr_pstrdup(pool, ip);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function urldecodes a string in-place.
|
||||
*
|
||||
* Parameters:
|
||||
* char *data The string to urldecode.
|
||||
*
|
||||
* Returns:
|
||||
* OK if successful or HTTP_BAD_REQUEST if any escape sequence decodes to a
|
||||
* null-byte ('\0'), or if an invalid escape sequence is found.
|
||||
*/
|
||||
int am_urldecode(char *data)
|
||||
{
|
||||
int rc;
|
||||
char *ip;
|
||||
|
||||
/* First we replace all '+'-characters with space. */
|
||||
for (ip = strchr(data, '+'); ip != NULL; ip = strchr(ip, '+')) {
|
||||
*ip = ' ';
|
||||
}
|
||||
|
||||
/* Then we call ap_unescape_url_keep2f to decode all the "%xx"
|
||||
* escapes. This function returns HTTP_NOT_FOUND if the string
|
||||
* contains a null-byte.
|
||||
*/
|
||||
rc = ap_unescape_url_keep2f(data);
|
||||
if (rc == HTTP_NOT_FOUND) {
|
||||
return HTTP_BAD_REQUEST;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* This function urlencodes a string. It will escape all characters
|
||||
* except a-z, A-Z, 0-9, '_' and '.'.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *pool The pool we should allocate memory from.
|
||||
* const char *str The string we should urlencode.
|
||||
*
|
||||
* Returns:
|
||||
* The urlencoded string, or NULL if str == NULL.
|
||||
*/
|
||||
char *am_urlencode(apr_pool_t *pool, const char *str)
|
||||
{
|
||||
const char *ip;
|
||||
apr_size_t length;
|
||||
char *ret;
|
||||
char *op;
|
||||
int hi, low;
|
||||
/* Return NULL if str is NULL. */
|
||||
if(str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Find the length of the output string. */
|
||||
length = 0;
|
||||
for(ip = str; *ip; ip++) {
|
||||
if(*ip >= 'a' && *ip <= 'z') {
|
||||
length++;
|
||||
} else if(*ip >= 'A' && *ip <= 'Z') {
|
||||
length++;
|
||||
} else if(*ip >= '0' && *ip <= '9') {
|
||||
length++;
|
||||
} else if(*ip == '_' || *ip == '.') {
|
||||
length++;
|
||||
} else {
|
||||
length += 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add space for null-terminator. */
|
||||
length++;
|
||||
|
||||
/* Allocate memory for string. */
|
||||
ret = (char *)apr_palloc(pool, length);
|
||||
|
||||
/* Encode string. */
|
||||
for(ip = str, op = ret; *ip; ip++, op++) {
|
||||
if(*ip >= 'a' && *ip <= 'z') {
|
||||
*op = *ip;
|
||||
} else if(*ip >= 'A' && *ip <= 'Z') {
|
||||
*op = *ip;
|
||||
} else if(*ip >= '0' && *ip <= '9') {
|
||||
*op = *ip;
|
||||
} else if(*ip == '_' || *ip == '.') {
|
||||
*op = *ip;
|
||||
} else {
|
||||
*op = '%';
|
||||
op++;
|
||||
|
||||
hi = (*ip & 0xf0) >> 4;
|
||||
|
||||
if(hi < 0xa) {
|
||||
*op = '0' + hi;
|
||||
} else {
|
||||
*op = 'A' + hi - 0xa;
|
||||
}
|
||||
op++;
|
||||
|
||||
low = *ip & 0x0f;
|
||||
|
||||
if(low < 0xa) {
|
||||
*op = '0' + low;
|
||||
} else {
|
||||
*op = 'A' + low - 0xa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make output string null-terminated. */
|
||||
*op = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function generates a given number of (pseudo)random bytes.
|
||||
* The current implementation uses OpenSSL's RAND_*-functions.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we are generating random bytes for.
|
||||
* The request is used for configuration and
|
||||
* error/warning reporting.
|
||||
* void *dest The address if the buffer we should fill with data.
|
||||
* apr_size_t count The number of random bytes to create.
|
||||
*
|
||||
* Returns:
|
||||
* OK on success, or HTTP_INTERNAL_SERVER on failure.
|
||||
*/
|
||||
int am_generate_random_bytes(request_rec *r, void *dest, apr_size_t count)
|
||||
{
|
||||
int rc;
|
||||
rc = RAND_pseudo_bytes((unsigned char *)dest, (int)count);
|
||||
if(rc == -1) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Error generating random data: %lu",
|
||||
ERR_get_error());
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
if(rc == 0) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
|
||||
"Random data is not cryptographically strong.");
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function generates a session id which is AM_SESSION_ID_LENGTH
|
||||
* characters long. The session id will consist of hexadecimal characters.
|
||||
*
|
||||
* Parameters:
|
||||
* request_rec *r The request we generate a session id for.
|
||||
*
|
||||
* Returns:
|
||||
* The session id, made up of AM_SESSION_ID_LENGTH hexadecimal characters,
|
||||
* terminated by a null-byte.
|
||||
*/
|
||||
char *am_generate_session_id(request_rec *r)
|
||||
{
|
||||
int rc;
|
||||
char *ret;
|
||||
int rand_data_len;
|
||||
unsigned char *rand_data;
|
||||
int i;
|
||||
unsigned char b;
|
||||
int hi, low;
|
||||
|
||||
ret = (char *)apr_palloc(r->pool, AM_SESSION_ID_LENGTH + 1);
|
||||
|
||||
/* We need to round the length of the random data _up_, in case the
|
||||
* length of the session id isn't even.
|
||||
*/
|
||||
rand_data_len = (AM_SESSION_ID_LENGTH + 1) / 2;
|
||||
|
||||
/* Fill the last rand_data_len bytes of the string with
|
||||
* random bytes. This allows us to overwrite from the beginning of
|
||||
* the string.
|
||||
*/
|
||||
rand_data = (unsigned char *)&ret[AM_SESSION_ID_LENGTH - rand_data_len];
|
||||
|
||||
/* Generate random numbers. */
|
||||
rc = am_generate_random_bytes(r, rand_data, rand_data_len);
|
||||
if(rc != OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert the random bytes to hexadecimal. Note that we will write
|
||||
* AM_SESSION_LENGTH+1 characters if we have a non-even length of the
|
||||
* session id. This is OK - we will simply overwrite the last character
|
||||
* with the null-terminator afterwards.
|
||||
*/
|
||||
for(i = 0; i < AM_SESSION_ID_LENGTH; i += 2) {
|
||||
b = rand_data[i / 2];
|
||||
hi = (b >> 4) & 0xf;
|
||||
low = b & 0xf;
|
||||
|
||||
if(hi >= 0xa) {
|
||||
ret[i] = 'a' + hi - 0xa;
|
||||
} else {
|
||||
ret[i] = '0' + hi;
|
||||
}
|
||||
|
||||
if(low >= 0xa) {
|
||||
ret[i+1] = 'a' + low - 0xa;
|
||||
} else {
|
||||
ret[i+1] = '0' + low;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add null-terminator- */
|
||||
ret[AM_SESSION_ID_LENGTH] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
autoreconf --force --install
|
||||
rm -rf autom4te.cache/
|
|
@ -0,0 +1,66 @@
|
|||
AC_INIT([mod_auth_mellon],[0.0.6],[olavmrk@gmail.com])
|
||||
|
||||
AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
|
||||
|
||||
|
||||
# This section defines the --with-apxs2 option.
|
||||
AC_ARG_WITH(
|
||||
[apxs2],
|
||||
[ --with-apxs2=PATH Full path to the apxs2 executable.],
|
||||
[
|
||||
APXS2=${withval}
|
||||
],
|
||||
[
|
||||
APXS2='unknown'
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
if test "$APXS2" = 'unknown'; then
|
||||
# The user didn't specify the --with-apxs2-option.
|
||||
|
||||
# Search for apxs2 in the specified directories
|
||||
AC_PATH_PROG(APXS2, apxs2, 'unknown',
|
||||
/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin)
|
||||
|
||||
if test "$APXS2" = 'unknown'; then
|
||||
# Didn't find apxs2 in any of the specified directories.
|
||||
# Search for apxs instead.
|
||||
AC_PATH_PROG(APXS2, apxs, 'unknown',
|
||||
/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin)
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# Test if $APXS2 exists and is an executable.
|
||||
if test ! -x "$APXS2"; then
|
||||
# $APXS2 isn't a executable file.
|
||||
AC_MSG_ERROR([
|
||||
Could not find apxs2. Please spesify the path to apxs2
|
||||
using the --with-apxs2=/full/path/to/apxs2 option.
|
||||
The executable may also be named 'apxs'.
|
||||
])
|
||||
fi
|
||||
|
||||
# Replace any occurances of @APXS2@ with the value of $APXS2 in the Makefile.
|
||||
AC_SUBST(APXS2)
|
||||
|
||||
# We need the lasso library for SAML2 communication.
|
||||
PKG_CHECK_MODULES(LASSO, lasso)
|
||||
AC_SUBST(LASSO_CFLAGS)
|
||||
AC_SUBST(LASSO_LIBS)
|
||||
|
||||
# We need the curl library for HTTP-Artifact downloads.
|
||||
PKG_CHECK_MODULES(CURL, libcurl)
|
||||
AC_SUBST(CURL_CFLAGS)
|
||||
AC_SUBST(CURL_LIBS)
|
||||
|
||||
# We also need openssl for its random number generator.
|
||||
PKG_CHECK_MODULES(OPENSSL, openssl)
|
||||
AC_SUBST(OPENSSL_CFLAGS)
|
||||
AC_SUBST(OPENSSL_LIBS)
|
||||
|
||||
|
||||
# Create Makefile from Makefile.in
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
|
@ -0,0 +1,11 @@
|
|||
# MellonCacheSize sets the maximum number of sessions which can be active
|
||||
# at once. When mod_auth_mellon reaches this limit, it will begin removing
|
||||
# the least recently used sessions.
|
||||
# Default: MellonCacheSize 100
|
||||
#MellonCacheSize 100
|
||||
|
||||
# MellonLockFile is the full path to a file used for synchronizing access
|
||||
# to the session data. The path should only be used by one instance of
|
||||
# apache at a time.
|
||||
# Default: MellonLockFile "/tmp/mellonLock"
|
||||
#MellonLockFile "/tmp/mellonLock"
|
|
@ -0,0 +1 @@
|
|||
LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so
|
|
@ -0,0 +1,41 @@
|
|||
libapache2-mod-auth-mellon (0.0.6-1) unstable; urgency=low
|
||||
|
||||
* Update version to 0.0.6.
|
||||
|
||||
-- Olav Morken <olavmrk@gmail.com> Wed, 15 Aug 2007 14:03:23 +0200
|
||||
|
||||
|
||||
libapache2-mod-auth-mellon (0.0.5-1) unstable; urgency=low
|
||||
|
||||
* Update version to 0.0.5.
|
||||
|
||||
-- Olav Morken <olavmrk@gmail.com> Wed, 8 Aug 2007 11:36:13 +0200
|
||||
|
||||
|
||||
libapache2-mod-auth-mellon (0.0.4-1) unstable; urgency=low
|
||||
|
||||
* Update version to 0.0.4.
|
||||
|
||||
-- Olav Morken <olavmrk@gmail.com> Tue, 7 Aug 2007 10:30:43 +0200
|
||||
|
||||
|
||||
libapache2-mod-auth-mellon (0.0.3-1) unstable; urgency=low
|
||||
|
||||
* Update version to 0.0.3.
|
||||
|
||||
-- Olav Morken <olavmrk@gmail.com> Fri, 13 Jul 2007 14:30:05 +0200
|
||||
|
||||
|
||||
libapache2-mod-auth-mellon (0.0.2-1) unstable; urgency=low
|
||||
|
||||
* Update version to 0.0.2.
|
||||
|
||||
-- Olav Morken <olavmrk@gmail.com> Tue, 10 Jul 2007 08:55:49 +0200
|
||||
|
||||
|
||||
libapache2-mod-auth-mellon (0.0.1-1) unstable; urgency=low
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Olav Morken <olavmrk@gmail.com> Mon, 9 Jul 2007 09:52:45 +0200
|
||||
|
|
@ -0,0 +1 @@
|
|||
5
|
|
@ -0,0 +1,15 @@
|
|||
Source: libapache2-mod-auth-mellon
|
||||
Section: web
|
||||
Priority: extra
|
||||
Maintainer: Olav Morken <olavmrk@gmail.com>
|
||||
Build-Depends: debhelper (>= 5), autotools-dev, apache2-prefork-dev (>= 2.0.55), libcurl3-dev, liblasso3-dev (>= 2.1.0)
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: libapache2-mod-auth-mellon
|
||||
Architecture: any
|
||||
Depends: openssl, apache2.2-common, libcurl3, liblasso3 (>= 2.1.0)
|
||||
Description: A SAML 2.0 authentication module for Apache
|
||||
mod-auth-mellon is an Apache module which enables you to authenticate
|
||||
users of a web site against a SAML 2.0 enabled IdP.
|
||||
After installing this package, you should run "a2enmon auth_mellon". For
|
||||
configuration information, see /usr/share/doc/mod-auth-mellon/README.gz
|
|
@ -0,0 +1,12 @@
|
|||
This package was debianized by Olav Morken <olavmrk@gmail.com> on
|
||||
Fri, 6 Jul 2007 15:25:15 +0200.
|
||||
|
||||
Copyright: 2007 UNINETT
|
||||
|
||||
License:
|
||||
|
||||
GPL
|
||||
|
||||
|
||||
The Debian packaging is (C) 2007, UNINETT and
|
||||
is licensed under the GPL, see `/usr/share/common-licenses/GPL'.
|
|
@ -0,0 +1 @@
|
|||
usr/lib/apache2/modules
|
|
@ -0,0 +1,2 @@
|
|||
README
|
||||
TODO
|
|
@ -0,0 +1,3 @@
|
|||
.libs/mod_auth_mellon.so usr/lib/apache2/modules
|
||||
debian/auth_mellon.load /etc/apache2/mods-available
|
||||
debian/auth_mellon.conf /etc/apache2/mods-available
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
# Sample debian/rules that uses debhelper.
|
||||
# This file was originally written by Joey Hess and Craig Small.
|
||||
# As a special exception, when this file is copied by dh-make into a
|
||||
# dh-make output file, you may use that output file without restriction.
|
||||
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
|
||||
# These are used for cross-compiling and for saving the configure script
|
||||
# from having to guess our platform (since we know it already)
|
||||
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
|
||||
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
|
||||
|
||||
|
||||
CFLAGS = -Wall -g
|
||||
|
||||
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
|
||||
configure:
|
||||
./autogen.sh
|
||||
|
||||
config.status: configure
|
||||
dh_testdir
|
||||
# Add here commands to configure the package.
|
||||
./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs"
|
||||
|
||||
|
||||
build: build-stamp
|
||||
|
||||
build-stamp: config.status
|
||||
dh_testdir
|
||||
|
||||
# Add here commands to compile the package.
|
||||
$(MAKE)
|
||||
#docbook-to-man debian/mod-auth-mellon.sgml > mod-auth-mellon.1
|
||||
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
|
||||
# Add here commands to clean up after the build process.
|
||||
-$(MAKE) distclean
|
||||
ifneq "$(wildcard /usr/share/misc/config.sub)" ""
|
||||
cp -f /usr/share/misc/config.sub config.sub
|
||||
endif
|
||||
ifneq "$(wildcard /usr/share/misc/config.guess)" ""
|
||||
cp -f /usr/share/misc/config.guess config.guess
|
||||
endif
|
||||
|
||||
|
||||
dh_clean
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
dh_install
|
||||
|
||||
|
||||
# Build architecture-independent files here.
|
||||
binary-indep: build install
|
||||
# We have nothing to do by default.
|
||||
|
||||
# Build architecture-dependent files here.
|
||||
binary-arch: build install
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installchangelogs
|
||||
dh_installdocs
|
||||
dh_installexamples
|
||||
# dh_install
|
||||
# dh_installmenu
|
||||
# dh_installdebconf
|
||||
# dh_installlogrotate
|
||||
# dh_installemacsen
|
||||
# dh_installpam
|
||||
# dh_installmime
|
||||
# dh_python
|
||||
# dh_installinit
|
||||
# dh_installcron
|
||||
# dh_installinfo
|
||||
dh_installman
|
||||
dh_link
|
||||
dh_strip
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
# dh_perl
|
||||
# dh_makeshlibs
|
||||
dh_installdeb
|
||||
# dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
*
|
||||
* mod_auth_mellon.c: an authentication apache module
|
||||
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "auth_mellon.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
||||
/* This function is called on server exit. It destroys the shared memory we
|
||||
* allocated for storing session data, and the global mutex we used to
|
||||
* synchronize access to the shared memory.
|
||||
*
|
||||
* The function is registered as a cleanup-function on the configuration
|
||||
* pool.
|
||||
*
|
||||
* Parameters:
|
||||
* void *p A pointer to the current server record.
|
||||
*
|
||||
* Returns:
|
||||
* This function always return OK.
|
||||
*/
|
||||
static apr_status_t am_global_kill(void *p)
|
||||
{
|
||||
server_rec *s = (server_rec *) p;
|
||||
am_mod_cfg_rec *m = am_get_mod_cfg(s);
|
||||
|
||||
if (m->cache) {
|
||||
/* Destroy the shared memory for session data. */
|
||||
apr_shm_destroy(m->cache);
|
||||
m->cache = NULL;
|
||||
}
|
||||
|
||||
if(m->lock) {
|
||||
/* Destroy the mutex. */
|
||||
apr_global_mutex_destroy(m->lock);
|
||||
m->lock = NULL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function is called after the configuration of the server is parsed
|
||||
* (it's a post-config hook).
|
||||
*
|
||||
* It initializes the shared memory and the mutex which is used to protect
|
||||
* the shared memory.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *conf The configuration pool. Valid as long as this
|
||||
* configuration is valid.
|
||||
* apr_pool_t *log A pool for memory which is cleared after each read
|
||||
* through the config files.
|
||||
* apr_pool_t *tmp A pool for memory which will be destroyed after
|
||||
* all the post_config hooks are run.
|
||||
* server_rec *s The current server record.
|
||||
*
|
||||
* Returns:
|
||||
* OK on successful initialization, or !OK on failure.
|
||||
*/
|
||||
static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
|
||||
apr_pool_t *tmp, server_rec *s)
|
||||
{
|
||||
am_cache_entry_t *table;
|
||||
apr_size_t mem_size;
|
||||
am_mod_cfg_rec *mod;
|
||||
int rv, i;
|
||||
const char userdata_key[] = "auth_mellon_init";
|
||||
char buffer[512];
|
||||
void *data;
|
||||
|
||||
/* Apache tests loadable modules by loading them (as is the only way).
|
||||
* This has the effect that all modules are loaded and initialised twice,
|
||||
* and we just want to initialise shared memory and mutexes when the
|
||||
* module loads for real!
|
||||
*
|
||||
* To accomplish this, we store a piece of data as userdata in the
|
||||
* process pool the first time the function is run. This data can be
|
||||
* detected on all subsequent runs, and then we know that this isn't the
|
||||
* first time this function runs.
|
||||
*/
|
||||
apr_pool_userdata_get(&data, userdata_key, s->process->pool);
|
||||
if (!data) {
|
||||
/* This is the first time this function is run. */
|
||||
apr_pool_userdata_set((const void *)1, userdata_key,
|
||||
apr_pool_cleanup_null, s->process->pool);
|
||||
return OK;
|
||||
}
|
||||
|
||||
mod = am_get_mod_cfg(s);
|
||||
|
||||
/* If the session store is initialized then we can't change it. */
|
||||
if(mod->cache != NULL) {
|
||||
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
|
||||
"auth_mellon session store already initialized -"
|
||||
" reinitialization skipped.");
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Copy from the variables set by the configuration file into variables
|
||||
* which will be set only once. We do this to avoid confusion if the user
|
||||
* tries to change the parameters of the session store after it is
|
||||
* initialized.
|
||||
*/
|
||||
mod->init_cache_size = mod->cache_size;
|
||||
mod->init_lock_file = apr_pstrdup(conf, mod->lock_file);
|
||||
|
||||
|
||||
/* find out the memory size of the cache */
|
||||
mem_size = sizeof(am_cache_entry_t) * mod->init_cache_size;
|
||||
|
||||
/* register a function to clean up the whole mess on exit */
|
||||
apr_pool_cleanup_register(conf, s,
|
||||
am_global_kill,
|
||||
apr_pool_cleanup_null);
|
||||
|
||||
|
||||
/* Create the shared memory, exit if it fails. */
|
||||
rv = apr_shm_create(&(mod->cache), mem_size, NULL, conf);
|
||||
|
||||
if (rv != APR_SUCCESS) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
|
||||
"shm_create: Error [%d] \"%s\"", rv,
|
||||
apr_strerror(rv, buffer, sizeof(buffer)));
|
||||
return !OK;
|
||||
}
|
||||
|
||||
/* Initialize the session table. */
|
||||
table = apr_shm_baseaddr_get(mod->cache);
|
||||
for (i = 0; i < mod->cache_size; i++) {
|
||||
table[i].key[0] = '\0';
|
||||
table[i].access = 0;
|
||||
}
|
||||
|
||||
/* Now create the mutex that we need for locking the shared memory, then
|
||||
* test for success. we really need this, so we exit on failure. */
|
||||
rv = apr_global_mutex_create(&(mod->lock),
|
||||
mod->init_lock_file,
|
||||
APR_LOCK_DEFAULT,
|
||||
conf);
|
||||
|
||||
if (rv != APR_SUCCESS) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
|
||||
"mutex_create: Error [%d] \"%s\"", rv,
|
||||
apr_strerror(rv, buffer, sizeof(buffer)));
|
||||
return !OK;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/* This function is run when each child process of apache starts.
|
||||
* apr_global_mutex_child_init must be run on the session data mutex for
|
||||
* every child process of apache.
|
||||
*
|
||||
* Parameters:
|
||||
* apr_pool_t *p This pool is for data associated with this
|
||||
* child process.
|
||||
* server_rec *s The server record for the current server.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*/
|
||||
static void am_child_init(apr_pool_t *p, server_rec *s)
|
||||
{
|
||||
am_mod_cfg_rec *m = am_get_mod_cfg(s);
|
||||
apr_status_t rv;
|
||||
CURLcode curl_res;
|
||||
|
||||
/* Reinitialize the mutex for the child process. */
|
||||
rv = apr_global_mutex_child_init(&(m->lock), m->init_lock_file, p);
|
||||
if (rv != APR_SUCCESS) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
|
||||
"Child process could not connect to mutex");
|
||||
}
|
||||
|
||||
/* lasso_init() must be run before any other lasso-functions. */
|
||||
lasso_init();
|
||||
|
||||
/* curl_global_init() should be called before any other curl
|
||||
* function. Relying on curl_easy_init() to call curl_global_init()
|
||||
* isn't thread safe.
|
||||
*/
|
||||
curl_res = curl_global_init(CURL_GLOBAL_SSL);
|
||||
if(curl_res != CURLE_OK) {
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
|
||||
"Failed to initialize curl library: %u", curl_res);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void register_hooks(apr_pool_t *p)
|
||||
{
|
||||
ap_hook_access_checker(am_auth_mellon_user, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_check_user_id(am_check_uid, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_post_config(am_global_init, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_child_init(am_child_init, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
module AP_MODULE_DECLARE_DATA auth_mellon_module =
|
||||
{
|
||||
STANDARD20_MODULE_STUFF,
|
||||
auth_mellon_dir_config,
|
||||
auth_mellon_dir_merge,
|
||||
auth_mellon_server_config,
|
||||
NULL,
|
||||
auth_mellon_commands,
|
||||
register_hooks
|
||||
};
|
||||
|
Loading…
Reference in New Issue