Merge branch 'upstream' into debian

This commit is contained in:
Timo Aaltonen 2011-10-28 13:13:45 +03:00
commit 4026d69f55
894 changed files with 403059 additions and 0 deletions

2
.bzrignore Normal file
View File

@ -0,0 +1,2 @@
.git
freeipa2-dev-doc

76
.gitignore vendored Normal file
View File

@ -0,0 +1,76 @@
configure
config.h
config.h.in
Makefile
Makefile.in
.deps/
.libs/
*.la
*.lo
*.o
*.pyc
.bzr
freeipa2-dev-doc
build
dist/
RELEASE
daemons/AUTHORS
daemons/COPYING
daemons/ChangeLog
daemons/INSTALL
daemons/NEWS
daemons/README
daemons/aclocal.m4
daemons/autom4te.cache/
daemons/config.guess
daemons/config.log
daemons/config.status
daemons/config.sub
daemons/depcomp
daemons/install-sh
daemons/ipa-kpasswd/ipa_kpasswd
daemons/ipa-version.h
daemons/libtool
daemons/ltmain.sh
daemons/missing
daemons/stamp-h1
install/AUTHORS
install/COPYING
install/ChangeLog
install/INSTALL
install/NEWS
install/README
install/aclocal.m4
install/autom4te.cache/
install/config.log
install/config.status
install/install-sh
install/missing
install/stamp-h1
install/ui/test/results
ipa-client/COPYING
ipa-client/ChangeLog
ipa-client/INSTALL
ipa-client/aclocal.m4
ipa-client/autom4te.cache/
ipa-client/config.guess
ipa-client/config.log
ipa-client/config.status
ipa-client/config.sub
ipa-client/depcomp
ipa-client/install-sh
ipa-client/ipa-client.spec
ipa-client/ipa-getkeytab
ipa-client/ipa-join
ipa-client/ipa-rmkeytab
ipa-client/libtool
ipa-client/ltmain.sh
ipa-client/missing
ipa-client/py-compile
ipa-client/stamp-h1
ipa-client/version.m4
freeipa.spec
ipapython/setup.py
ipapython/version.py
version.m4

8
.tx/config Normal file
View File

@ -0,0 +1,8 @@
[main]
host = https://www.transifex.net
[freeipa.ipa]
file_filter = install/po/<lang>.po
source_file = install/po/ipa.pot
source_lang = en

2880
API.txt Normal file

File diff suppressed because it is too large Load Diff

97
BUILD.txt Normal file
View File

@ -0,0 +1,97 @@
Here is a quickie guide to get you started in IPA development.
Dependencies
------------
The quickest way to get the dependencies needed for building is:
# yum install rpm-build `grep "^BuildRequires" freeipa.spec.in | awk '{ print $2 }' | grep -v "^/"`
This is currently (01/05/11):
yum install 389-ds-base-devel mozldap-devel svrcore-devel nspr-devel \
openssl-devel openldap-devel e2fsprogs-devel krb5-devel nss-devel \
libcap-devel python-devel autoconf automake libtool popt-devel m4 \
policycoreutils python-setuptools python-krbV xmlrpc-c-devel \
libcurl-devel gettext authconfig libuuid-devel
Building
--------
From the root of the source tree run:
$ make rpms
The resulting rpm packages are in dist/rpms:
# yum --nogpgcheck localinstall dist/rpms/*
# ipa-server-install
It may be possible to do a simple make all install but this has not been
well-tested. Additional work is done in pre/post install scripts in the ipa
spec file.
Developing plugins
------------------
It is possible to do management plugin development within the source tree.
To start with, you need a full IPA install on the current system. Build and
install the rpms and then configure IPA using ipa-server-install.
Get a TGT for the admin user with: kinit admin
Next you'll need 2 sessions in the source tree. In the first session run
python lite-server.py. In the second session you can run the ./ipa
tool and it will make requests to the lite-server listening on 127.0.0.1:8080.
This makes developing plugins much faster and you can also make use of the
Python pdb debugger on the server side.
You'll find you may need to refresh the underlying build if schema or other
changes are required.
Testing
-------
We use python nosetests to test for regressions in the management framework
and plugins. You'll need the python-nose package installed to run the tests.
To run all of the tests you will need 2 sessions, one to run the lite-server
and the other to execute the tests. You'll also need a TGT before starting
the lite-server:
% kinit admin
% make test
Some tests may be skipped. For example, all the XML-RPC tests will be skipped
if you haven't started the lite-server. The DNS tests will be skipped if
the underlying IPA installation doesn't configure DNS, etc.
API.txt
-------
The purpose of the file API.txt is to prevent accidental API changes. The
program ./makeapi creates file and also validates it (with the --validate
option). This validation is part of the build process.
There are three solutions to changes to the API:
1. Changes to existing API require a change to the MAJOR version.
2. Addition of new API requires a change to the MINOR version.
3. Or just back out your changes and don't make an API change.
If the API changes you'll need to run ./makeapi to update API.txt and
commit it along with VERSION with your API change.
If a module is optionally loaded then you will need to be able to
conditionally load it for API validation. The environment variable
api.env.validate_api is True during validation.
General Notes
-------------
IPA is not relocatable.
When building rpms the version contains the GIT id in the version. To prevent
this pass the argument IPA_VERSION_IS_GIT_SNAPSHOT=yes to make.
If you don't need a full CA during testing then using the self-signed CA
(pass --selfsign to ipa-server-install) takes less time to install.

674
COPYING Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. 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
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. 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.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<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 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program 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, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU 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. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

76
Contributors.txt Normal file
View File

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
The following people have contributed to the FreeIPA project.
(Listed in alphabetical order within category)
Developers:
Jr Aquino
Alexander Bokovoy
Jan Cholasta
Rob Crittenden
Nalin Dahyabhai
John Dennis
Jason DeRose
Endi Dewata
Jakub Hrozek
Martin Kosek
Nathan Kinder
Rich Megginson
Martin Nagy
Simo Sorce
Andrew Wnuk
Adam Young
Jan Zeleny
Pavel Zůna
Documentation:
David O'Brien
Testing:
Jenny Galipeau
Michael Gregg
Suzanne Hillman
Chandrasekar Kannan
Gowrishankar Rajaiyan
Yi Zhang
Translators:
Héctor Daniel Cabrera
Yuri Chornoivan
Teguh DC
Piotr Drąg
Gundachandru
Jake Li
Andrew Martynov
Sankarshan Mukhopadhyay
Wiki, Solution and Idea Contributors:
Viji V Nair
Ryan Thompson
David Zeuthen
Graphic Design and User Interaction Design:
Máirín Duffy
Managment:
Scott Haines
Bob Lord
Dmitri Pal
Kevin Unthank
Karl Wirth
Past and Occasional Contributors:
Sylvain Baubeau
Yuri Chornoivan
Frank Cusack
Don Davis
Gunther Deschner
Stephen Gallagher
Ian Kumlien
Karl MacMillan
Jon McCann
Kevin McCarthy
Jim Meyering
Pete Rowley
Andreas Schneider

2
MANIFEST.in Normal file
View File

@ -0,0 +1,2 @@
include COPYING TODO lite-server.py
include tests/*/*.py

218
Makefile Normal file
View File

@ -0,0 +1,218 @@
include VERSION
SUBDIRS=daemons install ipapython ipa-client
CLIENTDIRS=ipapython ipa-client
PRJ_PREFIX=freeipa
RPMBUILD ?= $(PWD)/rpmbuild
TARGET ?= master
SUPPORTED_PLATFORM=redhat
# After updating the version in VERSION you should run the version-update
# target.
ifeq ($(IPA_VERSION_IS_GIT_SNAPSHOT),"yes")
GIT_VERSION=$(shell git show --pretty=format:"%h" --stat HEAD 2>/dev/null|head -1)
ifneq ($(GIT_VERSION),)
IPA_VERSION=$(IPA_VERSION_MAJOR).$(IPA_VERSION_MINOR).$(IPA_VERSION_RELEASE)GIT$(GIT_VERSION)
endif # in a git tree and git returned a version
endif # git
ifndef IPA_VERSION
ifdef IPA_VERSION_PRE_RELEASE
IPA_VERSION=$(IPA_VERSION_MAJOR).$(IPA_VERSION_MINOR).$(IPA_VERSION_RELEASE).pre$(IPA_VERSION_PRE_RELEASE)
else
ifdef IPA_VERSION_RC_RELEASE
IPA_VERSION=$(IPA_VERSION_MAJOR).$(IPA_VERSION_MINOR).$(IPA_VERSION_RELEASE).rc$(IPA_VERSION_RC_RELEASE)
else
IPA_VERSION=$(IPA_VERSION_MAJOR).$(IPA_VERSION_MINOR).$(IPA_VERSION_RELEASE)
endif # rc
endif # pre
endif # ipa_version
TARBALL_PREFIX=freeipa-$(IPA_VERSION)
TARBALL=$(TARBALL_PREFIX).tar.gz
IPA_RPM_RELEASE=$(shell cat RELEASE)
LIBDIR ?= /usr/lib
DEVELOPER_MODE ?= 0
ifneq ($(DEVELOPER_MODE),0)
LINT_OPTIONS=--no-fail
endif
all: bootstrap-autogen server
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
client: client-autogen
@for subdir in $(CLIENTDIRS); do \
(cd $$subdir && $(MAKE) all) || exit 1; \
done
bootstrap-autogen: version-update client-autogen
@echo "Building IPA $(IPA_VERSION)"
cd daemons; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR) --with-openldap; fi
cd install; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); fi
client-autogen: version-update
cd ipa-client; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); fi
cd install; if [ ! -e Makefile ]; then ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); fi
install: all server-install
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
client-install: client
@for subdir in $(CLIENTDIRS); do \
(cd $$subdir && $(MAKE) install) || exit 1; \
done
cd install/po && $(MAKE) install || exit 1;
if [ "$(DESTDIR)" = "" ]; then \
python setup-client.py install; \
else \
python setup-client.py install --root $(DESTDIR); \
fi
lint:
./make-lint $(LINT_OPTIONS)
test:
$(MAKE) -C install/po test_lang
./make-testcert
./make-test
release-update:
if [ ! -e RELEASE ]; then echo 0 > RELEASE; fi
version-update: release-update
sed -e s/__VERSION__/$(IPA_VERSION)/ -e s/__RELEASE__/$(IPA_RPM_RELEASE)/ \
freeipa.spec.in > freeipa.spec
sed -e s/__VERSION__/$(IPA_VERSION)/ version.m4.in \
> version.m4
sed -e s/__VERSION__/$(IPA_VERSION)/ ipapython/setup.py.in \
> ipapython/setup.py
sed -e s/__VERSION__/$(IPA_VERSION)/ ipapython/version.py.in \
> ipapython/version.py
perl -pi -e "s:__NUM_VERSION__:$(IPA_VERSION_MAJOR)$(IPA_VERSION_MINOR)$(IPA_VERSION_RELEASE):" ipapython/version.py
perl -pi -e "s:__API_VERSION__:$(IPA_API_VERSION_MAJOR).$(IPA_API_VERSION_MINOR):" ipapython/version.py
sed -e s/__VERSION__/$(IPA_VERSION)/ daemons/ipa-version.h.in \
> daemons/ipa-version.h
perl -pi -e "s:__NUM_VERSION__:$(IPA_VERSION_MAJOR)$(IPA_VERSION_MINOR)$(IPA_VERSION_RELEASE):" daemons/ipa-version.h
perl -pi -e "s:__DATA_VERSION__:$(IPA_DATA_VERSION):" daemons/ipa-version.h
sed -e s/__VERSION__/$(IPA_VERSION)/ -e s/__RELEASE__/$(IPA_RPM_RELEASE)/ \
ipa-client/ipa-client.spec.in > ipa-client/ipa-client.spec
sed -e s/__VERSION__/$(IPA_VERSION)/ ipa-client/version.m4.in \
> ipa-client/version.m4
if [ "$(SUPPORTED_PLATFORM)" != "" ]; then \
sed -e s/SUPPORTED_PLATFORM/$(SUPPORTED_PLATFORM)/ ipapython/services.py.in \
> ipapython/services.py; \
fi
if [ "$(SKIP_API_VERSION_CHECK)" != "yes" ]; then \
./makeapi --validate; \
fi
server: version-update
python setup.py build
server-install: server
if [ "$(DESTDIR)" = "" ]; then \
python setup.py install; \
else \
python setup.py install --root $(DESTDIR); \
fi
archive:
-mkdir -p dist
git archive --format=tar --prefix=ipa/ $(TARGET) | (cd dist && tar xf -)
local-archive:
-mkdir -p dist/$(TARBALL_PREFIX)
rsync -a --exclude=dist --exclude=.git --exclude=build --exclude=rpmbuild . dist/$(TARBALL_PREFIX)
archive-cleanup:
rm -fr dist/freeipa
tarballs: local-archive
-mkdir -p dist/sources
# tar up clean sources
cd dist/$(TARBALL_PREFIX)/ipa-client; ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); make distclean
cd dist/$(TARBALL_PREFIX)/daemons; ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); make distclean
cd dist/$(TARBALL_PREFIX)/install; ../autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); make distclean
cd dist; tar cfz sources/$(TARBALL) $(TARBALL_PREFIX)
rm -rf dist/$(TARBALL_PREFIX)
rpmroot:
mkdir -p $(RPMBUILD)/BUILD
mkdir -p $(RPMBUILD)/RPMS
mkdir -p $(RPMBUILD)/SOURCES
mkdir -p $(RPMBUILD)/SPECS
mkdir -p $(RPMBUILD)/SRPMS
rpmdistdir:
mkdir -p dist/rpms
mkdir -p dist/srpms
rpms: rpmroot rpmdistdir version-update lint tarballs
cp dist/sources/$(TARBALL) $(RPMBUILD)/SOURCES/.
rpmbuild --define "_topdir $(RPMBUILD)" -ba freeipa.spec
cp rpmbuild/RPMS/*/$(PRJ_PREFIX)-*-$(IPA_VERSION)-*.rpm dist/rpms/
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-$(IPA_VERSION)-*.src.rpm dist/srpms/
rm -rf rpmbuild
client-rpms: rpmroot rpmdistdir version-update lint tarballs
cp dist/sources/$(TARBALL) $(RPMBUILD)/SOURCES/.
rpmbuild --define "_topdir $(RPMBUILD)" --define "ONLY_CLIENT 1" -ba freeipa.spec
cp rpmbuild/RPMS/*/$(PRJ_PREFIX)-*-$(IPA_VERSION)-*.rpm dist/rpms/
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-$(IPA_VERSION)-*.src.rpm dist/srpms/
rm -rf rpmbuild
srpms: rpmroot rpmdistdir version-update lint tarballs
cp dist/sources/$(TARBALL) $(RPMBUILD)/SOURCES/.
rpmbuild --define "_topdir $(RPMBUILD)" -bs freeipa.spec
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-$(IPA_VERSION)-*.src.rpm dist/srpms/
rm -rf rpmbuild
repodata:
-createrepo -p dist
dist: version-update archive tarballs archive-cleanup rpms repodata
local-dist: bootstrap-autogen clean local-archive tarballs archive-cleanup rpms
clean: version-update
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
rm -f *~
distclean: version-update
touch daemons/NEWS daemons/README daemons/AUTHORS daemons/ChangeLog
touch install/NEWS install/README install/AUTHORS install/ChangeLog
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
rm -fr rpmbuild dist build
rm -f daemons/NEWS daemons/README daemons/AUTHORS daemons/ChangeLog
rm -f install/NEWS install/README install/AUTHORS install/ChangeLog
maintainer-clean: clean
rm -fr rpmbuild dist build
cd selinux && $(MAKE) maintainer-clean
cd daemons && $(MAKE) maintainer-clean
cd install && $(MAKE) maintainer-clean
cd ipa-client && $(MAKE) maintainer-clean
cd ipapython && $(MAKE) maintainer-clean
rm -f version.m4
rm -f freeipa.spec

66
README Normal file
View File

@ -0,0 +1,66 @@
IPA Server
What is it?
-----------
For efficiency, compliance and risk mitigation, organizations need to
centrally manage and correlate vital security information including:
* Identity (machine, user, virtual machines, groups, authentication
credentials)
* Policy (configuration settings, access control information)
* Audit (events, logs, analysis thereof)
Since these are not new problems. there exist many approaches and
products focused on addressing them. However, these tend to have the
following weaknesses:
* Focus on solving identity management across the enterprise has meant
less focus on policy and audit.
* Vendor focus on Web identity management problems has meant less well
developed solutions for central management of the Linux and Unix
world's vital security info. Organizations are forced to maintain
a hodgepodge of internal and proprietary solutions at high TCO.
* Proprietary security products don't easily provide access to the
vital security information they collect or manage. This makes it
difficult to synchronize and analyze effectively.
The Latest Version
------------------
Details of the latest version can be found on the IPA server project
page under <http://www.freeipa.org/>.
Documentation
-------------
The most up-to-date documentation can be found at
<http://freeipa.org/page/Documentation/>.
Quick Start
-----------
To get started quickly, start here:
<https://fedorahosted.org/freeipa/wiki/QuickStartGuide>
Licensing
---------
Please see the file called COPYING.
Contacts
--------
* If you want to be informed about new code releases, bug fixes,
security fixes, general news and information about the IPA server
subscribe to the freeipa-announce mailing list at
<https://www.redhat.com/mailman/listinfo/freeipa-interest/>.
* If you have a bug report please submit it at:
<https://bugzilla.redhat.com>
* If you want to participate in actively developing IPA please
subscribe to the freeipa-devel mailing list at
<https://www.redhat.com/mailman/listinfo/freeipa-devel/> or join
us in IRC at irc://irc.freenode.net/freeipa

93
TODO Normal file
View File

@ -0,0 +1,93 @@
General ipalib/ipaserver improvements
-------------------------------------
* Port any commands still using old crud base classes to new crud base
classes, and then remove old crud base classes.
* Add a Command.backend convenience attribute that checks if the class
uses_backend attribute is sets the Command.backend attribute like this:
self.backend = self.Backend[self.uses_backend]
* Possibly generalize current Plugin.call() method (makes subprocess calls).
Probably should renamed this so it's not confused with Command.execute()...
maybe Plugin.subprocess_call()?.
* Add special logging methods to Plugin baseclass for authorization events
(escalation, de-escalation, and denial)... need to talk to John about this.
* Implement remaining missing features for full gettext service.
* Add ability to register pre-op, post-op plugins per command.
* Change Command so it filters args/options according to the Param.limit_to
kwarg (used to restrict certain params only to client or only to server).
* Add ability to have a post-processing step that only gets called
client-side. It should have a signature like output_for_cli() minus the
textui argument. Need to decide whether we allow this method to modify
the return value. (Use case still isn't very defined.)
* Improve CLI help to take advantange of the fact that command docstrings are
now split into summary and details.
* Remove remaining __getattr__() use in ipalib.plugable.
CRUD base classes
-----------------
* The Retrieve method should add in the common Flag('all') option for
retrieving all attributes.
* We probably need some LDAP centric crud method base classes, like
LDAPCreate, etc. Or other options it to have an LDAPObject base class and
have the crud Method plugins rely more on their corresponding Object plugin.
Existing plugins
----------------
* Many existing plugins that are doing crud-type operations aren't using the
Object + Method way of defining their parameters, and are therefore defining
the exact same parameter several times in a module. This should be fixed
one way or another... if there are deficiencies in the crud base classes,
they need to be improved.
Command Line interface
----------------------
* Further enhance textui plugin
* Make possible Enum values self-documenting... this might require writing our
own replacement for optparse. The isn't way to make optparse deal with the
global options the way Jason would like, so that's another motivation.
* All "comma-separated list of..." parameters should really be changed to
multivalue and have a flag that tells the CLI whether a multivalue should
be parsed as comma-separated. The List type currently satisfy this, but it
would be nice to have a comma-separated multivalue of any type.
* Add a File param type so an argument may be read from a file. This is
needed for cert-request to pass along the CSR.
* Replace RequiresRoot() with more fine-grained control based on the
files that need to be read/written
Packaging
---------
* Use setuptools instead of plain distutils
* Make setup.py generate dev-docs and run unit tests
* Package for deb/apt (debian/ dir)
Migration
---------
* Add the IPAService objectclass to existing principals
* Move existng host/ principals from cn=services to cn=computers?

82
VERSION Normal file
View File

@ -0,0 +1,82 @@
########################################################
# freeIPA Version #
# #
# freeIPA versions are as follows #
# 1.0.x New production series #
# 1.0.x{pre,rc}y Preview/Testing & RC #
# 1.0.0GITabcdefg Build from GIT #
# #
########################################################
########################################################
# This are the main version numbers #
# #
# <MAJOR>.<MINOR>.<RELEASE> #
# #
# e.g. IPA_VERSION_MAJOR=1 #
# IPA_VERSION_MINOR=0 #
# IPA_VERSION_RELEASE=0 #
# -> "1.0.0" #
########################################################
IPA_VERSION_MAJOR=2
IPA_VERSION_MINOR=1
IPA_VERSION_RELEASE=3
########################################################
# For 'pre' releases the version will be #
# #
# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> #
# #
# e.g. IPA_VERSION_PRE_RELEASE=1 #
# -> "1.0.0pre1" #
########################################################
IPA_VERSION_PRE_RELEASE=
########################################################
# For 'rc' releases the version will be #
# #
# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> #
# #
# e.g. IPA_VERSION_RC_RELEASE=1 #
# -> "1.0.0rc1" #
########################################################
IPA_VERSION_RC_RELEASE=
########################################################
# To mark GIT snapshots this should be set to 'yes' #
# in the development BRANCH, and set to 'no' only in #
# the IPA_X_X_RELEASE BRANCH #
# #
# <MAJOR>.<MINOR>.<RELEASE>GITxxx #
# #
# e.g. IPA_VERSION_IS_SVN_SNAPSHOT=yes #
# -> "1.0.0GITabcdefg" #
########################################################
IPA_VERSION_IS_GIT_SNAPSHOT="yes"
########################################################
# The version of IPA data. This is used to identify #
# incompatibilities in data that could cause issues #
# with replication. If the built-in versions don't #
# match exactly then replication will fail. #
# #
# The format is %Y%m%d%H%M%S #
# #
# e.g. IPA_DATA_VERSION=`date +%Y%m%d%H%M%S` #
# -> "20100614120000" #
########################################################
IPA_DATA_VERSION=20100614120000
########################################################
# The version of the IPA API. This controls which #
# client versions can use the XML-RPC and json APIs #
# #
# A change to existing API requires a MAJOR version #
# update. The addition of new API bumps the MINOR #
# version. #
# #
# The format is a whole number #
# #
########################################################
IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=13

3
autogen.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -i -f
./configure ${1+"$@"}

3
checks/README Normal file
View File

@ -0,0 +1,3 @@
This directory is for integration tests that require a live backend (LDAP,
Certificate Server, etc.). It's named "checks" so nose wont discover tests
here.

131
checks/check-ra.py Executable file
View File

@ -0,0 +1,131 @@
#!/usr/bin/python
# Authors:
# Jason Gerard DeRose <jderose@redhat.com>
# John Dennis <jdennis@redhat.com>
#
# Copyright (C) 2009 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
This tests the api.Backend.ra plugin against a test CA server awnuk has runnig.
It's only accessible from inside the Red Hat firewall. Obviously this needs
work so the community can also run this test, but it's a start.
Also, awnuk had to help me register the IPA instance I'm running with his
server. I don't exactly remember the steps, so ping him for help.
--jderose 2009-02-13
"""
from os import path
import sys
parent = path.dirname(path.dirname(path.abspath(__file__)))
sys.path.insert(0, parent)
verbose = True
from base64 import b64encode, b64decode
from ipalib import api
subject = u'CN=vm-070.idm.lab.bos.redhat.com'
csr = '\
MIIBZzCB0QIBADAoMSYwJAYDVQQDEx12bS0wNzAuaWRtLmxhYi5ib3MucmVkaGF0\n\
LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAriTSlAG+/xkvtxliWMeO\n\
Qu+vFQTz+/fgy7xWIg6WR2At6j/9eJ7LUYhqguqevOAQpuePxY4/FEfpmQ6PTgs/\n\
LXKa0vhIkXzkmMjKynUIWHYeaZekcXxye1dV/PdNB6H801xs60YjbScOJj3Hexvm\n\
hOKsdmwO1ukqTTEKDXrr3c8CAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAG4pTLrE\n\
cvrkQXVdMOjgAVJ6KZYl/caIOYhIlcJ3jhf95Bv/Zs3lpfHjXnM8jj4EWfyd0lZx\n\
2EUytXXubKJUpjUCeBp4oaQ2Ahvdxo++oUcbXkKxtCOUB6Mw8XEIVYaldZlcHDHM\n\
dysLdrZ3K9HOzoeSq2e0m+trQaWnBQG47O7F\n\
'
reference_decode = {
'certificate' : b64decode
}
trial_decode = {
'certificate' : b64decode
}
api.bootstrap(
in_server=True,
enable_ra=True,
ra_plugin='dogtag',
ca_host='vm-070.idm.lab.bos.redhat.com',
debug=True,
in_tree=True,
)
api.finalize()
ra = api.Backend.ra
def assert_equal(trial, reference):
keys = reference.keys()
keys.sort()
for key in keys:
reference_val = reference[key]
trial_val = trial[key]
if reference_decode.has_key(key):
reference_val = reference_decode[key](reference_val)
if trial_decode.has_key(key):
trial_val = trial_decode[key](trial_val)
assert reference_val == trial_val, \
'%s: not equal\n\nreference_val:\n%r\ntrial_val:\n%r' % \
(key, reference[key], trial[key])
api.log.info('******** Testing ra.request_certificate() ********')
request_result = ra.request_certificate(csr)
if verbose: print "request_result=\n%s" % request_result
assert_equal(request_result,
{'subject' : subject,
})
api.log.info('******** Testing ra.check_request_status() ********')
status_result = ra.check_request_status(request_result['request_id'])
if verbose: print "status_result=\n%s" % status_result
assert_equal(status_result,
{'serial_number' : request_result['serial_number'],
'request_id' : request_result['request_id'],
'cert_request_status' : u'complete',
})
api.log.info('******** Testing ra.get_certificate() ********')
get_result = ra.get_certificate(request_result['serial_number'])
if verbose: print "get_result=\n%s" % get_result
assert_equal(get_result,
{'serial_number' : request_result['serial_number'],
'certificate' : request_result['certificate'],
})
api.log.info('******** Testing ra.revoke_certificate() ********')
revoke_result = ra.revoke_certificate(request_result['serial_number'],
revocation_reason=6) # Put on hold
if verbose: print "revoke_result=\n%s" % revoke_result
assert_equal(revoke_result,
{'revoked' : True
})
api.log.info('******** Testing ra.take_certificate_off_hold() ********')
unrevoke_result = ra.take_certificate_off_hold(request_result['serial_number'])
if verbose: print "unrevoke_result=\n%s" % unrevoke_result
assert_equal(unrevoke_result,
{'unrevoked' : True
})

13
contrib/RHEL4/Makefile.am Normal file
View File

@ -0,0 +1,13 @@
NULL =
sbin_SCRIPTS = \
ipa-client-setup \
$(NULL)
EXTRA_DIST = \
$(sbin_SCRIPTS) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,55 @@
AC_PREREQ(2.59)
AC_INIT([ipa-client],
[0.99.0],
[http://www.freeipa.org/])
AM_INIT_AUTOMAKE([foreign])
AC_SUBST(VERSION)
dnl ---------------------------------------------------------------------------
dnl - Check for Python
dnl ---------------------------------------------------------------------------
AC_MSG_NOTICE([Checking for Python])
have_python=no
AM_PATH_PYTHON([2.3])
if test "x$PYTHON" = "x" ; then
AC_MSG_ERROR([Python not found])
fi
dnl ---------------------------------------------------------------------------
dnl - Set the data install directory since we don't use pkgdatadir
dnl ---------------------------------------------------------------------------
IPA_DATA_DIR="$datadir/ipa"
AC_SUBST(IPA_DATA_DIR)
dnl ---------------------------------------------------------------------------
dnl Finish
dnl ---------------------------------------------------------------------------
# Files
AC_CONFIG_FILES([
Makefile
])
AC_OUTPUT
echo "
IPA client $VERSION
========================
prefix: ${prefix}
exec_prefix: ${exec_prefix}
libdir: ${libdir}
bindir: ${bindir}
sbindir: ${sbindir}
sysconfdir: ${sysconfdir}
localstatedir: ${localstatedir}
datadir: ${datadir}
source code location: ${srcdir}
Maintainer mode: ${USE_MAINTAINER_MODE}
"

View File

@ -0,0 +1,368 @@
#! /usr/bin/python -E
# Authors: Simo Sorce <ssorce@redhat.com>
# Karl MacMillan <kmacmillan@mentalrootkit.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
VERSION = "%prog .1"
import sys
import os
import string
import shutil
import socket
import logging
from optparse import OptionParser
import ipachangeconf
import ldap
from ldap import LDAPError
class ipaserver:
def __init__(self, server):
self.server = server
self.realm = None
self.domain = None
self.basedn = None
def getServerName(self):
return str(self.server)
def getDomainName(self):
return str(self.domain)
def getRealmName(self):
return str(self.realm)
def getBaseDN(self):
return str(self.basedn)
def check(self):
lret = []
lres = []
lattr = ""
linfo = ""
lrealms = []
i = 0
#now verify the server is really an IPA server
try:
logging.debug("Init ldap with: ldap://"+self.server+":389")
lh = ldap.initialize("ldap://"+self.server+":389")
lh.simple_bind_s("","")
logging.debug("Search rootdse")
lret = lh.search_s("", ldap.SCOPE_BASE, "(objectClass=*)")
for lattr in lret[0][1]:
if lattr.lower() == "namingcontexts":
self.basedn = lret[0][1][lattr][0]
logging.debug("Search for (info=*) in "+self.basedn+"(base)")
lret = lh.search_s(self.basedn, ldap.SCOPE_BASE, "(info=IPA*)")
if not lret:
return False
logging.debug("Found: "+str(lret))
for lattr in lret[0][1]:
if lattr.lower() == "info":
linfo = lret[0][1][lattr][0].lower()
break
if not linfo:
return False
#search and return known realms
logging.debug("Search for (objectClass=krbRealmContainer) in "+self.basedn+"(sub)")
lret = lh.search_s("cn=kerberos,"+self.basedn, ldap.SCOPE_SUBTREE, "(objectClass=krbRealmContainer)")
if not lret:
#something very wrong
return False
logging.debug("Found: "+str(lret))
for lres in lret:
for lattr in lres[1]:
if lattr.lower() == "cn":
lrealms.append(lres[1][lattr][0])
if len(lrealms) != 1:
#which one? we can't attach to a multi-realm server without DNS working
return False
else:
self.realm = lrealms[0]
self.domain = lrealms[0].lower()
return True
except LDAPError, err:
#no good
logging.error("Ldap Error: "+str(err))
return False
ntp_conf = """# Permit time synchronization with our time source, but do not
# permit the source to query or modify the service on this system.
restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery
# Permit all access over the loopback interface. This could
# be tightened as well, but to do so would effect some of
# the administrative functions.
restrict 127.0.0.1
restrict -6 ::1
# Hosts on local network are less restricted.
#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server $SERVER
#broadcast 192.168.1.255 key 42 # broadcast server
#broadcastclient # broadcast client
#broadcast 224.0.1.1 key 42 # multicast server
#multicastclient 224.0.1.1 # multicast client
#manycastserver 239.255.254.254 # manycast server
#manycastclient 239.255.254.254 key 42 # manycast client
# Undisciplined Local Clock. This is a fake driver intended for backup
# and when no outside source of synchronized time is available.
server 127.127.1.0 # local clock
#fudge 127.127.1.0 stratum 10
# Drift file. Put this in a directory which the daemon can write to.
# No symbolic links allowed, either, since the daemon updates the file
# by creating a temporary in the same directory and then rename()'ing
# it to the file.
driftfile /var/lib/ntp/drift
# Key file containing the keys and key identifiers used when operating
# with symmetric key cryptography.
keys /etc/ntp/keys
# Specify the key identifiers which are trusted.
#trustedkey 4 8 42
# Specify the key identifier to use with the ntpdc utility.
#requestkey 8
# Specify the key identifier to use with the ntpq utility.
#controlkey 8
"""
ntp_sysconfig = """# Drop root to id 'ntp:ntp' by default.
OPTIONS="-x -u ntp:ntp -p /var/run/ntpd.pid"
# Set to 'yes' to sync hw clock after successful ntpdate
SYNC_HWCLOCK=yes
# Additional options for ntpdate
NTPDATE_OPTIONS=""
"""
def config_ntp(server_fqdn):
nc = string.replace(ntp_conf, "$SERVER", server_fqdn)
shutil.copy("/etc/ntp.conf", "/etc/ntp.conf.ipasave")
fd = open("/etc/ntp.conf", "w")
fd.write(nc)
fd.close()
shutil.copy("/etc/sysconfig/ntpd", "/etc/sysconfig/ntpd.ipasave")
fd = open("/etc/sysconfig/ntpd", "w")
fd.write(ntp_sysconfig)
fd.close()
# Set the ntpd to start on boot
os.system("/sbin/chkconfig ntpd on")
# Restart ntpd
os.system("/sbin/service ntpd restart")
def parse_options():
parser = OptionParser(version=VERSION)
parser.add_option("--server", dest="server", help="IPA server")
parser.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="print debugging information")
parser.add_option("-U", "--unattended", dest="unattended",
action="store_true",
help="unattended installation never prompts the user")
parser.add_option("-N", "--no-ntp", action="store_false",
help="do not configure ntp", default=True, dest="conf_ntp")
options, args = parser.parse_args()
if not options.server:
parser.error("must provide an IPA server name with --server")
return options
def ask_for_confirmation(message):
yesno = raw_input(message + " [y/N]: ")
if not yesno or yesno.lower()[0] != "y":
return False
print "\n"
return True
def logging_setup(options):
# Always log everything (i.e., DEBUG) to the log
# file.
logger = logging.getLogger('ipa-client-setup')
fh = logging.FileHandler('ipaclient-install.log')
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
# If the debug option is set, also log debug messages to the console
if options.debug:
logger.setLevel(logging.DEBUG)
else:
# Otherwise, log critical and error messages
logger.setLevel(logging.ERROR)
return logger
def main():
options = parse_options()
logger = logging_setup(options)
dnsok = True
ipasrv = ipaserver(options.server)
ret = ipasrv.check()
if ret == False:
print "Failed to verify that ["+options.server+"] is an IPA Server, aborting!"
return -1
print "IPA Server verified."
print "Realm: "+ipasrv.getRealmName()
print "DNS Domain: "+ipasrv.getDomainName()
print "IPA Server: "+ipasrv.getServerName()
print "BaseDN: "+ipasrv.getBaseDN()
print "\n"
if not options.unattended and not ask_for_confirmation("Continue to configure the system with these values?"):
return 1
# Configure ipa.conf
ipaconf = ipachangeconf.IPAChangeConf("IPA Installer")
ipaconf.setOptionAssignment(" = ")
ipaconf.setSectionNameDelimiters(("[","]"))
opts = [{'name':'comment', 'type':'comment', 'value':'File modified by ipa-client-install'},
{'name':'empty', 'type':'empty'}]
#[global]
defopts = [{'name':'xmlrpc_uri', 'type':'option', 'value':'https://%s/ipa/xml' % ipasrv.getServerName()},
{'name':'realm', 'type':'option', 'value':ipasrv.getRealmName()}]
opts.append({'name':'global', 'type':'section', 'value':defopts})
opts.append({'name':'empty', 'type':'empty'})
ipaconf.newConf("/etc/ipa/default.conf", opts)
# Configure ldap.conf
ldapconf = ipachangeconf.IPAChangeConf("IPA Installer")
ldapconf.setOptionAssignment(" ")
opts = [{'name':'comment', 'type':'comment', 'value':'File modified by ipa-client-install'},
{'name':'empty', 'type':'empty'},
{'name':'ldap_version', 'type':'option', 'value':'3'},
{'name':'base', 'type':'option', 'value':ipasrv.getBaseDN()},
{'name':'empty', 'type':'empty'},
{'name':'nss_base_passwd', 'type':'option', 'value':'cn=users,cn=accounts,'+ipasrv.getBaseDN()+'?sub'},
{'name':'nss_base_group', 'type':'option', 'value':'cn=users,cn=accounts,'+ipasrv.getBaseDN()+'?sub'},
{'name':'nss_schema', 'type':'option', 'value':'rfc2307bis'},
{'name':'nss_map_attribute', 'type':'option', 'value':'uniqueMember member'},
{'name':'nss_initgroups_ignoreusers', 'type':'option', 'value':'root,dirsrv'},
{'name':'empty', 'type':'empty'},
{'name':'nss_reconnect_maxsleeptime', 'type':'option', 'value':'8'},
{'name':'nss_reconnect_sleeptime', 'type':'option', 'value':'1'},
{'name':'bind_timelimit', 'type':'option', 'value':'5'},
{'name':'timelimit', 'type':'option', 'value':'15'},
{'name':'empty', 'type':'empty'},
{'name':'uri', 'type':'option', 'value':'ldap://'+ipasrv.getServerName()},
{'name':'empty', 'type':'empty'}]
try:
ldapconf.newConf("/etc/ldap.conf", opts)
except Exception, e:
print "Configuration failed: " + str(e)
return 1
if not "" == ipasrv.getRealmName():
#Configure krb5.conf
krbconf = ipachangeconf.IPAChangeConf("IPA Installer")
krbconf.setOptionAssignment(" = ")
krbconf.setSectionNameDelimiters(("[","]"))
krbconf.setSubSectionDelimiters(("{","}"))
krbconf.setIndent((""," "," "))
opts = [{'name':'comment', 'type':'comment', 'value':'File modified by ipa-client-install'},
{'name':'empty', 'type':'empty'}]
#[libdefaults]
libopts = [{'name':'default_realm', 'type':'option', 'value':ipasrv.getRealmName()}]
libopts.append({'name':'dns_lookup_realm', 'type':'option', 'value':'false'})
libopts.append({'name':'dns_lookup_kdc', 'type':'option', 'value':'false'})
libopts.append({'name':'ticket_lifetime', 'type':'option', 'value':'24h'})
libopts.append({'name':'forwardable', 'type':'option', 'value':'yes'})
opts.append({'name':'libdefaults', 'type':'section', 'value':libopts})
opts.append({'name':'empty', 'type':'empty'})
#[realms]
kropts =[{'name':'kdc', 'type':'option', 'value':ipasrv.getServerName()+':88'},
{'name':'admin_server', 'type':'option', 'value':ipasrv.getServerName()+':749'},
{'name':'default_domain', 'type':'option', 'value':ipasrv.getDomainName()}]
ropts = [{'name':ipasrv.getRealmName(), 'type':'subsection', 'value':kropts}]
opts.append({'name':'realms', 'type':'section', 'value':ropts})
opts.append({'name':'empty', 'type':'empty'})
#[domain_realm]
dropts = [{'name':'.'+ipasrv.getDomainName(), 'type':'option', 'value':ipasrv.getRealmName()},
{'name':ipasrv.getDomainName(), 'type':'option', 'value':ipasrv.getRealmName()}]
opts.append({'name':'domain_realm', 'type':'section', 'value':dropts})
opts.append({'name':'empty', 'type':'empty'})
#[appdefaults]
pamopts = [{'name':'debug', 'type':'option', 'value':'false'},
{'name':'ticket_lifetime', 'type':'option', 'value':'36000'},
{'name':'renew_lifetime', 'type':'option', 'value':'36000'},
{'name':'forwardable', 'type':'option', 'value':'true'},
{'name':'krb4_convert', 'type':'option', 'value':'false'}]
appopts = [{'name':'pam', 'type':'subsection', 'value':pamopts}]
opts.append({'name':'appdefaults', 'type':'section', 'value':appopts})
krbconf.newConf("/etc/krb5.conf", opts);
#Modify nsswitch to add nss_ldap
os.system("/usr/sbin/authconfig --enableldap --kickstart")
#Modify pam to add pam_krb5
os.system("/usr/sbin/authconfig --enablekrb5 --kickstart")
if options.conf_ntp:
config_ntp(ipasrv.getServerName())
print "Client configuration complete."
return 0
sys.exit(main())

View File

@ -0,0 +1,54 @@
Name: ipa-client
Version: 1.0.0
Release: 1%{?dist}
Summary: IPA client Setup script for RHEL-4
Group: System Environment/Base
License: GPLv2
URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
#BuildRequires: python-devel
Requires: python
Requires: python-ldap
%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
%description
IPA is a server for identity, policy, and audit.
The client package provide install and configuration scripts for RHEL-4 clients.
%prep
%setup -q
%configure --prefix=/usr
%build
make
%install
rm -rf %{buildroot}
%{__python} setup.py install --no-compile --root=%{buildroot}
%makeinstall \
SBINDIR=$RPM_BUILD_ROOT%{_sbindir}
mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/ipa
install -m644 ipa.conf $RPM_BUILD_ROOT%{_sysconfdir}/ipa/ipa.conf
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa-client-setup
%{python_sitelib}/ipachangeconf.py*
%config(noreplace) %{_sysconfdir}/ipa/ipa.conf
%changelog
* Thu Apr 3 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-1
- Version bump for release
* Mon Mar 25 2008 Simo Sorce <ssorce@redhat.com> - 0.99.0-1
- First RHEL-4 release

3
contrib/RHEL4/ipa.conf Normal file
View File

@ -0,0 +1,3 @@
[defaults]
# realm = EXAMPLE.COM
# server = ipa.example.com

View File

@ -0,0 +1,458 @@
#
# ipachangeconf - configuration file manipulation classes and functions
# partially based on authconfig code
# Copyright (c) 1999-2007 Red Hat, Inc.
# Author: Simo Sorce <ssorce@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import fcntl
import os
import string
import time
import shutil
def openLocked(filename, perms):
fd = -1
try:
fd = os.open(filename, os.O_RDWR | os.O_CREAT, perms)
fcntl.lockf(fd, fcntl.LOCK_EX)
except OSError, (errno, strerr):
if fd != -1:
try:
os.close(fd)
except OSError:
pass
raise IOError(errno, strerr)
return os.fdopen(fd, "r+")
#TODO: add subsection as a concept
# (ex. REALM.NAME = { foo = x bar = y } )
#TODO: put section delimiters as separating element of the list
# so that we can process multiple sections in one go
#TODO: add a comment all but provided options as a section option
class IPAChangeConf:
def __init__(self, name):
self.progname = name
self.indent = ("","","")
self.assign = (" = ","=")
self.dassign = self.assign[0]
self.comment = ("#",)
self.dcomment = self.comment[0]
self.eol = ("\n",)
self.deol = self.eol[0]
self.sectnamdel = ("[","]")
self.subsectdel = ("{","}")
def setProgName(self, name):
self.progname = name
def setIndent(self, indent):
if type(indent) is tuple:
self.indent = indent
elif type(indent) is str:
self.indent = (indent, )
else:
raise ValueError, 'Indent must be a list of strings'
def setOptionAssignment(self, assign):
if type(assign) is tuple:
self.assign = assign
else:
self.assign = (assign, )
self.dassign = self.assign[0]
def setCommentPrefix(self, comment):
if type(comment) is tuple:
self.comment = comment
else:
self.comment = (comment, )
self.dcomment = self.comment[0]
def setEndLine(self, eol):
if type(eol) is tuple:
self.eol = eol
else:
self.eol = (eol, )
self.deol = self.eol[0]
def setSectionNameDelimiters(self, delims):
self.sectnamdel = delims
def setSubSectionDelimiters(self, delims):
self.subsectdel = delims
def matchComment(self, line):
for v in self.comment:
if line.lstrip().startswith(v):
return line.lstrip()[len(v):]
return False
def matchEmpty(self, line):
if line.strip() == "":
return True
return False
def matchSection(self, line):
cl = "".join(line.strip().split()).lower()
if len(self.sectnamdel) != 2:
return False
if not cl.startswith(self.sectnamdel[0]):
return False
if not cl.endswith(self.sectnamdel[1]):
return False
return cl[len(self.sectnamdel[0]):-len(self.sectnamdel[1])]
def matchSubSection(self, line):
if self.matchComment(line):
return False
parts = line.split(self.dassign, 1)
if len(parts) < 2:
return False
if parts[1].strip() == self.subsectdel[0]:
return parts[0].strip()
return False
def matchSubSectionEnd(self, line):
if self.matchComment(line):
return False
if line.strip() == self.subsectdel[1]:
return True
return False
def getSectionLine(self, section):
if len(self.sectnamdel) != 2:
return section
return self.sectnamdel[0]+section+self.sectnamdel[1]+self.deol
def dump(self, options, level=0):
output = ""
if level >= len(self.indent):
level = len(self.indent)-1
for o in options:
if o['type'] == "section":
output += self.sectnamdel[0]+o['name']+self.sectnamdel[1]+self.deol
output += self.dump(o['value'], level+1)
continue
if o['type'] == "subsection":
output += self.indent[level]+o['name']+self.dassign+self.subsectdel[0]+self.deol
output += self.dump(o['value'], level+1)
output += self.indent[level]+self.subsectdel[1]+self.deol
continue
if o['type'] == "option":
output += self.indent[level]+o['name']+self.dassign+o['value']+self.deol
continue
if o['type'] == "comment":
output += self.dcomment+o['value']+self.deol
continue
if o['type'] == "empty":
output += self.deol
continue
raise SyntaxError, 'Unknown type: ['+o['type']+']'
return output
def parseLine(self, line):
if self.matchEmpty(line):
return {'name':'empty', 'type':'empty'}
value = self.matchComment(line)
if value:
return {'name':'comment', 'type':'comment', 'value':value.rstrip()} #pylint: disable=E1103
parts = line.split(self.dassign, 1)
if len(parts) < 2:
raise SyntaxError, 'Syntax Error: Unknown line format'
return {'name':parts[0].strip(), 'type':'option', 'value':parts[1].rstrip()}
def findOpts(self, opts, type, name, exclude_sections=False):
num = 0
for o in opts:
if o['type'] == type and o['name'] == name:
return (num, o)
if exclude_sections and (o['type'] == "section" or o['type'] == "subsection"):
return (num, None)
num += 1
return (num, None)
def commentOpts(self, inopts, level = 0):
opts = []
if level >= len(self.indent):
level = len(self.indent)-1
for o in inopts:
if o['type'] == 'section':
no = self.commentOpts(o['value'], level+1)
val = self.dcomment+self.sectnamdel[0]+o['name']+self.sectnamdel[1]
opts.append({'name':'comment', 'type':'comment', 'value':val})
for n in no:
opts.append(n)
continue
if o['type'] == 'subsection':
no = self.commentOpts(o['value'], level+1)
val = self.indent[level]+o['name']+self.dassign+self.subsectdel[0]
opts.append({'name':'comment', 'type':'comment', 'value':val})
for n in no:
opts.append(n)
val = self.indent[level]+self.subsectdel[1]
opts.append({'name':'comment', 'type':'comment', 'value':val})
continue
if o['type'] == 'option':
val = self.indent[level]+o['name']+self.dassign+o['value']
opts.append({'name':'comment', 'type':'comment', 'value':val})
continue
if o['type'] == 'comment':
opts.append(o)
continue
if o['type'] == 'empty':
opts.append({'name':'comment', 'type':'comment', 'value':''})
continue
raise SyntaxError, 'Unknown type: ['+o['type']+']'
return opts
def mergeOld(self, oldopts, newopts):
opts = []
for o in oldopts:
if o['type'] == "section" or o['type'] == "subsection":
(num, no) = self.findOpts(newopts, o['type'], o['name'])
if not no:
opts.append(o)
continue
if no['action'] == "set":
mo = self.mergeOld(o['value'], no['value'])
opts.append({'name':o['name'], 'type':o['type'], 'value':mo})
continue
if no['action'] == "comment":
co = self.commentOpts(o['value'])
for c in co:
opts.append(c)
continue
if no['action'] == "remove":
continue
raise SyntaxError, 'Unknown action: ['+no['action']+']'
if o['type'] == "comment" or o['type'] == "empty":
opts.append(o)
continue
if o['type'] == "option":
(num, no) = self.findOpts(newopts, 'option', o['name'], True)
if not no:
opts.append(o)
continue
if no['action'] == 'comment' or no['action'] == 'remove':
if no['value'] != None and o['value'] != no['value']:
opts.append(o)
continue
if no['action'] == 'comment':
opts.append({'name':'comment', 'type':'comment',
'value':self.dcomment+o['name']+self.dassign+o['value']})
continue
if no['action'] == 'set':
opts.append(no)
continue
raise SyntaxError, 'Unknown action: ['+o['action']+']'
raise SyntaxError, 'Unknown type: ['+o['type']+']'
return opts
def mergeNew(self, opts, newopts):
cline = 0
for no in newopts:
if no['type'] == "section" or no['type'] == "subsection":
(num, o) = self.findOpts(opts, no['type'], no['name'])
if not o:
if no['action'] == 'set':
opts.append(no)
continue
if no['action'] == "set":
self.mergeNew(o['value'], no['value'])
continue
cline = num+1
continue
if no['type'] == "option":
(num, o) = self.findOpts(opts, no['type'], no['name'], True)
if not o:
if no['action'] == 'set':
opts.append(no)
continue
cline = num+1
continue
if no['type'] == "comment" or no['type'] == "empty":
opts.insert(cline, no)
cline += 1
continue
raise SyntaxError, 'Unknown type: ['+no['type']+']'
def merge(self, oldopts, newopts):
#Use a two pass strategy
#First we create a new opts tree from oldopts removing/commenting
# the options as indicated by the contents of newopts
#Second we fill in the new opts tree with options as indicated
# in the newopts tree (this is becaus eentire (sub)sections may
# exist in the newopts that do not exist in oldopts)
opts = self.mergeOld(oldopts, newopts)
self.mergeNew(opts, newopts)
return opts
#TODO: Make parse() recursive?
def parse(self, f):
opts = []
sectopts = []
section = None
subsectopts = []
subsection = None
curopts = opts
fatheropts = opts
# Read in the old file.
for line in f:
# It's a section start.
value = self.matchSection(line)
if value:
if section is not None:
opts.append({'name':section, 'type':'section', 'value':sectopts})
sectopts = []
curopts = sectopts
fatheropts = sectopts
section = value
continue
# It's a subsection start.
value = self.matchSubSection(line)
if value:
if subsection is not None:
raise SyntaxError, 'nested subsections are not supported yet'
subsectopts = []
curopts = subsectopts
subsection = value
continue
value = self.matchSubSectionEnd(line)
if value:
if subsection is None:
raise SyntaxError, 'Unmatched end subsection terminator found'
fatheropts.append({'name':subsection, 'type':'subsection', 'value':subsectopts})
subsection = None
curopts = fatheropts
continue
# Copy anything else as is.
curopts.append(self.parseLine(line))
#Add last section if any
if len(sectopts) is not 0:
opts.append({'name':section, 'type':'section', 'value':sectopts})
return opts
# Write settings to configuration file
# file is a path
# options is a set of dictionaries in the form:
# [{'name': 'foo', 'value': 'bar', 'action': 'set/comment'}]
# section is a section name like 'global'
def changeConf(self, file, newopts):
autosection = False
savedsection = None
done = False
output = ""
f = None
try:
#Do not catch an unexisting file error, we want to fail in that case
shutil.copy2(file, file+".ipabkp")
f = openLocked(file, 0644)
oldopts = self.parse(f)
options = self.merge(oldopts, newopts)
output = self.dump(options)
# Write it out and close it.
f.seek(0)
f.truncate(0)
f.write(output)
finally:
try:
if f:
f.close()
except IOError:
pass
return True
# Write settings to new file, backup old
# file is a path
# options is a set of dictionaries in the form:
# [{'name': 'foo', 'value': 'bar', 'action': 'set/comment'}]
# section is a section name like 'global'
def newConf(self, file, options):
autosection = False
savedsection = None
done = False
output = ""
f = None
try:
try:
shutil.copy2(file, file+".ipabkp")
except IOError, err:
if err.errno == 2:
# The orign file did not exist
pass
f = openLocked(file, 0644)
# Trunkate
f.seek(0)
f.truncate(0)
output = self.dump(options)
f.write(output)
finally:
try:
if f:
f.close()
except IOError:
pass
return True

75
contrib/RHEL4/setup.py Normal file
View File

@ -0,0 +1,75 @@
#!/usr/bin/python
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""FreeIPA python support library
FreeIPA is a server for identity, policy, and audit.
"""
DOCLINES = __doc__.split("\n")
import os
import sys
import distutils.sysconfig
CLASSIFIERS = """\
Development Status :: 4 - Beta
Intended Audience :: System Environment/Base
License :: GPL
Programming Language :: Python
Operating System :: POSIX
Operating System :: Unix
"""
# BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
# update it when the contents of directories change.
if os.path.exists('MANIFEST'): os.remove('MANIFEST')
def setup_package():
from distutils.core import setup
old_path = os.getcwd()
local_path = os.path.dirname(os.path.abspath(sys.argv[0]))
os.chdir(local_path)
sys.path.insert(0,local_path)
try:
setup(
name = "ipa-client",
version = "0.99.0",
license = "GPL",
author = "Simo Sorce",
author_email = "ssorce@redhat.com",
maintainer = "freeIPA Developers",
maintainer_email = "freeipa-devel@redhat.com",
url = "http://www.freeipa.org/",
description = DOCLINES[0],
long_description = "\n".join(DOCLINES[2:]),
download_url = "http://www.freeipa.org/page/Downloads",
classifiers=filter(None, CLASSIFIERS.split('\n')),
platforms = ["Linux"],
py_modules=['ipachangeconf']
)
finally:
del sys.path[0]
os.chdir(old_path)
return
if __name__ == '__main__':
setup_package()

View File

@ -0,0 +1,32 @@
# -*- shell-script -*-
# Programmable completion for the IPA ipa command under bash. Source
# this file (or on some systems add it to ~/.bash_completion and start a new
# shell) and bash's completion mechanism will know all about ipa's options!
# Known to work with bash 2.05a with programmable completion and extended
# pattern matching enabled (use 'shopt -s extglob progcomp' to enable
# these if they are not already enabled).
# based on the bzr bash completion script by Martin Pool
_ipa_commands()
{
ipa help commands | sed -r 's/^([-[:alnum:]]*).*/\1/' | grep '^[[:alnum:]]'
}
_ipa()
{
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
if [ $COMP_CWORD -eq 1 ]; then
COMPREPLY=( $( compgen -W "$(_ipa_commands)" $cur ) )
elif [ $COMP_CWORD -eq 2 ]; then
case "$prev" in
help)
COMPREPLY=( $( compgen -W "$(_ipa_commands) commands" $cur ) )
;;
esac
fi
}
complete -F _ipa -o default ipa

43
daemons/Makefile.am Normal file
View File

@ -0,0 +1,43 @@
# This file will be processed with automake-1.7 to create Makefile.in
#
AUTOMAKE_OPTIONS = 1.7
NULL =
AM_CFLAGS = $(NULL)
if HAVE_GCC
AM_CFLAGS += -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith \
-Wcast-align -Werror-implicit-function-declaration \
$(NULL)
endif
export AM_CFLAGS
SUBDIRS = \
ipa-kpasswd \
ipa-slapi-plugins \
$(NULL)
DISTCLEANFILES = \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
intltool-*.in \
compile \
configure \
COPYING \
INSTALL \
install-sh \
missing \
mkinstalldirs \
config.guess \
ltmain.sh \
config.sub \
depcomp \
Makefile.in \
config.h.* \
aclocal.m4 \
version.m4 \
ipa-client.spec \
py-compile \
$(NULL)

323
daemons/configure.ac Normal file
View File

@ -0,0 +1,323 @@
AC_PREREQ(2.59)
m4_include(../version.m4)
AC_INIT([ipa-server],
IPA_VERSION,
[https://hosted.fedoraproject.org/projects/freeipa/newticket])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign])
AM_MAINTAINER_MODE
AC_PROG_CC
AC_STDC_HEADERS
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
AC_HEADER_STDC
AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes])
AC_SUBST(VERSION)
dnl ---------------------------------------------------------------------------
dnl - Check for NSPR
dnl ---------------------------------------------------------------------------
AC_CHECK_HEADER(nspr4/nspr.h)
AC_CHECK_HEADER(nspr/nspr.h)
if test "x$ac_cv_header_nspr4_nspr_h" = "xno" && test "x$ac_cv_header_nspr_nspr_h" = "xno" ; then
AC_MSG_ERROR([Required NSPR header not available (nspr-devel)])
fi
if test "x$ac_cv_header_nspr4_nspr_h" = "xyes" ; then
NSPR4="-I/usr/include/nspr4"
fi
if test "x$ac_cv_header_nspr_nspr_h" = "xyes" ; then
NSPR4="-I/usr/include/nspr"
fi
dnl ---------------------------------------------------------------------------
dnl - Check for NSS
dnl ---------------------------------------------------------------------------
SAVE_CPPFLAGS=$CPPFLAGS
CPPFLAGS=$NSPR4
AC_CHECK_HEADER(nss3/nss.h)
AC_CHECK_HEADER(nss/nss.h)
CPPFLAGS=$SAVE_CPPFLAGS
if test "x$ac_cv_header_nss3_nss_h" = "xno" && test "x$ac_cv_header_nss_nss_h" = "xno" ; then
AC_MSG_ERROR([Required NSS header not available (nss-devel)])
fi
if test "x$ac_cv_header_nss3_nss_h" = "xyes" ; then
NSS3="-I/usr/include/nss3"
fi
if test "x$ac_cv_header_nss_nss_h" = "xyes" ; then
NSS3="-I/usr/include/nss"
fi
dnl ---------------------------------------------------------------------------
dnl - Check for DS slapi plugin
dnl ---------------------------------------------------------------------------
# Need to hack CPPFLAGS to be able to correctly detetct slapi-plugin.h
SAVE_CPPFLAGS=$CPPFLAGS
CPPFLAGS=$NSPR4
AC_CHECK_HEADER(dirsrv/slapi-plugin.h)
if test "x$ac_cv_header_dirsrv_slapi-plugin_h" = "xno" ; then
AC_MSG_ERROR([Required 389-ds header not available (389-ds-base-devel)])
fi
AC_CHECK_HEADER(dirsrv/repl-session-plugin.h)
if test "x$ac_cv_header_dirsrv_repl_session_plugin_h" = "xno" ; then
AC_MSG_ERROR([Required 389-ds header not available (389-ds-base-devel)])
fi
CPPFLAGS=$SAVE_CPPFLAGS
if test "x$ac_cv_header_dirsrv_slapi_plugin_h" = "xno" ; then
AC_MSG_ERROR([Required DS slapi plugin header not available (fedora-ds-base-devel)])
fi
dnl ---------------------------------------------------------------------------
dnl - Check for KRB5
dnl ---------------------------------------------------------------------------
KRB5_LIBS=
AC_CHECK_HEADER(krb5.h, [], [AC_MSG_ERROR([krb5.h not found])])
krb5_impl=mit
if test "x$ac_cv_header_krb5_h" = "xyes" ; then
dnl lazy check for Heimdal Kerberos
AC_CHECK_HEADERS(heim_err.h)
if test $ac_cv_header_heim_err_h = yes ; then
krb5_impl=heimdal
else
krb5_impl=mit
fi
if test "x$krb5_impl" = "xmit"; then
AC_CHECK_LIB(k5crypto, main,
[krb5crypto=k5crypto],
[krb5crypto=crypto])
AC_CHECK_LIB(krb5, main,
[have_krb5=yes
KRB5_LIBS="-lkrb5 -l$krb5crypto -lcom_err"],
[have_krb5=no],
[-l$krb5crypto -lcom_err])
elif test "x$krb5_impl" = "xheimdal"; then
AC_CHECK_LIB(des, main,
[krb5crypto=des],
[krb5crypto=crypto])
AC_CHECK_LIB(krb5, main,
[have_krb5=yes
KRB5_LIBS="-lkrb5 -l$krb5crypto -lasn1 -lroken -lcom_err"],
[have_krb5=no],
[-l$krb5crypto -lasn1 -lroken -lcom_err])
AC_DEFINE(HAVE_HEIMDAL_KERBEROS, 1,
[define if you have HEIMDAL Kerberos])
else
have_krb5=no
AC_MSG_WARN([Unrecognized Kerberos5 Implementation])
fi
if test "x$have_krb5" = "xyes" ; then
ol_link_krb5=yes
AC_DEFINE(HAVE_KRB5, 1,
[define if you have Kerberos V])
else
AC_MSG_ERROR([Required Kerberos 5 support not available])
fi
fi
AC_SUBST(KRB5_LIBS)
dnl ---------------------------------------------------------------------------
dnl - Check for Mozilla LDAP and OpenLDAP SDK
dnl ---------------------------------------------------------------------------
SAVE_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$NSPR4 $NSS3"
AC_CHECK_HEADER(svrcore.h)
AC_CHECK_HEADER(svrcore/svrcore.h)
if test "x$ac_cv_header_svrcore_h" = "xno" && test "x$ac_cv_header_svrcore_svrcore_h" = "xno" ; then
AC_MSG_ERROR([Required svrcore header not available (svrcore-devel)])
fi
if test "x$ac_cv_header_svrcore_svrcore_h" = "yes" ; then
CPPFLAGS="$CPPFLAGS -I/usr/include/svrcore"
fi
AC_CHECK_LIB(ldap, ldap_search, with_ldap=yes)
dnl Check for other libraries we need to link with to get the main routines.
test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes], , -llber) }
test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes with_ldap_krb=yes], , -llber -lkrb) }
test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes with_ldap_krb=yes with_ldap_des=yes], , -llber -lkrb -ldes) }
dnl Recently, we need -lber even though the main routines are elsewhere,
dnl because otherwise be get link errors w.r.t. ber_pvt_opt_on. So just
dnl check for that (it's a variable not a fun but that doesn't seem to
dnl matter in these checks) and stick in -lber if so. Can't hurt (even to
dnl stick it in always shouldn't hurt, I don't think) ... #### Someone who
dnl #### understands LDAP needs to fix this properly.
test "$with_ldap_lber" != "yes" && { AC_CHECK_LIB(lber, ber_pvt_opt_on, with_ldap_lber=yes) }
if test "$with_ldap" = "yes"; then
if test "$with_ldap_des" = "yes" ; then
OPENLDAP_LIBS="${OPENLDAP_LIBS} -ldes"
fi
if test "$with_ldap_krb" = "yes" ; then
OPENLDAP_LIBS="${OPENLDAP_LIBS} -lkrb"
fi
if test "$with_ldap_lber" = "yes" ; then
OPENLDAP_LIBS="${OPENLDAP_LIBS} -llber"
fi
OPENLDAP_LIBS="${OPENLDAP_LIBS} -lldap"
else
AC_MSG_ERROR([OpenLDAP not found])
fi
AC_SUBST(OPENLDAP_LIBS)
OPENLDAP_CFLAGS="${OPENLDAP_CFLAGS} -DWITH_OPENLDAP"
AC_SUBST(OPENLDAP_CFLAGS)
AC_ARG_WITH([openldap],
[AS_HELP_STRING([--with-openldap],
[compile plugins with openldap instead of mozldap])],
[], [])
LDAP_CFLAGS="${OPENLDAP_CFLAGS} $NSPR4 $NSS3 -DUSE_OPENLDAP"
LDAP_LIBS="${OPENLDAP_LIBS}"
AC_DEFINE_UNQUOTED(WITH_OPENLDAP, 1, [Use OpenLDAP libraries])
AC_SUBST(LDAP_CFLAGS)
AC_SUBST(LDAP_LIBS)
dnl ---------------------------------------------------------------------------
dnl - Check for OpenSSL Crypto library
dnl ---------------------------------------------------------------------------
dnl This is a very simple check, we should probably check also for MD4_Init and
dnl probably also the version we are using is recent enough
SSL_LIBS=
AC_CHECK_HEADER(openssl/des.h, [], [AC_MSG_ERROR([openssl/des.h not found])])
AC_CHECK_LIB(crypto, DES_set_key_unchecked, [SSL_LIBS="-lcrypto"])
AC_SUBST(SSL_LIBS)
dnl ---------------------------------------------------------------------------
dnl - Check for UUID library
dnl ---------------------------------------------------------------------------
AC_CHECK_HEADERS(uuid/uuid.h,,[AC_MSG_ERROR([uuid/uuid.h not found])])
AC_CHECK_LIB(uuid, uuid_generate_time, [UUID_LIBS="-luuid"])
AC_SUBST(UUID_LIBS)
dnl ---------------------------------------------------------------------------
dnl - Check for Python
dnl ---------------------------------------------------------------------------
AC_MSG_NOTICE([Checking for Python])
have_python=no
AM_PATH_PYTHON(2.3)
if test "x$PYTHON" = "x" ; then
AC_MSG_ERROR([Python not found])
fi
dnl ---------------------------------------------------------------------------
dnl - Set the data install directory since we don't use pkgdatadir
dnl ---------------------------------------------------------------------------
IPA_DATA_DIR="$datadir/ipa"
AC_SUBST(IPA_DATA_DIR)
dnl ---------------------------------------------------------------------------
dnl Finish
dnl ---------------------------------------------------------------------------
# Turn on the additional warnings last, so -Werror doesn't affect other tests.
AC_ARG_ENABLE(more-warnings,
[AC_HELP_STRING([--enable-more-warnings],
[Maximum compiler warnings])],
set_more_warnings="$enableval",[
if test -d $srcdir/../.hg; then
set_more_warnings=yes
else
set_more_warnings=no
fi
])
AC_MSG_CHECKING(for more warnings)
if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
AC_MSG_RESULT(yes)
CFLAGS="\
-Wall \
-Wchar-subscripts -Wmissing-declarations -Wmissing-prototypes \
-Wnested-externs -Wpointer-arith \
-Wcast-align -Wsign-compare \
$CFLAGS"
for option in -Wno-strict-aliasing -Wno-sign-compare; do
SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $option"
AC_MSG_CHECKING([whether gcc understands $option])
AC_TRY_COMPILE([], [],
has_option=yes,
has_option=no,)
if test $has_option = no; then
CFLAGS="$SAVE_CFLAGS"
fi
AC_MSG_RESULT($has_option)
unset has_option
unset SAVE_CFLAGS
done
unset option
else
AC_MSG_RESULT(no)
fi
# Flags
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LDFLAGS)
# Files
AC_CONFIG_FILES([
Makefile
ipa-kpasswd/Makefile
ipa-slapi-plugins/Makefile
ipa-slapi-plugins/ipa-enrollment/Makefile
ipa-slapi-plugins/ipa-lockout/Makefile
ipa-slapi-plugins/ipa-pwd-extop/Makefile
ipa-slapi-plugins/ipa-winsync/Makefile
ipa-slapi-plugins/ipa-version/Makefile
ipa-slapi-plugins/ipa-uuid/Makefile
ipa-slapi-plugins/ipa-modrdn/Makefile
])
AC_OUTPUT
echo "
IPA Server $VERSION
========================
prefix: ${prefix}
exec_prefix: ${exec_prefix}
libdir: ${libdir}
bindir: ${bindir}
sbindir: ${sbindir}
sysconfdir: ${sysconfdir}
localstatedir: ${localstatedir}
datadir: ${datadir}
source code location: ${srcdir}
compiler: ${CC}
cflags: ${CFLAGS}
LDAP libs: ${LDAP_LIBS}
KRB5 libs: ${KRB5_LIBS}
OpenSSL libs: ${SSL_LIBS}
Maintainer mode: ${USE_MAINTAINER_MODE}
"

View File

@ -0,0 +1,59 @@
NULL =
INCLUDES = \
-I. \
-I$(srcdir) \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(OPENLDAP_CFLAGS) \
$(KRB5_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
sbin_PROGRAMS = \
ipa_kpasswd \
$(NULL)
ipa_kpasswd_SOURCES = \
ipa_kpasswd.c \
$(NULL)
ipa_kpasswd_LDADD = \
$(OPENLDAP_LIBS) \
$(KRB5_LIBS) \
$(NULL)
install-exec-local:
mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/kpasswd
chmod 700 $(DESTDIR)$(localstatedir)/cache/ipa/kpasswd
uninstall-local:
-rmdir $(DESTDIR)$(localstatedir)/cache/ipa/kpasswd
-rmdir $(DESTDIR)$(localstatedir)/cache/ipa
EXTRA_DIST = \
README \
ipa_kpasswd.init \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in
initdir=$(sysconfdir)/rc.d/init.d
install-data-hook: ipa_kpasswd.init
if test '!' -d $(DESTDIR)$(initdir); then \
$(mkinstalldirs) $(DESTDIR)$(initdir); \
chmod 755 $(DESTDIR)$(initdir); \
fi
$(INSTALL_SCRIPT) $(srcdir)/ipa_kpasswd.init $(DESTDIR)$(initdir)/ipa_kpasswd
uninstall-hook:
rm -f $(DESTDIR)$(initdir)/ipa_kpasswd

View File

@ -0,0 +1,2 @@
This is an implementation of the RFC3244 kpasswd protocol.
It is used to proxy password change operations to Directory Server.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
#!/bin/sh
#
# ipa_kpasswd This starts and stops ipa_kpasswd
#
# chkconfig: - 36 64
# description: ipa_kpasswd IPA Kpasswd daemon
# processname: /usr/sbin/ipa_kpasswd
# configdir: /etc/sysconfig/ipa-kpasswd
#
# Source function library.
if [ -f /etc/rc.d/init.d/functions ] ; then
. /etc/rc.d/init.d/functions
fi
# Source networking configuration.
if [ -f /etc/sysconfig/network ] ; then
. /etc/sysconfig/network
fi
# Check that networking is up.
if [ "${NETWORKING}" = "no" ]
then
echo "Networking is down"
exit 0
fi
# Source networking configuration.
if [ -f /etc/sysconfig/ipa-kpasswd ] ; then
. /etc/sysconfig/ipa-kpasswd
fi
NAME="ipa_kpasswd"
PROG="/usr/sbin/ipa_kpasswd"
start() {
echo -n $"Starting $NAME: "
daemon $NAME
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/ipa_kpasswd || \
RETVAL=1
return $RETVAL
}
stop() {
echo -n $"Shutting down $NAME: "
killproc $NAME
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ipa_kpasswd
return $RETVAL
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $PROG
;;
restart)
restart
;;
condrestart)
[ -f /var/lock/subsys/ipa_kpasswd ] && restart || :
;;
reload)
exit 3
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
exit 2
esac
exit $?

View File

@ -0,0 +1,19 @@
NULL =
SUBDIRS = \
ipa-enrollment \
ipa-lockout \
ipa-modrdn \
ipa-pwd-extop \
ipa-uuid \
ipa-version \
ipa-winsync \
$(NULL)
EXTRA_DIST = \
README \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

View File

@ -0,0 +1,75 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Copyright (C) 2010 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#ifndef _SLAPI_PLUGINS_UTIL_H
#define _SLAPI_PLUGINS_UTIL_H
#define EOK 0
#define EFAIL -1
#ifndef discard_const
#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
#endif
#define log_func discard_const(__func__)
#define LOG_PLUGIN_NAME(NAME, fmt, ...) \
slapi_log_error(SLAPI_LOG_PLUGIN, \
NAME, \
fmt, ##__VA_ARGS__)
#define LOG(fmt, ...) \
LOG_PLUGIN_NAME(IPA_PLUGIN_NAME, fmt, ##__VA_ARGS__)
#define LOG_CONFIG_NAME(NAME, fmt, ...) \
slapi_log_error(SLAPI_LOG_CONFIG, \
NAME, \
fmt, ##__VA_ARGS__)
#define LOG_CONFIG(fmt, ...) \
LOG_CONFIG_NAME(IPA_PLUGIN_NAME, fmt, ##__VA_ARGS__)
#define LOG_FATAL(fmt, ...) \
slapi_log_error(SLAPI_LOG_FATAL, log_func, \
"[file %s, line %d]: " fmt, \
__FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_TRACE(fmt, ...) \
slapi_log_error(SLAPI_LOG_TRACE, log_func, fmt, ##__VA_ARGS__)
#define LOG_OOM() LOG_FATAL("Out of Memory!\n")
#endif /* _SLAPI_PLUGINS_UTIL_H */

View File

@ -0,0 +1,46 @@
NULL =
PLUGIN_COMMON_DIR=../common
INCLUDES = \
-I. \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(KRB5_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_enrollment_extop.la \
$(NULL)
libipa_enrollment_extop_la_SOURCES = \
ipa_enrollment.c \
$(NULL)
libipa_enrollment_extop_la_LDFLAGS = -avoid-version
libipa_enrollment_extop_la_LIBADD = \
$(LDAP_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
enrollment-conf.ldif \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,16 @@
dn: cn=ipa_enrollment_extop,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: ipa_enrollment_extop
nsslapd-pluginpath: libipa_enrollment_extop
nsslapd-plugininitfunc: ipaenrollment_init
nsslapd-plugintype: extendedop
nsslapd-pluginenabled: on
nsslapd-pluginid: ipa_enrollment_extop
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: RedHat
nsslapd-plugindescription: Enroll hosts into the IPA domain
nsslapd-plugin-depends-on-type: database
nsslapd-realmTree: $SUFFIX

View File

@ -0,0 +1,462 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Copyright (C) 2005 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/*
* Enroll a host into the IPA domain.
*
*/
#include <stdio.h>
#include <string.h>
#include <dirsrv/slapi-plugin.h>
#include <krb5.h>
#include "util.h"
#define IPA_PLUGIN_NAME "ipa-enrollment"
/* OID of the extended operation handled by this plug-in */
#define JOIN_OID "2.16.840.1.113730.3.8.10.3"
Slapi_PluginDesc pdesc = {
IPA_PLUGIN_NAME,
"IPA Project",
"IPA/2.0",
"IPA Enrollment Extended Operation plugin"
};
static char *ipaenrollment_oid_list[] = {
JOIN_OID,
NULL
};
static char *ipaenrollment_name_list[] = {
"Enrollment Extended Operation",
NULL
};
static void *ipaenrollment_plugin_id;
static char *realm;
static const char *ipa_realm_dn;
static int
ipaenrollement_secure(Slapi_PBlock *pb, char **errMesg)
{
int ssf;
int rc = LDAP_SUCCESS;
LOG_TRACE("=> ipaenrollment_secure\n");
/* Allow enrollment on all connections with a Security Strength
* Factor (SSF) higher than 1 */
if (slapi_pblock_get(pb, SLAPI_OPERATION_SSF, &ssf) != 0) {
LOG_TRACE("Could not get SSF from connection\n");
*errMesg = "Operation requires a secure connection.\n";
rc = LDAP_OPERATIONS_ERROR;
goto done;
}
if (NULL == realm) {
*errMesg = "Kerberos realm is not set.\n";
LOG_FATAL("%s", errMesg);
rc = LDAP_OPERATIONS_ERROR;
goto done;
}
if (ssf <= 1) {
*errMesg = "Operation requires a secure connection.\n";
rc = LDAP_CONFIDENTIALITY_REQUIRED;
goto done;
}
done:
LOG_TRACE("<= ipaenrollment_secure\n");
return rc;
}
/* The extop call passes in the FQDN of the host to enroll.
* We take that and set the krbPrincipalName and add the appropriate
* objectclasses, then return krbPrincipalName. The caller should take
* this and pass it to ipa-getkeytab to generate the keytab.
*
* The password for the entry is removed by ipa-getkeytab.
*/
static int
ipa_join(Slapi_PBlock *pb)
{
char *bindDN = NULL;
char *errMesg = NULL;
struct berval *extop_value = NULL;
Slapi_PBlock *pbte = NULL;
Slapi_PBlock *pbtm = NULL;
Slapi_Entry *targetEntry=NULL;
Slapi_DN *sdn;
Slapi_Backend *be;
Slapi_Entry **es = NULL;
int rc=0, ret=0, res, i;
int is_root=0;
char *krbLastPwdChange = NULL;
char *fqdn = NULL;
Slapi_Mods *smods;
char *attrlist[] = {"fqdn", "krbPrincipalKey", "krbLastPwdChange", "krbPrincipalName", NULL };
char * filter;
int scope = LDAP_SCOPE_SUBTREE;
char *principal = NULL;
struct berval retbval;
if (NULL == realm) {
errMesg = "Kerberos realm is not set.\n";
LOG_FATAL("%s", errMesg);
rc = LDAP_OPERATIONS_ERROR;
goto free_and_return;
}
/* Get Bind DN */
slapi_pblock_get(pb, SLAPI_CONN_DN, &bindDN);
/* If the connection is bound anonymously we must refuse to process
* this operation.
*/
if (bindDN == NULL || *bindDN == '\0') {
/* Refuse the operation because they're bound anonymously */
errMesg = "Anonymous Binds are not allowed.\n";
rc = LDAP_INSUFFICIENT_ACCESS;
goto free_and_return;
}
/* Get the ber value of the extended operation */
slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
/* We are passed in the FQDN of the host to enroll. Do an internal
* search and pull that entry.
*/
filter = slapi_ch_smprintf("(fqdn=%s)", extop_value->bv_val);
pbte = slapi_pblock_new();
slapi_search_internal_set_pb(pbte,
ipa_realm_dn, scope, filter, attrlist, 0,
NULL, /* Controls */
NULL, /* UniqueID */
ipaenrollment_plugin_id,
0); /* Flags */
/* do search the tree */
ret = slapi_search_internal_pb(pbte);
slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_RESULT, &res);
if (ret == -1 || res != LDAP_SUCCESS) {
LOG_TRACE("Search for host failed, err (%d)\n", res?res:ret);
errMesg = "Host not found.\n";
rc = LDAP_NO_SUCH_OBJECT;
goto free_and_return;
}
/* get entries */
slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &es);
if (!es) {
LOG_TRACE("No entries ?!");
errMesg = "Host not found.\n";
rc = LDAP_NO_SUCH_OBJECT;
goto free_and_return;
}
/* count entries */
for (i = 0; es[i]; i++) /* count */ ;
/* if there is none or more than one, freak out */
if (i != 1) {
LOG_TRACE("Too many entries, or entry no found (%d)", i);
errMesg = "Host not found.\n";
rc = LDAP_NO_SUCH_OBJECT;
goto free_and_return;
}
targetEntry = es[0];
/* Is this host already enrolled? */
krbLastPwdChange = slapi_entry_attr_get_charptr(targetEntry, "krbLastPwdChange");
if (NULL != krbLastPwdChange) {
LOG_TRACE("Host already enrolled");
errMesg = "Host already enrolled.\n";
rc = LDAP_OPERATIONS_ERROR;
goto free_and_return;
}
/* First thing to do is to ask access control if the bound identity has
* rights to modify the userpassword attribute on this entry. If not,
* then we fail immediately with insufficient access. This means that
* we don't leak any useful information to the client such as current
* password wrong, etc.
*/
is_root = slapi_dn_isroot(bindDN);
if (slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &is_root)) {
LOG_FATAL("slapi_pblock_set failed!\n");
rc = LDAP_OPERATIONS_ERROR;
goto free_and_return;
}
/* In order to perform the access control check,
* we need to select a backend (even though
* we don't actually need it otherwise).
*/
sdn = slapi_sdn_new_dn_byval(bindDN);
be = slapi_be_select(sdn);
if (slapi_pblock_set(pb, SLAPI_BACKEND, be)) {
LOG_FATAL("slapi_pblock_set failed!\n");
rc = LDAP_OPERATIONS_ERROR;
goto free_and_return;
}
/* Access Strategy:
* If the user has WRITE-ONLY access, a new keytab is set on the entry.
*/
ret = slapi_access_allowed(pb, targetEntry, "krbPrincipalKey", NULL, SLAPI_ACL_WRITE);
if (ret != LDAP_SUCCESS) {
errMesg = "Insufficient access rights\n";
rc = LDAP_INSUFFICIENT_ACCESS;
goto free_and_return;
}
/* If a principal is already set return the name */
principal = slapi_entry_attr_get_charptr(targetEntry, "krbPrincipalName");
if (NULL != principal)
goto done;
/* Add the elements needed for enrollment */
smods = slapi_mods_new();
fqdn = slapi_entry_attr_get_charptr(targetEntry, "fqdn");
principal = slapi_ch_smprintf("host/%s@%s", fqdn, realm);
slapi_mods_add_string(smods, LDAP_MOD_ADD, "krbPrincipalName", principal);
slapi_mods_add_string(smods, LDAP_MOD_ADD, "objectClass", "krbPrincipalAux");
pbtm = slapi_pblock_new();
slapi_modify_internal_set_pb (pbtm, slapi_entry_get_dn_const(targetEntry),
slapi_mods_get_ldapmods_byref(smods),
NULL, /* Controls */
NULL, /* UniqueID */
ipaenrollment_plugin_id, /* PluginID */
0); /* Flags */
rc = slapi_modify_internal_pb (pbtm);
if (rc) {
LOG_TRACE("WARNING: modify error %d on entry '%s'\n",
rc, slapi_entry_get_dn_const(targetEntry));
} else {
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
if (rc != LDAP_SUCCESS){
LOG_TRACE("WARNING: modify error %d on entry '%s'\n",
rc, slapi_entry_get_dn_const(targetEntry));
} else {
LOG_TRACE("<= apply mods: Successful\n");
}
}
done:
/* Return the krbprincipalname */
retbval.bv_val = principal;
retbval.bv_len = strlen(principal);
ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, JOIN_OID);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &retbval);
if (ret) {
errMesg = "Could not set return values";
LOG("%s\n", errMesg);
rc = SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
}
/* Free anything that we allocated above */
free_and_return:
if (pbte) {
slapi_free_search_results_internal(pbte);
slapi_pblock_destroy(pbte);
}
if (pbtm) {
slapi_pblock_destroy(pbtm);
}
if (krbLastPwdChange) slapi_ch_free_string(&krbLastPwdChange);
LOG(errMesg ? errMesg : "success\n");
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
free(principal);
return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
}
/* Extended operation plug-in */
static int
ipaenrollment_extop(Slapi_PBlock *pb)
{
char *oid;
char *errMesg = NULL;
int rc, ret;
LOG_TRACE("=> ipaenrollment_extop\n");
rc = ipaenrollement_secure(pb, &errMesg);
if (rc) {
goto free_and_return;
}
/* Get the OID and the value included in the request */
if (slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0) {
errMesg = "Could not get OID and value from request.\n";
rc = LDAP_OPERATIONS_ERROR;
LOG(errMesg);
goto free_and_return;
}
if (strcasecmp(oid, JOIN_OID) == 0) {
ret = ipa_join(pb);
return ret;
}
errMesg = "Request OID does not match supported OIDs.\n";
rc = LDAP_OPERATIONS_ERROR;
free_and_return:
LOG(errMesg);
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
}
static int
ipaenrollment_start(Slapi_PBlock *pb)
{
krb5_error_code krberr;
krb5_context krbctx;
char *config_dn = NULL;
char *partition_dn = NULL;
Slapi_Entry *config_entry = NULL;
int ret = LDAP_SUCCESS;
Slapi_DN *sdn;
int rc = 0;
krberr = krb5_init_context(&krbctx);
if (krberr) {
LOG_FATAL("krb5_init_context failed\n");
/* Yes, we failed, but it is because /etc/krb5.conf doesn't exist
* or is misconfigured. Start up in a degraded mode.
*/
goto done;
}
krberr = krb5_get_default_realm(krbctx, &realm);
if (krberr) {
realm = NULL;
LOG_FATAL("Failed to get default realm?!\n");
goto done;
}
if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &config_dn) != 0) {
LOG_FATAL("No config DN?\n");
goto done;
}
sdn = slapi_sdn_new_dn_byref(config_dn);
if ((rc = slapi_search_internal_get_entry(sdn, NULL, &config_entry,
ipaenrollment_plugin_id)) != LDAP_SUCCESS ){
LOG_TRACE("ipaenrollment_start: No such entry-(%s), err (%d)\n",
config_dn, rc);
}
slapi_sdn_free(&sdn);
partition_dn = slapi_entry_attr_get_charptr(config_entry, "nsslapd-realmtree");
if (!partition_dn) {
LOG_FATAL("Missing partition configuration entry (nsslapd-realmTree)!\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
ipa_realm_dn = slapi_ch_smprintf("cn=computers,cn=accounts,%s", partition_dn);
slapi_ch_free_string(&partition_dn);
if (!ipa_realm_dn) {
LOG_FATAL("Out of memory ?\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
done:
if (krbctx) krb5_free_context(krbctx);
if (config_entry) slapi_entry_free(config_entry);
return ret;
}
int
ipaenrollment_init(Slapi_PBlock *pb)
{
int ret;
/* Get the arguments appended to the plugin extendedop directive
* in the plugin entry. The first argument
* (after the standard arguments for the directive) should
* contain the OID of the extended op.
*/
ret = slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ipaenrollment_plugin_id);
if ((ret != 0) || (NULL == ipaenrollment_plugin_id)) {
LOG("Could not get identity or identity was NULL\n");
return -1;
}
LOG("Registering plug-in for extended op.\n");
/* Register the plug-in function as an extended operation
plug-in function. */
ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)ipaenrollment_start);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, ipaenrollment_oid_list);
if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, ipaenrollment_name_list);
if (!ret) slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)ipaenrollment_extop);
if (ret) {
LOG("Failed to set plug-in version, function, and OID.\n");
return -1;
}
return 0;
}

View File

@ -0,0 +1,46 @@
NULL =
PLUGIN_COMMON_DIR=../common
INCLUDES = \
-I. \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-I/usr/include/dirsrv \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_lockout.la \
$(NULL)
libipa_lockout_la_SOURCES = \
ipa_lockout.c \
$(NULL)
libipa_lockout_la_LDFLAGS = -avoid-version
libipa_lockout_la_LIBADD = \
$(LDAP_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
lockout-conf.ldif \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,643 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Copyright (C) 2010 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
/**
* IPA Lockout plug-in
*
* Update the Kerberos lockout variables on LDAP binds.
*
*/
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include "slapi-plugin.h"
#include "nspr.h"
#include "util.h"
#define IPALOCKOUT_PLUGIN_NAME "ipa-lockout-plugin"
#define IPALOCKOUT_PLUGIN_VERSION 0x00010000
#define IPA_PLUGIN_NAME IPALOCKOUT_PLUGIN_NAME
#define IPALOCKOUT_FEATURE_DESC "IPA Lockout"
#define IPALOCKOUT_PLUGIN_DESC "IPA Lockout plugin"
#define IPALOCKOUT_POSTOP_DESC "IPA Lockout postop plugin"
#define IPALOCKOUT_PREOP_DESC "IPA Lockout preop plugin"
static Slapi_PluginDesc pdesc = {
IPALOCKOUT_FEATURE_DESC,
"Red Hat, Inc.",
"1.0",
IPALOCKOUT_PLUGIN_DESC
};
static void *_PluginID = NULL;
static char *_PluginDN = NULL;
static int g_plugin_started = 0;
#define GENERALIZED_TIME_LENGTH 15
/**
*
* management functions
*
*/
int ipalockout_init(Slapi_PBlock * pb);
static int ipalockout_start(Slapi_PBlock * pb);
static int ipalockout_close(Slapi_PBlock * pb);
static int ipalockout_postop_init(Slapi_PBlock * pb);
static int ipalockout_preop_init(Slapi_PBlock * pb);
/**
*
* the ops (where the real work is done)
*
*/
static int ipalockout_postop(Slapi_PBlock *pb);
static int ipalockout_preop(Slapi_PBlock *pb);
/**
*
* Get the plug-in version
*
*/
int ipalockout_version(void)
{
return IPALOCKOUT_PLUGIN_VERSION;
}
/**
* Plugin identity mgmt
*/
void setPluginID(void *pluginID)
{
_PluginID = pluginID;
}
void *getPluginID(void)
{
return _PluginID;
}
void setPluginDN(char *pluginDN)
{
_PluginDN = pluginDN;
}
char *getPluginDN(void)
{
return _PluginDN;
}
int
ipalockout_init(Slapi_PBlock *pb)
{
int status = EOK;
char *plugin_identity = NULL;
LOG_TRACE("--in-->\n");
slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
PR_ASSERT(plugin_identity);
setPluginID(plugin_identity);
if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
(void *) ipalockout_start) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
(void *) ipalockout_close) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
(void *) &pdesc) != 0 ||
slapi_register_plugin("postoperation",
1,
"ipalockout_init",
ipalockout_postop_init,
IPALOCKOUT_POSTOP_DESC,
NULL,
plugin_identity
) ||
slapi_register_plugin("preoperation",
1,
"ipalockout_init",
ipalockout_preop_init,
IPALOCKOUT_PREOP_DESC,
NULL,
plugin_identity
)
) {
LOG_FATAL("failed to register plugin\n");
status = EFAIL;
}
LOG_TRACE("<--out--\n");
return status;
}
static int
ipalockout_postop_init(Slapi_PBlock *pb)
{
int status = EOK;
if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
(void *) &pdesc) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_BIND_FN,
(void *) ipalockout_postop) != 0) {
status = EFAIL;
}
return status;
}
static int
ipalockout_preop_init(Slapi_PBlock *pb)
{
int status = EOK;
if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
(void *) &pdesc) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN,
(void *) ipalockout_preop) != 0) {
status = EFAIL;
}
return status;
}
static int
ipalockout_start(Slapi_PBlock * pb)
{
LOG_TRACE("--in-->\n");
/* Check if we're already started */
if (g_plugin_started) {
goto done;
}
g_plugin_started = 1;
LOG("ready for service\n");
LOG_TRACE("<--out--\n");
done:
return EOK;
}
static int
ipalockout_close(Slapi_PBlock * pb)
{
LOG_TRACE( "--in-->\n");
LOG_TRACE("<--out--\n");
return EOK;
}
/*
* In the post-operation we know whether the bind was successful or not
* so here we handle updating the Kerberos lockout policy attributes.
*/
static int ipalockout_postop(Slapi_PBlock *pb)
{
char *dn = NULL;
char *policy_dn = NULL;
Slapi_Entry *target_entry = NULL;
Slapi_Entry *policy_entry = NULL;
Slapi_DN *sdn = NULL;
Slapi_DN *pdn = NULL;
Slapi_PBlock *pbtm = NULL;
Slapi_Mods *smods = NULL;
Slapi_Value *objectclass = NULL;
char *errstr = NULL;
int ldrc, rc = 0;
int ret = LDAP_SUCCESS;
unsigned long failedcount = 0;
char failedcountstr[32];
int failed_bind = 0;
struct tm utctime;
time_t time_now;
char timestr[GENERALIZED_TIME_LENGTH+1];
unsigned int failcnt_interval = 0;
char *lastfail = NULL;
int tries = 0;
int failure = 1;
LOG_TRACE("--in-->\n");
/* Just bail if we aren't ready to service requests yet. */
if (!g_plugin_started) {
goto done;
}
slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rc);
/* free the dn here */
if (slapi_pblock_get(pb, SLAPI_CONN_DN, &dn) != 0) {
LOG_FATAL("Error retrieving bind DN\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
/* dn will be NULL on failed auth, get the target instead */
/* don't free this dn */
if (dn == NULL && rc != LDAP_SUCCESS) {
failed_bind = 1;
if (slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn) != 0) {
LOG_FATAL("Error retrieving target DN\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
}
/* Client is anonymously bound */
if (dn == NULL) {
LOG_TRACE("anonymous bind\n");
goto done;
}
/* Get the entry */
sdn = slapi_sdn_new_dn_byref(dn);
if (sdn == NULL) {
LOG_OOM();
errstr = "Out of memory.\n";
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
ldrc = slapi_search_internal_get_entry(sdn, NULL, &target_entry,
getPluginID());
if (ldrc != LDAP_SUCCESS) {
LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", dn, ldrc);
goto done;
}
/* Only update kerberos principal entries */
objectclass = slapi_value_new_string("krbPrincipalAux");
if ((slapi_entry_attr_has_syntax_value(target_entry, SLAPI_ATTR_OBJECTCLASS, objectclass)) != 1) {
LOG_TRACE("Not a kerberos user\n");
slapi_value_free(&objectclass);
goto done;
}
slapi_value_free(&objectclass);
/* Only update if there is a password policy */
policy_dn = slapi_entry_attr_get_charptr(target_entry, "krbPwdPolicyReference");
if (policy_dn == NULL) {
LOG_TRACE("No kerberos password policy\n");
goto done;
} else {
pdn = slapi_sdn_new_dn_byref(policy_dn);
ldrc = slapi_search_internal_get_entry(pdn, NULL, &policy_entry,
getPluginID());
slapi_sdn_free(&pdn);
if (ldrc != LDAP_SUCCESS) {
LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", policy_dn, ldrc);
errstr = "Failed to retrieve account policy.";
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
}
failedcount = slapi_entry_attr_get_ulong(target_entry, "krbLoginFailedCount");
failcnt_interval = slapi_entry_attr_get_uint(policy_entry, "krbPwdFailureCountInterval");
lastfail = slapi_entry_attr_get_charptr(target_entry, "krbLastFailedAuth");
time_now = time(NULL);
if (lastfail != NULL) {
struct tm tm;
int ret = 0;
memset(&tm, 0, sizeof(struct tm));
ret = sscanf(lastfail,
"%04u%02u%02u%02u%02u%02u",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (ret == 6) {
tm.tm_year -= 1900;
tm.tm_mon -= 1;
if (time_now > timegm(&tm) + failcnt_interval) {
failedcount = 0;
}
}
}
while (tries < 5) {
smods = slapi_mods_new();
/* On failures try very hard to update the entry so that failures
* are counted properly. This involves doing a DELETE of the value
* we expect and an ADD of the new one in the same update. If the
* record has changed while we were handling the request our
* update will fail and we will try again.
*
* On a successful bind just do a replace and set failurecount to 0.
*/
if (failed_bind) {
PR_snprintf(failedcountstr, sizeof(failedcountstr), "%lu", failedcount);
if (!gmtime_r(&(time_now), &utctime)) {
errstr = "failed to parse current date (buggy gmtime_r ?)\n";
LOG_FATAL("%s", errstr);
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
strftime(timestr, GENERALIZED_TIME_LENGTH+1,
"%Y%m%d%H%M%SZ", &utctime);
slapi_mods_add_string(smods, LDAP_MOD_DELETE, "krbLoginFailedCount", failedcountstr);
failedcount += 1;
PR_snprintf(failedcountstr, sizeof(failedcountstr), "%lu", failedcount);
slapi_mods_add_string(smods, LDAP_MOD_ADD, "krbLoginFailedCount", failedcountstr);
if (lastfail)
slapi_mods_add_string(smods, LDAP_MOD_DELETE, "krbLastFailedAuth", lastfail);
slapi_mods_add_string(smods, LDAP_MOD_ADD, "krbLastFailedAuth", timestr);
} else {
PR_snprintf(failedcountstr, sizeof(failedcountstr), "%lu", 0L);
time_now = time(NULL);
if (!gmtime_r(&(time_now), &utctime)) {
errstr = "failed to parse current date (buggy gmtime_r ?)\n";
LOG_FATAL("%s", errstr);
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
strftime(timestr, GENERALIZED_TIME_LENGTH+1,
"%Y%m%d%H%M%SZ", &utctime);
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLoginFailedCount", failedcountstr);
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastSuccessfulAuth", timestr);
}
pbtm = slapi_pblock_new();
slapi_modify_internal_set_pb (pbtm, slapi_entry_get_dn_const(target_entry),
slapi_mods_get_ldapmods_byref(smods),
NULL, /* Controls */
NULL, /* UniqueID */
getPluginID(), /* PluginID */
0); /* Flags */
slapi_modify_internal_pb (pbtm);
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
if (rc != LDAP_SUCCESS) {
LOG_TRACE("WARNING: modify error %d on entry '%s'\n",
rc, slapi_entry_get_dn_const(target_entry));
ldrc = slapi_search_internal_get_entry(sdn, NULL, &target_entry,
getPluginID());
if (ldrc != LDAP_SUCCESS) {
LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", dn, ldrc);
goto done;
}
slapi_mods_free(&smods);
slapi_pblock_destroy(pbtm);
if (lastfail) slapi_ch_free_string(&lastfail);
smods = NULL;
pbtm = NULL;
lastfail = NULL;
tries += 1;
} else {
LOG_TRACE("<= apply mods: Successful\n");
failure = 0;
break;
}
} /* while */
if (failure) {
ret = LDAP_OPERATIONS_ERROR;
}
done:
if (!failed_bind && dn != NULL) slapi_ch_free_string(&dn);
slapi_entry_free(target_entry);
if (policy_dn) {
slapi_ch_free_string(&policy_dn);
slapi_entry_free(policy_entry);
}
if (sdn) slapi_sdn_free(&sdn);
if (lastfail) slapi_ch_free_string(&lastfail);
if (pbtm) slapi_pblock_destroy(pbtm);
if (smods) slapi_mods_free(&smods);
LOG("postop returning %d: %s\n", ret, errstr ? errstr : "success\n");
if (ret) {
slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
}
LOG_TRACE("<--out--\n");
return (ret == 0 ? EOK : EFAIL);
}
/*
* In the pre-op stage the bind hasn't occurred yet. It is here that
* we do the lockout enforcement.
*/
static int ipalockout_preop(Slapi_PBlock *pb)
{
char *dn = NULL;
char *policy_dn = NULL;
Slapi_Entry *target_entry = NULL;
Slapi_Entry *policy_entry = NULL;
Slapi_DN *sdn = NULL;
Slapi_DN *pdn = NULL;
Slapi_Value *objectclass = NULL;
char *errstr = NULL;
int ldrc = 0;
int ret = LDAP_SUCCESS;
unsigned long failedcount = 0;
time_t time_now;
unsigned int failcnt_interval = 0;
unsigned int max_fail = 0;
unsigned int lockout_duration = 0;
time_t last_failed = 0;
char *lastfail = NULL;
char *unlock_time = NULL;
LOG_TRACE("--in-->\n");
/* Just bail if we aren't ready to service requests yet. */
if (!g_plugin_started) {
goto done;
}
if (slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn) != 0) {
LOG_FATAL("Error retrieving target DN\n");
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
/* Client is anonymously bound */
if (dn == NULL) {
LOG_TRACE("anonymous bind\n");
goto done;
}
/* Get the entry */
sdn = slapi_sdn_new_dn_byref(dn);
if (sdn == NULL) {
LOG_OOM();
errstr = "Out of memory.\n";
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
ldrc = slapi_search_internal_get_entry(sdn, NULL, &target_entry,
getPluginID());
if (ldrc != LDAP_SUCCESS) {
LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", dn, ldrc);
goto done;
}
/* Only handle kerberos principal entries */
objectclass = slapi_value_new_string("krbPrincipalAux");
if ((slapi_entry_attr_has_syntax_value(target_entry, SLAPI_ATTR_OBJECTCLASS, objectclass)) != 1) {
LOG_TRACE("Not a kerberos user\n");
slapi_value_free(&objectclass);
goto done;
}
slapi_value_free(&objectclass);
/* Only continue if there is a password policy */
policy_dn = slapi_entry_attr_get_charptr(target_entry, "krbPwdPolicyReference");
if (policy_dn == NULL) {
LOG_TRACE("No kerberos password policy\n");
goto done;
} else {
pdn = slapi_sdn_new_dn_byref(policy_dn);
ldrc = slapi_search_internal_get_entry(pdn, NULL, &policy_entry,
getPluginID());
slapi_sdn_free(&pdn);
if (ldrc != LDAP_SUCCESS) {
LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", policy_dn, ldrc);
errstr = "Failed to retrieve account policy.";
ret = LDAP_OPERATIONS_ERROR;
goto done;
}
}
failedcount = slapi_entry_attr_get_ulong(target_entry, "krbLoginFailedCount");
time_now = time(NULL);
failcnt_interval = slapi_entry_attr_get_uint(policy_entry, "krbPwdFailureCountInterval");
lastfail = slapi_entry_attr_get_charptr(target_entry, "krbLastFailedAuth");
unlock_time = slapi_entry_attr_get_charptr(target_entry, "krbLastAdminUnlock");
if (lastfail != NULL) {
struct tm tm;
int ret = 0;
memset(&tm, 0, sizeof(struct tm));
ret = sscanf(lastfail,
"%04u%02u%02u%02u%02u%02u",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (ret == 6) {
tm.tm_year -= 1900;
tm.tm_mon -= 1;
last_failed = timegm(&tm);
LOG("%d > %d ?\n", time_now, last_failed + failcnt_interval);
LOG("diff %d\n", (last_failed + failcnt_interval) - time_now);
if (time_now > last_failed + failcnt_interval) {
failedcount = 0;
}
}
if (unlock_time) {
time_t unlock;
memset(&tm, 0, sizeof(struct tm));
ret = sscanf(lastfail,
"%04u%02u%02u%02u%02u%02u",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (ret == 6) {
tm.tm_year -= 1900;
tm.tm_mon -= 1;
unlock = timegm(&tm);
if (last_failed <= unlock) {
goto done;
}
}
slapi_ch_free_string(&unlock_time);
}
slapi_ch_free_string(&lastfail);
}
max_fail = slapi_entry_attr_get_uint(policy_entry, "krbPwdMaxFailure");
if (max_fail == 0) {
goto done;
}
lockout_duration = slapi_entry_attr_get_uint(policy_entry, "krbPwdLockoutDuration");
if (lockout_duration == 0) {
errstr = "Entry permanently locked.\n";
ret = LDAP_UNWILLING_TO_PERFORM;
goto done;
}
if (failedcount > max_fail) {
if (time_now < last_failed + lockout_duration) {
/* Too many failures */
LOG_TRACE("Too many failed logins. %lu out of %d\n", failedcount, max_fail);
errstr = "Too many failed logins.\n";
ret = LDAP_UNWILLING_TO_PERFORM;
}
}
done:
slapi_entry_free(target_entry);
slapi_entry_free(policy_entry);
if (policy_dn) slapi_ch_free_string(&policy_dn);
if (sdn) slapi_sdn_free(&sdn);
LOG("preop returning %d: %s\n", ret, errstr ? errstr : "success\n");
if (ret) {
slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
}
LOG_TRACE("<--out--\n");
return (ret == 0 ? EOK : EFAIL);
}

View File

@ -0,0 +1,15 @@
dn: cn=IPA Lockout,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: IPA Lockout
nsslapd-pluginpath: libipa_lockout
nsslapd-plugininitfunc: ipalockout_init
nsslapd-plugintype: object
nsslapd-pluginenabled: on
nsslapd-pluginid: ipalockout_version
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: Red Hat, Inc.
nsslapd-plugindescription: IPA Lockout plugin
nsslapd-plugin-depends-on-type: database

View File

@ -0,0 +1,46 @@
NULL =
PLUGIN_COMMON_DIR=../common
INCLUDES = \
-I. \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-I/usr/include/dirsrv \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_modrdn.la \
$(NULL)
libipa_modrdn_la_SOURCES = \
ipa_modrdn.c \
$(NULL)
libipa_modrdn_la_LDFLAGS = -avoid-version
libipa_modrdn_la_LIBADD = \
$(LDAP_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
modrdn-conf.ldif \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,796 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Copyright (C) 2010 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
/**
* IPA MODRDN plug-in
*/
#include <string.h>
#include <stdbool.h>
#include "slapi-plugin.h"
#include "nspr.h"
#include "prclist.h"
#include <pthread.h>
#include "util.h"
#define IPA_PLUGIN_NAME "ipa-modrdn-plugin"
#define IPAMODRDN_PLUGIN_VERSION 0x00010000
#define IPAMODRDN_DN "cn=IPA MODRDN,cn=plugins,cn=config" /* temporary */
/**
* IPA MODRDN config types
*/
#define IPAMODRDN_SATTR "ipaModRDNsourceAttr"
#define IPAMODRDN_TATTR "ipaModRDNtargetAttr"
#define IPAMODRDN_PREFIX "ipaModRDNprefix"
#define IPAMODRDN_SUFFIX "ipaModRDNsuffix"
#define IPAMODRDN_FILTER "ipaModRDNfilter"
#define IPAMODRDN_SCOPE "ipaModRDNscope"
#define IPAMODRDN_FEATURE_DESC "IPA MODRDN"
#define IPAMODRDN_PLUGIN_DESC "IPA MODRDN plugin"
#define IPAMODRDN_POSTOP_DESC "IPA MODRDN postop plugin"
static Slapi_PluginDesc pdesc = {
IPAMODRDN_FEATURE_DESC,
"Red Hat, Inc.",
"1.0",
IPAMODRDN_PLUGIN_DESC
};
/**
* linked list of config entries
*/
struct configEntry {
PRCList list;
char *dn;
char *sattr;
char *tattr;
char *prefix;
char *suffix;
char *filter;
Slapi_Filter *slapi_filter;
char *scope;
};
static PRCList *ipamodrdn_global_config = NULL;
static pthread_rwlock_t g_ipamodrdn_cache_lock;
static void *_PluginID = NULL;
static char *_PluginDN = NULL;
static int g_plugin_started = 0;
/**
*
* management functions
*
*/
int ipamodrdn_init(Slapi_PBlock * pb);
static int ipamodrdn_start(Slapi_PBlock * pb);
static int ipamodrdn_close(Slapi_PBlock * pb);
/**
*
* Local operation functions
*
*/
static int ipamodrdn_load_plugin_config(void);
static int ipamodrdn_parse_config_entry(Slapi_Entry * e, bool apply);
static void ipamodrdn_delete_config(void);
static void ipamodrdn_free_config_entry(struct configEntry ** entry);
/**
*
* helpers
*
*/
static char *ipamodrdn_get_dn(Slapi_PBlock * pb);
static int ipamodrdn_dn_is_config(char *dn);
/**
*
* the ops (where the real work is done)
*
*/
static int ipamodrdn_config_check_post_op(Slapi_PBlock * pb);
static int ipamodrdn_post_op(Slapi_PBlock * pb);
/**
* debug functions - global, for the debugger
*/
void ipamodrdn_dump_config(void);
void ipamodrdn_dump_config_entry(struct configEntry *);
/**
*
* Deal with cache locking
*
*/
void ipamodrdn_read_lock(void)
{
pthread_rwlock_rdlock(&g_ipamodrdn_cache_lock);
}
void ipamodrdn_write_lock(void)
{
pthread_rwlock_wrlock(&g_ipamodrdn_cache_lock);
}
void ipamodrdn_unlock(void)
{
pthread_rwlock_unlock(&g_ipamodrdn_cache_lock);
}
/**
*
* Get the plug-in version
*
*/
int ipamodrdn_version(void)
{
return IPAMODRDN_PLUGIN_VERSION;
}
/**
* Plugin identity mgmt
*/
void setPluginID(void *pluginID)
{
_PluginID = pluginID;
}
void *getPluginID(void)
{
return _PluginID;
}
void setPluginDN(char *pluginDN)
{
_PluginDN = pluginDN;
}
char *getPluginDN(void)
{
return _PluginDN;
}
/*
ipamodrdn_init
-------------
adds our callbacks to the list
*/
int
ipamodrdn_init(Slapi_PBlock *pb)
{
int status = EOK;
char *plugin_identity = NULL;
LOG_TRACE("--in-->\n");
/**
* Store the plugin identity for later use.
* Used for internal operations
*/
slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
PR_ASSERT(plugin_identity);
setPluginID(plugin_identity);
if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
(void *) &pdesc) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
(void *) ipamodrdn_start) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
(void *) ipamodrdn_close) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
(void *) ipamodrdn_config_check_post_op) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
(void *) ipamodrdn_post_op) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
(void *) ipamodrdn_config_check_post_op) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
(void *) ipamodrdn_config_check_post_op) != 0) {
LOG_FATAL("failed to register plugin\n");
status = EFAIL;
}
LOG_TRACE("<--out--\n");
return status;
}
/*
ipamodrdn_start
--------------
Kicks off the config cache.
It is called after ipamodrdn_init.
*/
static int
ipamodrdn_start(Slapi_PBlock * pb)
{
char *plugindn = NULL;
LOG_TRACE("--in-->\n");
/* Check if we're already started */
if (g_plugin_started) {
goto done;
}
if (pthread_rwlock_init(&g_ipamodrdn_cache_lock, NULL) != 0) {
LOG_FATAL("lock creation failed\n");
return EFAIL;
}
/**
* Get the plug-in target dn from the system
* and store it for future use. This should avoid
* hardcoding of DN's in the code.
*/
slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn);
if (NULL == plugindn || 0 == strlen(plugindn)) {
LOG("had to use hard coded config dn\n");
plugindn = IPAMODRDN_DN;
} else {
LOG("config at %s\n", plugindn);
}
setPluginDN(plugindn);
/*
* Load the config for our plug-in
*/
ipamodrdn_global_config = (PRCList *)
slapi_ch_calloc(1, sizeof(struct configEntry));
PR_INIT_CLIST(ipamodrdn_global_config);
if (ipamodrdn_load_plugin_config() != EOK) {
LOG_FATAL("unable to load plug-in configuration\n");
return EFAIL;
}
g_plugin_started = 1;
LOG("ready for service\n");
LOG_TRACE("<--out--\n");
done:
return EOK;
}
/*
ipamodrdn_close
--------------
closes down the cache
*/
static int
ipamodrdn_close(Slapi_PBlock * pb)
{
LOG_TRACE( "--in-->\n");
ipamodrdn_delete_config();
slapi_ch_free((void **)&ipamodrdn_global_config);
LOG_TRACE("<--out--\n");
return EOK;
}
/*
* config looks like this
* - cn=myplugin
* --- cn=posix
* ------ cn=accounts
* ------ cn=groups
* --- cn=samba
* --- cn=etc
* ------ cn=etc etc
*/
static int
ipamodrdn_load_plugin_config(void)
{
int status = EOK;
int result;
int i;
Slapi_PBlock *search_pb;
Slapi_Entry **entries = NULL;
LOG_TRACE("--in-->\n");
ipamodrdn_write_lock();
ipamodrdn_delete_config();
search_pb = slapi_pblock_new();
slapi_search_internal_set_pb(search_pb, getPluginDN(),
LDAP_SCOPE_SUBTREE, "objectclass=*",
NULL, 0, NULL, NULL, getPluginID(), 0);
slapi_search_internal_pb(search_pb);
slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
if (LDAP_SUCCESS != result) {
status = EFAIL;
goto cleanup;
}
slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
&entries);
if (NULL == entries || NULL == entries[0]) {
status = EOK;
goto cleanup;
}
for (i = 0; (entries[i] != NULL); i++) {
/* We don't care about the status here because we may have
* some invalid config entries, but we just want to continue
* looking for valid ones. */
ipamodrdn_parse_config_entry(entries[i], true);
}
cleanup:
slapi_free_search_results_internal(search_pb);
slapi_pblock_destroy(search_pb);
ipamodrdn_unlock();
LOG_TRACE("<--out--\n");
return status;
}
/*
* ipamodrdn_parse_config_entry()
*
* Parses a single config entry. If apply is non-zero, then
* we will load and start using the new config. You can simply
* validate config without making any changes by setting apply
* to 0.
*
* Returns EOK if the entry is valid and EFAIL
* if it is invalid.
*/
static int
ipamodrdn_parse_config_entry(Slapi_Entry * e, bool apply)
{
char *value;
struct configEntry *entry = NULL;
struct configEntry *config_entry;
PRCList *list;
int entry_added = 0;
int ret = EOK;
LOG_TRACE("--in-->\n");
/* If this is the main MODRDN plug-in config entry, just bail. */
if (strcasecmp(getPluginDN(), slapi_entry_get_ndn(e)) == 0) {
ret = EFAIL;
goto bail;
}
entry = (struct configEntry *)
slapi_ch_calloc(1, sizeof(struct configEntry));
if (NULL == entry) {
ret = EFAIL;
goto bail;
}
value = slapi_entry_get_ndn(e);
if (value) {
entry->dn = slapi_ch_strdup(value);
}
LOG_CONFIG("----------> dn [%s]\n", entry->dn);
entry->sattr = slapi_entry_attr_get_charptr(e, IPAMODRDN_SATTR);
if (!entry->sattr) {
LOG_FATAL("The %s config setting is required for %s.\n",
IPAMODRDN_SATTR, entry->dn);
ret = EFAIL;
goto bail;
}
LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_SATTR, entry->sattr);
entry->tattr = slapi_entry_attr_get_charptr(e, IPAMODRDN_TATTR);
if (!entry->tattr) {
LOG_FATAL("The %s config setting is required for %s.\n",
IPAMODRDN_TATTR, entry->dn);
ret = EFAIL;
goto bail;
}
LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_TATTR, entry->tattr);
value = slapi_entry_attr_get_charptr(e, IPAMODRDN_PREFIX);
if (value && value[0]) {
entry->prefix = value;
} else {
entry->prefix = slapi_ch_strdup("");
}
LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_PREFIX, entry->prefix);
value = slapi_entry_attr_get_charptr(e, IPAMODRDN_SUFFIX);
if (value && value[0]) {
entry->suffix = value;
} else {
entry->suffix = slapi_ch_strdup("");
}
LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_SUFFIX, entry->suffix);
value = slapi_entry_attr_get_charptr(e, IPAMODRDN_FILTER);
if (value) {
entry->filter = value;
if (NULL == (entry->slapi_filter = slapi_str2filter(value))) {
LOG_FATAL("Error: Invalid search filter in entry [%s]: [%s]\n",
entry->dn, value);
ret = EFAIL;
goto bail;
}
} else {
LOG_FATAL("The %s config setting is required for %s.\n",
IPAMODRDN_FILTER, entry->dn);
ret = EFAIL;
goto bail;
}
LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_FILTER, value);
value = slapi_entry_attr_get_charptr(e, IPAMODRDN_SCOPE);
if (value) {
entry->scope = value;
} else {
LOG_FATAL("The %s config config setting is required for %s.\n",
IPAMODRDN_SCOPE, entry->dn);
ret = EFAIL;
goto bail;
}
LOG_CONFIG("----------> %s [%s]\n", IPAMODRDN_SCOPE, entry->scope);
/* If we were only called to validate config, we can
* just bail out before applying the config changes */
if (!apply) {
goto bail;
}
/**
* Finally add the entry to the list.
* We sort by scope dn length with longer
* dn's first - this allows the scope
* checking code to be simple and quick and
* cunningly linear.
*/
if (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
list = PR_LIST_HEAD(ipamodrdn_global_config);
while (list != ipamodrdn_global_config) {
config_entry = (struct configEntry *) list;
if (slapi_dn_issuffix(entry->scope, config_entry->scope)) {
PR_INSERT_BEFORE(&(entry->list), list);
LOG_CONFIG("store [%s] before [%s] \n",
entry->scope, config_entry->scope);
entry_added = 1;
break;
}
list = PR_NEXT_LINK(list);
if (ipamodrdn_global_config == list) {
/* add to tail */
PR_INSERT_BEFORE(&(entry->list), list);
LOG_CONFIG("store [%s] at tail\n", entry->scope);
entry_added = 1;
break;
}
}
} else {
/* first entry */
PR_INSERT_LINK(&(entry->list), ipamodrdn_global_config);
LOG_CONFIG("store [%s] at head \n", entry->scope);
entry_added = 1;
}
bail:
if (0 == entry_added) {
/* Don't log error if we weren't asked to apply config */
if (apply && (entry != NULL)) {
LOG_FATAL("Invalid config entry [%s] skipped\n", entry->dn);
}
ipamodrdn_free_config_entry(&entry);
} else {
ret = EOK;
}
LOG_TRACE("<--out--\n");
return ret;
}
static void
ipamodrdn_free_config_entry(struct configEntry **entry)
{
struct configEntry *e;
if (!entry || !*entry) {
return;
}
e = *entry;
if (e->dn) {
LOG_CONFIG("freeing config entry [%s]\n", e->dn);
}
slapi_ch_free_string(&e->dn);
slapi_ch_free_string(&e->sattr);
slapi_ch_free_string(&e->tattr);
slapi_ch_free_string(&e->prefix);
slapi_ch_free_string(&e->suffix);
slapi_ch_free_string(&e->filter);
slapi_filter_free(e->slapi_filter, 1);
slapi_ch_free_string(&e->scope);
slapi_ch_free((void **)entry);
}
static void
ipamodrdn_delete_configEntry(PRCList *entry)
{
PR_REMOVE_LINK(entry);
ipamodrdn_free_config_entry((struct configEntry **) &entry);
}
static void
ipamodrdn_delete_config(void)
{
PRCList *list;
while (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
list = PR_LIST_HEAD(ipamodrdn_global_config);
ipamodrdn_delete_configEntry(list);
}
return;
}
/****************************************************
Helpers
****************************************************/
static char *ipamodrdn_get_dn(Slapi_PBlock * pb)
{
char *dn = NULL;
LOG_TRACE("--in-->\n");
if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn)) {
LOG_FATAL("failed to get dn of changed entry");
}
LOG_TRACE("<--out--\n");
return dn;
}
/* config check
matching config dn or a descendent reloads config
*/
static int ipamodrdn_dn_is_config(char *dn)
{
int ret = 0;
LOG_TRACE("--in-->\n");
if (slapi_dn_issuffix(dn, getPluginDN())) {
ret = 1;
}
LOG_TRACE("<--out--\n");
return ret;
}
/****************************************************
Functions that actually do things other
than config and startup
****************************************************/
static int
ipamodrdn_change_attr(struct configEntry *cfgentry,
char *targetdn, const char *value)
{
Slapi_PBlock *mod_pb = slapi_pblock_new();
LDAPMod mod;
LDAPMod *mods[2];
char *val[2] = { NULL };
int ret;
val[0] = slapi_ch_smprintf("%s%s%s",
cfgentry->prefix, value, cfgentry->suffix);
if (!val[0]) {
LOG_OOM();
ret = EFAIL;
goto done;
}
val[1] = 0;
mod.mod_op = LDAP_MOD_REPLACE;
mod.mod_type = cfgentry->tattr;
mod.mod_values = val;
mods[0] = &mod;
mods[1] = 0;
LOG("Setting %s to %s in entry (%s)\n", cfgentry->tattr, value, targetdn);
/* Perform the modify operation. */
slapi_modify_internal_set_pb(mod_pb, targetdn, mods,
0, 0, getPluginID(), 0);
slapi_modify_internal_pb(mod_pb);
slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
if (ret != LDAP_SUCCESS) {
LOG_FATAL("Failed to change attribute with error %d\n", ret);
ret = EFAIL;
}
ret = EOK;
done:
if (val[0]) slapi_ch_free_string(&(val[0]));
slapi_pblock_destroy(mod_pb);
return ret;
}
/* for mods and adds:
where dn's are supplied, the closest in scope
is used as long as the type filter matches
and the attr value has not been generated yet.
*/
static int ipamodrdn_post_op(Slapi_PBlock *pb)
{
char *dn = NULL;
PRCList *list = NULL;
struct configEntry *cfgentry = NULL;
struct slapi_entry *e = NULL;
Slapi_Attr *sattr = NULL;
Slapi_Attr *tattr = NULL;
int ret = LDAP_SUCCESS;
LOG_TRACE("--in-->\n");
/* Just bail if we aren't ready to service requests yet. */
if (!g_plugin_started) {
goto done;
}
slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
if (NULL == e) {
goto done;
}
dn = slapi_entry_get_ndn(e);
if (NULL == dn) {
goto done;
}
ipamodrdn_read_lock();
if (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
list = PR_LIST_HEAD(ipamodrdn_global_config);
for(list = PR_LIST_HEAD(ipamodrdn_global_config);
list != ipamodrdn_global_config;
list = PR_NEXT_LINK(list)) {
cfgentry = (struct configEntry *) list;
/* is the entry in scope? */
if (cfgentry->scope) {
if (!slapi_dn_issuffix(dn, cfgentry->scope)) {
continue;
}
}
/* does the entry match the filter? */
if (cfgentry->slapi_filter) {
ret = slapi_vattr_filter_test(pb, e,
cfgentry->slapi_filter, 0);
if (ret != LDAP_SUCCESS) {
continue;
}
}
if (slapi_entry_attr_find(e, cfgentry->sattr, &sattr) != 0) {
LOG_TRACE("Source attr %s not found for %s\n",
cfgentry->sattr, dn);
continue;
}
if (slapi_entry_attr_find(e, cfgentry->tattr, &tattr) != 0) {
LOG_TRACE("Target attr %s not found for %s\n",
cfgentry->tattr, dn);
} else {
Slapi_Value *val;
const char *strval;
ret = slapi_attr_first_value(sattr, &val);
if (ret == -1 || !val) {
LOG_FATAL("Source attr %s is empty\n", cfgentry->sattr);
continue;
}
strval = slapi_value_get_string(val);
ret = ipamodrdn_change_attr(cfgentry, dn, strval);
if (ret != EOK) {
LOG_FATAL("Failed to set target attr %s for %s\n",
cfgentry->tattr, dn);
}
}
}
}
ipamodrdn_unlock();
ret = LDAP_SUCCESS;
done:
if (ret) {
LOG("operation failure [%d]\n", ret);
ret = EFAIL;
}
LOG_TRACE("<--out--\n");
return ret;
}
static int ipamodrdn_config_check_post_op(Slapi_PBlock * pb)
{
char *dn;
LOG_TRACE("--in-->\n");
if ((dn = ipamodrdn_get_dn(pb))) {
if (ipamodrdn_dn_is_config(dn)) {
ipamodrdn_load_plugin_config();
}
}
LOG_TRACE("<--out--\n");
return 0;
}

View File

@ -0,0 +1,16 @@
dn: cn=IPA MODRDN,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: IPA MODRDN
nsslapd-pluginpath: libipa_modrdn
nsslapd-plugininitfunc: ipamodrdn_init
nsslapd-plugintype: postoperation
nsslapd-pluginenabled: on
nsslapd-pluginid: ipamodrdn_version
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: Red Hat, Inc.
nsslapd-plugindescription: IPA MODRDN plugin
nsslapd-plugin-depends-on-type: database
nsslapd-pluginPrecedence: 60

View File

@ -0,0 +1,57 @@
NULL =
PLUGIN_COMMON_DIR=../common
KRB5_UTIL_DIR= ../../../util
KRB5_UTIL_SRCS=$(KRB5_UTIL_DIR)/ipa_krb5.c
INCLUDES = \
-I. \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-I$(KRB5_UTIL_DIR) \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(KRB5_CFLAGS) \
$(SSL_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_pwd_extop.la \
$(NULL)
libipa_pwd_extop_la_SOURCES = \
ipapwd_common.c \
ipapwd_encoding.c \
ipapwd_prepost.c \
ipa_pwd_extop.c \
$(KRB5_UTIL_SRCS) \
$(NULL)
libipa_pwd_extop_la_LDFLAGS = -avoid-version
libipa_pwd_extop_la_LIBADD = \
$(KRB5_LIBS) \
$(SSL_LIBS) \
$(LDAP_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
pwd-extop-conf.ldif \
$(NULL)
EXTRA_DIST = \
README \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,170 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Authors:
* Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2007-2010 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <prio.h>
#include <ssl.h>
#include <dirsrv/slapi-plugin.h>
#include <krb5.h>
#include <lber.h>
#include <time.h>
#include <iconv.h>
#include <openssl/des.h>
#include <openssl/md4.h>
#define IPAPWD_PLUGIN_NAME "ipa-pwd-extop"
#define IPAPWD_FEATURE_DESC "IPA Password Manager"
#define IPAPWD_PLUGIN_DESC "IPA Password Extended Operation plugin"
#define IPA_PLUGIN_NAME IPAPWD_PLUGIN_NAME
#define IPAPWD_CHECK_CONN_SECURE 0x00000001
#define IPAPWD_CHECK_DN 0x00000002
#define IPA_CHANGETYPE_NORMAL 0
#define IPA_CHANGETYPE_ADMIN 1
#define IPA_CHANGETYPE_DSMGR 2
struct ipapwd_data {
Slapi_Entry *target;
char *dn;
char *password;
time_t timeNow;
time_t lastPwChange;
time_t expireTime;
int changetype;
int pwHistoryLen;
};
struct ipapwd_operation {
struct ipapwd_data pwdata;
int pwd_op;
int is_krb;
};
#define GENERALIZED_TIME_LENGTH 15
#define IPAPWD_POLICY_MASK 0x0FF
#define IPAPWD_POLICY_ERROR 0x100
#define IPAPWD_POLICY_OK 0
/* from ipapwd_common.c */
struct ipapwd_encsalt {
krb5_int32 enc_type;
krb5_int32 salt_type;
};
struct ipapwd_krbcfg {
krb5_context krbctx;
char *realm;
krb5_keyblock *kmkey;
int num_supp_encsalts;
struct ipapwd_encsalt *supp_encsalts;
int num_pref_encsalts;
struct ipapwd_encsalt *pref_encsalts;
char **passsync_mgrs;
int num_passsync_mgrs;
bool allow_lm_hash;
bool allow_nt_hash;
};
int ipapwd_entry_checks(Slapi_PBlock *pb, struct slapi_entry *e,
int *is_root, int *is_krb, int *is_smb,
char *attr, int access);
int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
struct ipapwd_krbcfg **config, int check_flags);
int ipapwd_CheckPolicy(struct ipapwd_data *data);
int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist);
int ipapwd_get_cur_kvno(Slapi_Entry *target);
int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
struct ipapwd_data *data, int is_krb);
Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
struct ipapwd_data *data);
int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods);
int ipapwd_set_extradata(const char *dn,
const char *principal,
time_t unixtime);
void ipapwd_free_slapi_value_array(Slapi_Value ***svals);
void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg);
/* from ipapwd_encoding.c */
struct ipapwd_krbkeydata {
int32_t type;
struct berval value;
};
struct ipapwd_krbkey {
struct ipapwd_krbkeydata *salt;
struct ipapwd_krbkeydata *ekey;
struct berval s2kparams;
};
struct ipapwd_keyset {
uint16_t major_vno;
uint16_t minor_vno;
uint32_t kvno;
uint32_t mkvno;
struct ipapwd_krbkey *keys;
int num_keys;
};
void encode_int16(unsigned int val, unsigned char *p);
struct berval *encode_keys(struct ipapwd_keyset *kset);
void ipapwd_keyset_free(struct ipapwd_keyset **pkset);
int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
struct ipapwd_data *data, char *userpw,
int is_krb, int is_smb, Slapi_Value ***svals,
char **nthash, char **lmhash, char **errMesg);
/* from ipapwd_prepost.c */
int ipapwd_ext_init(void);
int ipapwd_pre_init(Slapi_PBlock *pb);
int ipapwd_post_init(Slapi_PBlock *pb);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,796 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Authors:
* Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2007-2010 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirsrv/slapi-plugin.h>
#include <lber.h>
#include <time.h>
#include "ipapwd.h"
#include "util.h"
#include "ipa_krb5.h"
/* krbTicketFlags */
#define KTF_DISALLOW_POSTDATED 0x00000001
#define KTF_DISALLOW_FORWARDABLE 0x00000002
#define KTF_DISALLOW_TGT_BASED 0x00000004
#define KTF_DISALLOW_RENEWABLE 0x00000008
#define KTF_DISALLOW_PROXIABLE 0x00000010
#define KTF_DISALLOW_DUP_SKEY 0x00000020
#define KTF_DISALLOW_ALL_TIX 0x00000040
#define KTF_REQUIRES_PRE_AUTH 0x00000080
#define KTF_REQUIRES_HW_AUTH 0x00000100
#define KTF_REQUIRES_PWCHANGE 0x00000200
#define KTF_DISALLOW_SVR 0x00001000
#define KTF_PWCHANGE_SERVICE 0x00002000
/* Salt types */
#define KRB5_KDB_SALTTYPE_NORMAL 0
#define KRB5_KDB_SALTTYPE_V4 1
#define KRB5_KDB_SALTTYPE_NOREALM 2
#define KRB5_KDB_SALTTYPE_ONLYREALM 3
#define KRB5_KDB_SALTTYPE_SPECIAL 4
#define KRB5_KDB_SALTTYPE_AFS3 5
#define KRB5P_SALT_SIZE 16
void krb5int_c_free_keyblock_contents(krb5_context context,
register krb5_keyblock *key);
/* Novell key-format scheme:
KrbKeySet ::= SEQUENCE {
attribute-major-vno [0] UInt16,
attribute-minor-vno [1] UInt16,
kvno [2] UInt32,
mkvno [3] UInt32 OPTIONAL,
keys [4] SEQUENCE OF KrbKey,
...
}
KrbKey ::= SEQUENCE {
salt [0] KrbSalt OPTIONAL,
key [1] EncryptionKey,
s2kparams [2] OCTET STRING OPTIONAL,
...
}
KrbSalt ::= SEQUENCE {
type [0] Int32,
salt [1] OCTET STRING OPTIONAL
}
EncryptionKey ::= SEQUENCE {
keytype [0] Int32,
keyvalue [1] OCTET STRING
}
*/
/* ascii hex output of bytes in "in"
* out len is 32 (preallocated)
* in len is 16 */
static const char hexchars[] = "0123456789ABCDEF";
static void hexbuf(char *out, const uint8_t *in)
{
int i;
for (i = 0; i < 16; i++) {
out[i*2] = hexchars[in[i] >> 4];
out[i*2+1] = hexchars[in[i] & 0x0f];
}
}
struct berval *encode_keys(struct ipapwd_keyset *kset)
{
BerElement *be = NULL;
struct berval *bval = NULL;
int ret, i;
be = ber_alloc_t(LBER_USE_DER);
if (!be) {
LOG_OOM();
return NULL;
}
ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{",
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
kset->major_vno,
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
kset->minor_vno,
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2),
kset->kvno,
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3),
kset->mkvno,
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4));
if (ret == -1) {
LOG_FATAL("encoding asn1 vno info failed\n");
goto done;
}
for (i = 0; i < kset->num_keys; i++) {
ret = ber_printf(be, "{");
if (ret == -1) {
LOG_FATAL("encoding asn1 EncryptionKey failed\n");
goto done;
}
if (kset->keys[i].salt) {
ret = ber_printf(be, "t[{t[i]",
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
kset->keys[i].salt->type);
if ((ret != -1) && kset->keys[i].salt->value.bv_len) {
ret = ber_printf(be, "t[o]",
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
kset->keys[i].salt->value.bv_val,
kset->keys[i].salt->value.bv_len);
}
if (ret != -1) {
ret = ber_printf(be, "}]");
}
if (ret == -1) {
goto done;
}
}
ret = ber_printf(be, "t[{t[i]t[o]}]",
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
kset->keys[i].ekey->type,
(ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
kset->keys[i].ekey->value.bv_val,
kset->keys[i].ekey->value.bv_len);
if (ret == -1) {
LOG_FATAL("encoding asn1 EncryptionKey failed\n");
goto done;
}
/* FIXME: s2kparams not supported yet */
ret = ber_printf(be, "}");
if (ret == -1) {
LOG_FATAL("encoding asn1 EncryptionKey failed\n");
goto done;
}
}
ret = ber_printf(be, "}]}");
if (ret == -1) {
LOG_FATAL("encoding asn1 end of sequences failed\n");
goto done;
}
ret = ber_flatten(be, &bval);
if (ret == -1) {
LOG_FATAL("flattening asn1 failed\n");
goto done;
}
done:
ber_free(be, 1);
return bval;
}
void ipapwd_keyset_free(struct ipapwd_keyset **pkset)
{
struct ipapwd_keyset *kset = *pkset;
int i;
if (!kset) return;
for (i = 0; i < kset->num_keys; i++) {
if (kset->keys[i].salt) {
free(kset->keys[i].salt->value.bv_val);
free(kset->keys[i].salt);
}
if (kset->keys[i].ekey) {
free(kset->keys[i].ekey->value.bv_val);
free(kset->keys[i].ekey);
}
free(kset->keys[i].s2kparams.bv_val);
}
free(kset->keys);
free(kset);
*pkset = NULL;
}
void encode_int16(unsigned int val, unsigned char *p)
{
p[1] = (val >> 8) & 0xff;
p[0] = (val ) & 0xff;
}
static Slapi_Value **encrypt_encode_key(struct ipapwd_krbcfg *krbcfg,
struct ipapwd_data *data,
char **errMesg)
{
krb5_context krbctx;
char *krbPrincipalName = NULL;
uint32_t krbMaxTicketLife;
int kvno, i;
int krbTicketFlags;
struct berval *bval = NULL;
Slapi_Value **svals = NULL;
krb5_principal princ = NULL;
krb5_error_code krberr;
krb5_data pwd;
struct ipapwd_keyset *kset = NULL;
krbctx = krbcfg->krbctx;
svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *));
if (!svals) {
LOG_OOM();
return NULL;
}
kvno = ipapwd_get_cur_kvno(data->target);
krbPrincipalName = slapi_entry_attr_get_charptr(data->target,
"krbPrincipalName");
if (!krbPrincipalName) {
*errMesg = "no krbPrincipalName present in this entry\n";
LOG_FATAL("%s", *errMesg);
goto enc_error;
}
krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ);
if (krberr) {
LOG_FATAL("krb5_parse_name failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
goto enc_error;
}
krbMaxTicketLife = slapi_entry_attr_get_uint(data->target,
"krbMaxTicketLife");
if (krbMaxTicketLife == 0) {
/* FIXME: retrieve the default from config (max_life from kdc.conf) */
krbMaxTicketLife = 86400; /* just set the default 24h for now */
}
krbTicketFlags = slapi_entry_attr_get_int(data->target,
"krbTicketFlags");
pwd.data = (char *)data->password;
pwd.length = strlen(data->password);
kset = malloc(sizeof(struct ipapwd_keyset));
if (!kset) {
LOG_OOM();
goto enc_error;
}
/* this encoding assumes all keys have the same kvno */
/* major-vno = 1 and minor-vno = 1 */
kset->major_vno = 1;
kset->minor_vno = 1;
/* increment kvno (will be 1 if this is a new entry) */
kset->kvno = kvno + 1;
/* we also assum mkvno is 0 */
kset->mkvno = 0;
kset->num_keys = krbcfg->num_pref_encsalts;
kset->keys = calloc(kset->num_keys, sizeof(struct ipapwd_krbkey));
if (!kset->keys) {
LOG_OOM();
goto enc_error;
}
for (i = 0; i < kset->num_keys; i++) {
krb5_keyblock key;
krb5_data salt;
krb5_octet *ptr;
krb5_data plain;
krb5_enc_data cipher;
size_t len;
const char *p;
salt.data = NULL;
switch (krbcfg->pref_encsalts[i].salt_type) {
case KRB5_KDB_SALTTYPE_ONLYREALM:
p = strchr(krbPrincipalName, '@');
if (!p) {
LOG_FATAL("Invalid principal name, no realm found!\n");
goto enc_error;
}
p++;
salt.data = strdup(p);
if (!salt.data) {
LOG_OOM();
goto enc_error;
}
salt.length = strlen(salt.data); /* final \0 omitted on purpose */
break;
case KRB5_KDB_SALTTYPE_NOREALM:
krberr = ipa_krb5_principal2salt_norealm(krbctx, princ, &salt);
if (krberr) {
LOG_FATAL("krb5_principal2salt failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
goto enc_error;
}
break;
case KRB5_KDB_SALTTYPE_NORMAL:
krberr = krb5_principal2salt(krbctx, princ, &salt);
if (krberr) {
LOG_FATAL("krb5_principal2salt failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
goto enc_error;
}
break;
case KRB5_KDB_SALTTYPE_SPECIAL:
/* make random salt */
salt.length = KRB5P_SALT_SIZE;
salt.data = malloc(KRB5P_SALT_SIZE);
if (!salt.data) {
LOG_OOM();
goto enc_error;
}
krberr = krb5_c_random_make_octets(krbctx, &salt);
if (krberr) {
LOG_FATAL("krb5_c_random_make_octets failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
goto enc_error;
}
break;
case KRB5_KDB_SALTTYPE_V4:
salt.length = 0;
break;
case KRB5_KDB_SALTTYPE_AFS3:
p = strchr(krbPrincipalName, '@');
if (!p) {
LOG_FATAL("Invalid principal name, no realm found!\n");
goto enc_error;
}
p++;
salt.data = strdup(p);
if (!salt.data) {
LOG_OOM();
goto enc_error;
}
salt.length = SALT_TYPE_AFS_LENGTH; /* special value */
break;
default:
LOG_FATAL("Invalid salt type [%d]\n",
krbcfg->pref_encsalts[i].salt_type);
goto enc_error;
}
/* need to build the key now to manage the AFS salt.length
* special case */
krberr = krb5_c_string_to_key(krbctx,
krbcfg->pref_encsalts[i].enc_type,
&pwd, &salt, &key);
if (krberr) {
LOG_FATAL("krb5_c_string_to_key failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
krb5_free_data_contents(krbctx, &salt);
goto enc_error;
}
if (salt.length == SALT_TYPE_AFS_LENGTH) {
salt.length = strlen(salt.data);
}
krberr = krb5_c_encrypt_length(krbctx,
krbcfg->kmkey->enctype,
key.length, &len);
if (krberr) {
LOG_FATAL("krb5_c_string_to_key failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
krb5int_c_free_keyblock_contents(krbctx, &key);
krb5_free_data_contents(krbctx, &salt);
goto enc_error;
}
if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) {
LOG_OOM();
krb5int_c_free_keyblock_contents(krbctx, &key);
krb5_free_data_contents(krbctx, &salt);
goto enc_error;
}
encode_int16(key.length, ptr);
plain.length = key.length;
plain.data = (char *)key.contents;
cipher.ciphertext.length = len;
cipher.ciphertext.data = (char *)ptr+2;
krberr = krb5_c_encrypt(krbctx, krbcfg->kmkey, 0, 0, &plain, &cipher);
if (krberr) {
LOG_FATAL("krb5_c_encrypt failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
krb5int_c_free_keyblock_contents(krbctx, &key);
krb5_free_data_contents(krbctx, &salt);
free(ptr);
goto enc_error;
}
/* KrbSalt */
kset->keys[i].salt = malloc(sizeof(struct ipapwd_krbkeydata));
if (!kset->keys[i].salt) {
LOG_OOM();
krb5int_c_free_keyblock_contents(krbctx, &key);
free(ptr);
goto enc_error;
}
kset->keys[i].salt->type = krbcfg->pref_encsalts[i].salt_type;
if (salt.length) {
kset->keys[i].salt->value.bv_len = salt.length;
kset->keys[i].salt->value.bv_val = salt.data;
}
/* EncryptionKey */
kset->keys[i].ekey = malloc(sizeof(struct ipapwd_krbkeydata));
if (!kset->keys[i].ekey) {
LOG_OOM();
krb5int_c_free_keyblock_contents(krbctx, &key);
free(ptr);
goto enc_error;
}
kset->keys[i].ekey->type = key.enctype;
kset->keys[i].ekey->value.bv_len = len+2;
kset->keys[i].ekey->value.bv_val = malloc(len+2);
if (!kset->keys[i].ekey->value.bv_val) {
LOG_OOM();
krb5int_c_free_keyblock_contents(krbctx, &key);
free(ptr);
goto enc_error;
}
memcpy(kset->keys[i].ekey->value.bv_val, ptr, len+2);
/* make sure we free the memory used now that we are done with it */
krb5int_c_free_keyblock_contents(krbctx, &key);
free(ptr);
}
bval = encode_keys(kset);
if (!bval) {
LOG_FATAL("encoding asn1 KrbSalt failed\n");
goto enc_error;
}
svals[0] = slapi_value_new_berval(bval);
if (!svals[0]) {
LOG_FATAL("Converting berval to Slapi_Value\n");
goto enc_error;
}
ipapwd_keyset_free(&kset);
krb5_free_principal(krbctx, princ);
slapi_ch_free_string(&krbPrincipalName);
ber_bvfree(bval);
return svals;
enc_error:
*errMesg = "key encryption/encoding failed\n";
if (kset) ipapwd_keyset_free(&kset);
krb5_free_principal(krbctx, princ);
slapi_ch_free_string(&krbPrincipalName);
if (bval) ber_bvfree(bval);
free(svals);
return NULL;
}
#define KTF_DOS_CHARSET "CP850" /* same default as samba */
#define KTF_UTF8 "UTF-8"
#define KTF_UCS2 "UCS-2LE"
static const uint8_t parity_table[128] = {
1, 2, 4, 7, 8, 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31,
32, 35, 37, 38, 41, 42, 44, 47, 49, 50, 52, 55, 56, 59, 61, 62,
64, 67, 69, 70, 73, 74, 76, 79, 81, 82, 84, 87, 88, 91, 93, 94,
97, 98,100,103,104,107,109,110,112,115,117,118,121,122,124,127,
128,131,133,134,137,138,140,143,145,146,148,151,152,155,157,158,
161,162,164,167,168,171,173,174,176,179,181,182,185,186,188,191,
193,194,196,199,200,203,205,206,208,211,213,214,217,218,220,223,
224,227,229,230,233,234,236,239,241,242,244,247,248,251,253,254
};
static void lm_shuffle(uint8_t *out, uint8_t *in)
{
out[0] = parity_table[in[0]>>1];
out[1] = parity_table[((in[0]<<6)|(in[1]>>2)) & 0x7F];
out[2] = parity_table[((in[1]<<5)|(in[2]>>3)) & 0x7F];
out[3] = parity_table[((in[2]<<4)|(in[3]>>4)) & 0x7F];
out[4] = parity_table[((in[3]<<3)|(in[4]>>5)) & 0x7F];
out[5] = parity_table[((in[4]<<2)|(in[5]>>6)) & 0x7F];
out[6] = parity_table[((in[5]<<1)|(in[6]>>7)) & 0x7F];
out[7] = parity_table[in[6] & 0x7F];
}
struct ntlm_keys {
uint8_t lm[16];
uint8_t nt[16];
};
/* create the lm and nt hashes
newPassword: the clear text utf8 password
do_lm_hash: determine if LM hash is generated
do_nt_hash: determine if NT hash is generated
keys[out]: array with generated hashes
*/
static int encode_ntlm_keys(char *newPasswd,
bool do_lm_hash,
bool do_nt_hash,
struct ntlm_keys *keys)
{
int ret = 0;
/* do lanman first */
if (do_lm_hash) {
iconv_t cd;
size_t cs, il, ol;
char *inc, *outc;
char *upperPasswd;
char *asciiPasswd;
DES_key_schedule schedule;
DES_cblock deskey;
DES_cblock magic = "KGS!@#$%";
/* TODO: must store the dos charset somewhere in the directory */
cd = iconv_open(KTF_DOS_CHARSET, KTF_UTF8);
if (cd == (iconv_t)(-1)) {
ret = -1;
goto done;
}
/* the lanman password is upper case */
upperPasswd = (char *)slapi_utf8StrToUpper((unsigned char *)newPasswd);
if (!upperPasswd) {
iconv_close(cd);
ret = -1;
goto done;
}
il = strlen(upperPasswd);
/* an ascii string can only be smaller than or equal to an utf8 one */
ol = il;
if (ol < 14) ol = 14;
asciiPasswd = calloc(ol+1, 1);
if (!asciiPasswd) {
slapi_ch_free_string(&upperPasswd);
iconv_close(cd);
ret = -1;
goto done;
}
inc = upperPasswd;
outc = asciiPasswd;
cs = iconv(cd, &inc, &il, &outc, &ol);
if (cs == -1) {
ret = -1;
slapi_ch_free_string(&upperPasswd);
free(asciiPasswd);
iconv_close(cd);
goto done;
}
/* done with these */
slapi_ch_free_string(&upperPasswd);
iconv_close(cd);
/* we are interested only in the first 14 ASCII chars for lanman */
if (strlen(asciiPasswd) > 14) {
asciiPasswd[14] = '\0';
}
/* first half */
lm_shuffle(deskey, (uint8_t *)asciiPasswd);
DES_set_key_unchecked(&deskey, &schedule);
DES_ecb_encrypt(&magic, (DES_cblock *)keys->lm,
&schedule, DES_ENCRYPT);
/* second half */
lm_shuffle(deskey, (uint8_t *)&asciiPasswd[7]);
DES_set_key_unchecked(&deskey, &schedule);
DES_ecb_encrypt(&magic, (DES_cblock *)&(keys->lm[8]),
&schedule, DES_ENCRYPT);
/* done with it */
free(asciiPasswd);
} else {
memset(keys->lm, 0, 16);
}
if (do_nt_hash) {
iconv_t cd;
size_t cs, il, ol, sl;
char *inc, *outc;
char *ucs2Passwd;
MD4_CTX md4ctx;
/* TODO: must store the dos charset somewhere in the directory */
cd = iconv_open(KTF_UCS2, KTF_UTF8);
if (cd == (iconv_t)(-1)) {
ret = -1;
goto done;
}
il = strlen(newPasswd);
/* an ucs2 string can be at most double than an utf8 one */
sl = ol = (il+1)*2;
ucs2Passwd = calloc(ol, 1);
if (!ucs2Passwd) {
ret = -1;
iconv_close(cd);
goto done;
}
inc = newPasswd;
outc = ucs2Passwd;
cs = iconv(cd, &inc, &il, &outc, &ol);
if (cs == -1) {
ret = -1;
free(ucs2Passwd);
iconv_close(cd);
goto done;
}
/* done with it */
iconv_close(cd);
/* get the final ucs2 string length */
sl -= ol;
ret = MD4_Init(&md4ctx);
if (ret == 0) {
ret = -1;
free(ucs2Passwd);
goto done;
}
ret = MD4_Update(&md4ctx, ucs2Passwd, sl);
if (ret == 0) {
ret = -1;
free(ucs2Passwd);
goto done;
}
ret = MD4_Final(keys->nt, &md4ctx);
if (ret == 0) {
ret = -1;
free(ucs2Passwd);
goto done;
}
} else {
memset(keys->nt, 0, 16);
}
ret = 0;
done:
return ret;
}
int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
struct ipapwd_data *data, char *userpw,
int is_krb, int is_smb, Slapi_Value ***svals,
char **nthash, char **lmhash, char **errMesg)
{
int rc;
*svals = NULL;
*nthash = NULL;
*lmhash = NULL;
*errMesg = NULL;
if (is_krb) {
*svals = encrypt_encode_key(krbcfg, data, errMesg);
if (!*svals) {
/* errMesg should have been set in encrypt_encode_key() */
LOG_FATAL("key encryption/encoding failed\n");
rc = LDAP_OPERATIONS_ERROR;
goto done;
}
}
if (is_smb) {
char lm[33], nt[33];
struct ntlm_keys ntlm;
int ret;
ret = encode_ntlm_keys(userpw,
krbcfg->allow_lm_hash,
krbcfg->allow_nt_hash,
&ntlm);
if (ret) {
*errMesg = "Failed to generate NT/LM hashes\n";
LOG_FATAL("%s", *errMesg);
rc = LDAP_OPERATIONS_ERROR;
goto done;
}
if (krbcfg->allow_lm_hash) {
hexbuf(lm, ntlm.lm);
lm[32] = '\0';
*lmhash = slapi_ch_strdup(lm);
}
if (krbcfg->allow_nt_hash) {
hexbuf(nt, ntlm.nt);
nt[32] = '\0';
*nthash = slapi_ch_strdup(nt);
}
}
rc = LDAP_SUCCESS;
done:
/* when error, free possibly allocated output parameters */
if (rc) {
ipapwd_free_slapi_value_array(svals);
}
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
dn: cn=ipa_pwd_extop,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: ipa_pwd_extop
nsslapd-pluginpath: libipa_pwd_extop
nsslapd-plugininitfunc: ipapwd_init
nsslapd-plugintype: extendedop
nsslapd-pluginenabled: on
nsslapd-pluginid: ipa_pwd_extop
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: RedHat
nsslapd-plugindescription: Support saving passwords in multiple formats for different consumers (krb5, samba, freeradius, etc.)
nsslapd-plugin-depends-on-type: database
nsslapd-realmTree: $SUFFIX

View File

@ -0,0 +1,47 @@
NULL =
PLUGIN_COMMON_DIR=../common
INCLUDES = \
-I. \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-I/usr/include/dirsrv \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_uuid.la \
$(NULL)
libipa_uuid_la_SOURCES = \
ipa_uuid.c \
$(NULL)
libipa_uuid_la_LDFLAGS = -avoid-version
libipa_uuid_la_LIBADD = \
$(LDAP_LIBS) \
$(UUID_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
uuid-conf.ldif \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
dn: cn=IPA UUID,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: IPA UUID
nsslapd-pluginpath: libipa_uuid
nsslapd-plugininitfunc: ipauuid_init
nsslapd-plugintype: preoperation
nsslapd-pluginenabled: on
nsslapd-pluginid: ipauuid_version
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: Red Hat, Inc.
nsslapd-plugindescription: IPA UUID plugin
nsslapd-plugin-depends-on-type: database

View File

@ -0,0 +1,48 @@
NULL =
PLUGIN_COMMON_DIR=../common
INCLUDES = \
-I. \
-I../../ \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-I/usr/include/dirsrv \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(KRB5_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_repl_version.la \
$(NULL)
libipa_repl_version_la_SOURCES = \
ipa_repl_version.c \
$(NULL)
libipa_repl_version_la_LDFLAGS = -avoid-version
libipa_repl_version_la_LIBADD = \
$(LDAP_LIBS) \
$(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
version-conf.ldif \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,199 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Copyright (C) 2005 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#include "slapi-plugin.h"
#include "repl-session-plugin.h"
#include "ipa-version.h"
#include "util.h"
#include <string.h>
/* Identify the type of data we're sending, an unsigned int in this case */
#define REPL_VERSION_DATA_GUID "2D562D8B-2F30-4447-AF76-2B721D1D5F6A"
#define IPA_PLUGIN_NAME "ipa_replication_version"
static char *data_version = NULL;
/*
* Plugin identifiers
*/
static Slapi_PluginDesc repl_version_pdesc = {
"ipa-repl-version-plugin",
"Red Hat, Inc.",
"1.0",
"IPA Replication version plugin"
};
static Slapi_ComponentId *repl_version_plugin_id = NULL;
/*
* Replication Version Callbacks
*/
/*
* This is called on a master when we are about to acquire a
* replica.
*
* Returning non-0 will abort the replication session. This
* results in the master going into incremental backoff mode.
*/
static int
repl_version_plugin_pre_acquire_cb(void *cookie, const Slapi_DN *repl_subtree,
int is_total, char **data_guid, struct berval **data)
{
LOG("repl_version_plugin_pre_acquire_cb() called for suffix \"%s\", "
"is_total: \"%s\".\n", slapi_sdn_get_ndn(repl_subtree),
is_total ? "TRUE" : "FALSE");
/* allocate some data to be sent to the replica */
*data_guid = slapi_ch_smprintf("%s", REPL_VERSION_DATA_GUID);
*data = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
(*data)->bv_val = slapi_ch_smprintf("%s", data_version);
(*data)->bv_len = strlen((*data)->bv_val) + 1;
LOG("repl_version_plugin_pre_acquire_cb() sending data: guid: \"%s\" data: \"%s\".\n",
*data_guid, (*data)->bv_val);
return 0;
}
/*
* This is called on a replica when it receives a start replication
* extended operation from a master.
*
* The data sent by the master (version) is compared with our own
* hardcoded version to determine if replication can proceed or not.
*
* The replication plug-in will take care of freeing data_guid and data.
*
* Returning non-0 will abort the replication session. This
* results in the master going into incremental backoff mode.
*/
static int
repl_version_plugin_recv_acquire_cb(const char *repl_subtree, int is_total,
const char *data_guid, const struct berval *data)
{
LOG("test_repl_session_plugin_recv_acquire_cb() called for suffix \"%s\", is_total: \"%s\".\n",
repl_subtree, is_total ? "TRUE" : "FALSE");
/* compare our data version to the master data version */
if (data_guid && data && (strcmp(data_guid, REPL_VERSION_DATA_GUID) == 0)) {
LOG("repl_version_plugin_recv_acquire_cb() received data: guid: \"%s\" data: \"%s\".\n",
data_guid, data->bv_val);
if (!(strcmp(data_version, data->bv_val) == 0)) {
LOG_FATAL("Incompatible IPA versions, pausing replication. "
"This server: \"%s\" remote server: \"%s\".\n",
data_version, data->bv_val);
return 1;
}
}
return 0;
}
/*
* Callback list for registering API
*/
static void *repl_version_api[] = {
NULL, /* reserved for api broker use, must be zero */
NULL, /* init cb */
repl_version_plugin_pre_acquire_cb,
NULL, /* reply_acquire_cb */
NULL, /* post_acquire_cb */
repl_version_plugin_recv_acquire_cb,
NULL /* destroy cb */
};
/*
* Plug-in framework functions
*/
static int
repl_version_plugin_start(Slapi_PBlock *pb)
{
LOG("--> repl_version_plugin_start -- begin\n");
data_version = slapi_ch_smprintf("%llu", (unsigned long long) DATA_VERSION);
LOG("<-- repl_version_plugin_start -- end\n");
return 0;
}
static int
repl_version_plugin_close(Slapi_PBlock *pb)
{
LOG("--> repl_version_plugin_close -- begin\n");
slapi_apib_unregister(REPL_SESSION_v1_0_GUID);
slapi_ch_free_string(&data_version);
LOG("<-- repl_version_plugin_close -- end\n");
return 0;
}
int repl_version_plugin_init(Slapi_PBlock *pb)
{
LOG("--> repl_version_plugin_init -- begin\n");
if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_VERSION_01 ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
(void *) repl_version_plugin_start ) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
(void *) repl_version_plugin_close ) != 0 ||
slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
(void *)&repl_version_pdesc ) != 0 )
{
LOG_FATAL("<-- repl_version_plugin_init -- failed to register plugin -- end\n");
return -1;
}
if( slapi_apib_register(REPL_SESSION_v1_0_GUID, repl_version_api) ) {
LOG_FATAL("<-- repl_version_plugin_start -- failed to register repl_version api -- end\n");
return -1;
}
/* Retrieve and save the plugin identity to later pass to
internal operations */
if (slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &repl_version_plugin_id) != 0) {
LOG_FATAL("<-- repl_version_plugin_init -- failed to retrieve plugin identity -- end\n");
return -1;
}
LOG("<-- repl_version_plugin_init -- end\n");
return 0;
}

View File

@ -0,0 +1,17 @@
dn: cn=IPA Version Replication,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: IPA Version Replication
nsslapd-pluginpath: libipa_repl_version
nsslapd-plugininitfunc: repl_version_plugin_init
nsslapd-plugintype: preoperation
nsslapd-pluginenabled: off
nsslapd-pluginid: ipa_repl_version
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: Red Hat, Inc.
nsslapd-plugindescription: IPA Replication version plugin
nsslapd-plugin-depends-on-type: database
nsslapd-plugin-depends-on-named: Multimaster Replication Plugin

View File

@ -0,0 +1,47 @@
NULL =
PLUGIN_COMMON_DIR=../common
INCLUDES = \
-I. \
-I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(WARN_CFLAGS) \
$(NULL)
plugindir = $(libdir)/dirsrv/plugins
plugin_LTLIBRARIES = \
libipa_winsync.la \
$(NULL)
libipa_winsync_la_SOURCES = \
ipa-winsync.c \
ipa-winsync-config.c \
$(NULL)
libipa_winsync_la_LDFLAGS = -avoid-version
#libipa_winsync_la_LIBADD = \
# $(LDAP_LIBS) \
# $(NULL)
appdir = $(IPA_DATA_DIR)
app_DATA = \
ipa-winsync-conf.ldif \
$(NULL)
EXTRA_DIST = \
README \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,28 @@
dn: cn=ipa-winsync,cn=plugins,cn=config
changetype: add
objectclass: top
objectclass: nsSlapdPlugin
objectclass: extensibleObject
cn: ipa-winsync
nsslapd-pluginpath: libipa_winsync
nsslapd-plugininitfunc: ipa_winsync_plugin_init
nsslapd-pluginDescription: Allows IPA to work with the DS windows sync feature
nsslapd-pluginid: ipa-winsync
nsslapd-pluginversion: 1.0
nsslapd-pluginvendor: Red Hat
nsslapd-plugintype: preoperation
nsslapd-pluginenabled: on
nsslapd-plugin-depends-on-type: database
ipaWinSyncRealmFilter: (objectclass=krbRealmContainer)
ipaWinSyncRealmAttr: cn
ipaWinSyncNewEntryFilter: (cn=ipaConfig)
ipaWinSyncNewUserOCAttr: ipauserobjectclasses
ipaWinSyncUserFlatten: true
ipaWinsyncHomeDirAttr: ipaHomesRootDir
ipaWinsyncLoginShellAttr: ipaDefaultLoginShell
ipaWinSyncDefaultGroupAttr: ipaDefaultPrimaryGroup
ipaWinSyncDefaultGroupFilter: (gidNumber=*)(objectclass=posixGroup)(objectclass=groupOfNames)
ipaWinSyncAcctDisable: both
ipaWinSyncForceSync: true
ipaWinSyncUserAttr: uidNumber 999
ipaWinSyncUserAttr: gidNumber 999

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,170 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Authors:
* Rich Megginson <rmeggins@redhat.com>
*
* Copyright (C) 2008 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#ifndef IPA_WINSYNC_H
#define IPA_WINSYNC_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef WINSYNC_TEST_IPA
#include <slapi-plugin.h>
#include "winsync-plugin.h"
#else /* the default */
#include <dirsrv/slapi-plugin.h>
#include <dirsrv/winsync-plugin.h>
#endif /* WINSYNC_TEST_IPA */
#include <string.h>
#include <strings.h>
#include "util.h"
#define IPA_PLUGIN_NAME "ipa-winsync"
typedef struct ipa_winsync_config_struct {
Slapi_Mutex *lock; /* for config access */
Slapi_Entry *config_e; /* configuration entry */
PRBool flatten; /* flatten AD DNs */
char *realm_filter;
char *realm_attr;
char *new_entry_filter;
char *new_user_oc_attr; /* don't care about groups for now */
char *homedir_prefix_attr;
char *login_shell_attr;
char *default_group_attr;
char *default_group_filter;
int acct_disable; /* see below for possible values */
char *inactivated_filter;
char *activated_filter;
PRBool forceSync;
} IPA_WinSync_Config;
/*
This is the structure that holds our domain
specific configuration
*/
typedef struct ipa_winsync_domain_config {
Slapi_Entry *domain_e; /* info is stored in this entry */
char *realm_name; /* realm name */
char *homedir_prefix;
char *login_shell;
char *inactivated_group_dn; /* DN of inactivated group */
char *activated_group_dn; /* DN of activated group */
} IPA_WinSync_Domain_Config;
void ipa_winsync_set_plugin_identity(void * identity);
void * ipa_winsync_get_plugin_identity(void);
int ipa_winsync_config( Slapi_Entry *config_e );
IPA_WinSync_Config *ipa_winsync_get_config( void );
/*
* Agreement/domain specific configuration
*/
/* return a new domain specific configuration object */
void *ipa_winsync_config_new_domain(const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree);
/* refresh the domain specific configuration object */
void ipa_winsync_config_refresh_domain(void *cbdata, const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree);
/* destroy the domain specific configuration object */
void ipa_winsync_config_destroy_domain(void *cbdata, const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree);
/* name of attribute holding the filter to use to
find the ipa realm value
*/
#define IPA_WINSYNC_REALM_FILTER_ATTR "ipaWinSyncRealmFilter"
/* name of attribute holding the name of the attribute
which contains the ipa realm value
*/
#define IPA_WINSYNC_REALM_ATTR_ATTR "ipaWinSyncRealmAttr"
/* name of attribute holding the filter to use to
find the new user template entry
*/
#define IPA_WINSYNC_NEW_ENTRY_FILTER_ATTR "ipaWinSyncNewEntryFilter"
/* name of attribute holding the name of the attribute
in the new user template entry which has the list of objectclasses
*/
#define IPA_WINSYNC_NEW_USER_OC_ATTR "ipaWinSyncNewUserOCAttr"
/* name of attribute holding the new user attributes and values */
#define IPA_WINSYNC_NEW_USER_ATTRS_VALS "ipaWinSyncUserAttr"
/* name of attribute holding the name of the attribute which
has the homeDirectory prefix - suffix is the uid */
#define IPA_WINSYNC_HOMEDIR_PREFIX_ATTR "ipaWinSyncHomeDirAttr"
/* name of attribute holding the name of the attribute which
has the loginShell value */
#define IPA_WINSYNC_LOGIN_SHELL_ATTR "ipaWinSyncLoginShellAttr"
/* name of attribute holding the name of the attribute which is
used to get the default posix gidNumber */
#define IPA_WINSYNC_DEFAULTGROUP_ATTR "ipaWinSyncDefaultGroupAttr"
/* filter used to find the group with the gid number whose group name
is in the IPA_WINSYNC_DEFAULTGROUP_ATTR - the filter will have
cn=valueofIPA_WINSYNC_DEFAULTGROUP_ATTR appended to it */
#define IPA_WINSYNC_DEFAULTGROUP_FILTER_ATTR "ipaWinSyncDefaultGroupFilter"
/* name of attribute holding boolean value to flatten user dns or not */
#define IPA_WINSYNC_USER_FLATTEN "ipaWinSyncUserFlatten"
/* name of attribute holding account disable sync value */
#define IPA_WINSYNC_ACCT_DISABLE "ipaWinSyncAcctDisable"
/* possible values of IPA_WINSYNC_ACCT_DISABLE */
#define IPA_WINSYNC_ACCT_DISABLE_NONE "none"
#define IPA_WINSYNC_ACCT_DISABLE_TO_AD "to_ad"
#define IPA_WINSYNC_ACCT_DISABLE_TO_DS "to_ds"
#define IPA_WINSYNC_ACCT_DISABLE_BOTH "both"
/* enum representing the values above */
enum {
ACCT_DISABLE_INVALID, /* the invalid value */
ACCT_DISABLE_NONE, /* do not sync acct disable status */
ACCT_DISABLE_TO_AD, /* sync only from ds to ad */
ACCT_DISABLE_TO_DS, /* sync only from ad to ds */
ACCT_DISABLE_BOTH /* bi-directional sync */
};
/* name of attributes holding the search filters to use to find
the DN of the groups that represent inactivated and activated users */
#define IPA_WINSYNC_INACTIVATED_FILTER "ipaWinSyncInactivatedFilter"
#define IPA_WINSYNC_ACTIVATED_FILTER "ipaWinSyncActivatedFilter"
/* name of attribute holding the value of the forceSync parameter -
this is a boolean attribute - if true, all users in AD that have
a corresponding entry in the DS will be synced - there will be no
way to "turn off sync" on individual entries - if this value is
false, only users which have the ntUser objectclass and an
ntDomainUserID attribute which corresponds to an AD account
with the same value for samAccountName will be synced
*/
#define IPA_WINSYNC_FORCE_SYNC "ipaWinSyncForceSync"
#endif /* IPA_WINSYNC_H */

48
daemons/ipa-version.h.in Normal file
View File

@ -0,0 +1,48 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Copyright (C) 2010 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
/* The full version including strings */
#define VERSION "__VERSION__"
/* Just the numeric portion of the version so one can do direct numeric
comparisons to see if the API is compatible.
*/
#define NUM_VERSION __NUM_VERSION__
/* The data version. This is distinguished from the server version based
on the compatibility of the data/plugins/etc from one version to another.
*/
#define DATA_VERSION __DATA_VERSION__

437
doc/examples/examples.py Normal file
View File

@ -0,0 +1,437 @@
# Authors:
# Pavel Zuna <pzuna@redhat.com>
#
# Copyright (C) 2010 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Example plugins
"""
# Hey guys, so you're interested in writing plugins for IPA? Great!
# We compiled this small file with examples on how to extend IPA to suit
# your needs. We'll be going from very simple to pretty complex plugins
# hopefully covering most of what our framework has to offer.
# First, let's import some stuff.
# api is an object containing references to all plugins and useful classes.
# errors is a module containing all IPA specific exceptions.
from ipalib import api, errors
# Command is the base class for command plugin.
from ipalib import Command
# Str is a subclass of Param, it is used to define string parameters for
# command. We'll go through all other subclasses of Param supported by IPA
# later in this file
from ipalib import Str
# output is a module containing the most common output patterns.
# Command plugin do output validation based on these patterns.
# You can define your own as we're going to show you later.
from ipalib import output
# We're going to create an example command plugin, that takes a name as its
# only argument. Commands in IPA support input validation by defining
# functions we're going to call 'validators'. This is an example of such
# function:
def validate_name(ugettext, name):
"""
Validate names for the exhelloworld command. Names starting with 'Y'
(picked at random) are considered invalid.
"""
if name.startswith('Y'):
raise errors.ValidationError(
name='name',
error='Names starting with \'Y\' are invalid!'
)
# If the validator doesn't return anything (i.e. it returns None),
# the parameter passes validation.
class exhelloworld(Command):
"""
Example command: Hello world!
"""
# takes_args is an attribute of Command. It's a tuple containing
# instances of Param (or its subclasses such as Str) that define
# what position arguments are accepted by the command.
takes_args = (
# The first argument of Param constructor is the name that will be
# used to identify this parameter. It can be followed by validator
# functions. The constructor can also take a bunch of keyword
# arguments. Here we use default, to set the parameters default value
# and autofill, that fills the default value if the parameter isn't
# present.
# Note the ? at the end of the parameter name. It makes the parameter
# optional.
Str('name?', validate_name,
default=u'anonymous coward',
autofill=True,
),
)
# has_output is an attribute of Command, it is a tuple containing
# output.Output instances that define its output pattern.
# Commands in IPA return dicts with keys corresponding to items
# in the has_output tuple.
has_output = (
# output.summary is one of the basic patterns.
# It's a string that should be filled with a user-friendly
# decription of the action performed by the command.
output.summary,
)
# Every command needs to override the execute method.
# This is where the command functionality should go.
# It is always executed on the server-side, so don't rely
# on client-side stuff in here!
def execute(self, name):
return dict(summary='Hello world, %s!' % name)
# register the command, uncomment this line if you want to try it out
#api.register(exhelloworld)
# Anyway, that was a pretty bad example of a command or, to be more precise,
# a bad example of resource use. When a client executes a command locally, its
# name and parameters are transfered to the server over XML-RPC. The command
# execute method is then executed on the server and results are transfered
# back to the client. The command does nothing, but create a string - a task
# that could be easily done locally. This can be done by overriding the Command
# forward method. It has the same signature as execute and is normally
# responsible for transferring stuff to the server.
# Most commands will, however, need to perfom tasks on the server. I didn't
# want to start with forward and confuse the hell out of you. :)
# Okey, time to look at something a little more advance. A command that
# actually communicates with the LDAP backend.
# Let's import a new parameter type: Flag.
# Parameters of type Flag do not have values per say. They are either enabled
# or disabled (True or False), so there's no need to make then optional, ever.
from ipalib import Flag
class exshowuser(Command):
"""
Example command: retrieve an user entry from LDAP
"""
takes_args = (
Str('username'),
)
# takes_options is another attribute of Command. It works the same
# way as takes_args, but instead of positional arguments, it enables
# us to define what options the commmand takes.
# Note that an options can be both required and optional.
takes_options = (
Flag('all',
# the doc keyword argument is what you see when you go
# `ipa COMMAND --help` or `ipa help COMMAND`
doc='retrieve and print all attributes from the server. Affects command output.',
flags=['no_output'],
),
)
has_output = (
# Here, you can see a custom output pattern. The pattern constructor
# takes the output name (key in the dictionary returned by execute),
# the allowed type(s) (can be a tuple with several types), a
# simple description and a list of flags. Currently, only
# the 'no_display' flag is supported by the Command.output_for_cli
# method, but you can always use your own if you plan
# to override it - I'll show you how later.
output.Output('result', dict, 'user entry without DN'),
output.Output('dn', unicode, 'DN of the user entry', ['no_display']),
)
# Notice the ** argument notation for options. It is not required, but
# we strongly recommend you to use it. In some cases, special options
# are added automatically to commands and not listing them or using **
# may lead to exception flying around... and nobody likes exceptions
# flying around.
def execute(self, username, **options):
# OK, I said earlier that this command is going to communicate
# with the LDAP backend, You could always use python-ldap to do
# that, but there's also this nice class we have... it's called
# ldap2 and this is how you get a handle to it:
ldap = self.api.Backend.ldap2
# ldap2 enables you to do a lot of crazy stuff with LDAP and it's
# specially crafted to suit IPA plugin needs. I recommend you either
# look at ipaserver/plugins/ldap2 or checkout some of the generated
# HTML docs on www.freeipa.org as I won't be able to cover everything
# it offers in this file.
# We want to retrieve an user entry from LDAP. We need to know its
# DN first. There's a bunch of method in ldap2 to build DNs. For our
# purpose, this will do:
dn = ldap.make_dn_from_attr(
'uid', username, self.api.env.container_user
)
# Note that api.env contains a lot of useful constant. We recommend
# you to check them out and use them whenever possible.
# Let's check if the --all option is enabled. If it is, let's
# retrieve all of the entry attributes. If not, only retrieve some
# basic stuff like the username, first and last names.
if options.get('all', False):
attrs_list = ['*']
else:
attrs_list = ['uid', 'givenname', 'sn']
# Give us the entry, LDAP!
(dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
return dict(result=entry_attrs, dn=dn)
# register the command, uncomment this line if you want to try it out
#api.register(exshowuser)
# Now let's a take a look on how you can modify the command output if you don't
# like the default.
class exshowuser2(exshowuser):
"""
Example command: exusershow with custom output
"""
# Just some values we're going to use for textui.print_entry
attr_order = ['uid', 'givenname', 'sn']
attr_labels = {
'uid': 'User login', 'givenname': 'First name', 'sn': 'Last name'
}
def output_for_cli(self, textui, output, *args, **options):
# Now we've done it! We have overridden the default output_for_cli.
# textui is a class that implements a lot of useful outputting methods,
# please use it when you can
# output contains the dict returned by execute
# args, options contain the command parameters
textui.print_dashed('User entry:')
textui.print_indented('DN: %s' % output['dn'])
textui.print_entry(output['result'], self.attr_order, self.attr_labels)
# register the command, uncomment this line if you want to try it out
#api.register(exshowuser2)
# Alright, so now you'll always want to define your own output_for_cli...
# No, you won't! Because the default output_for_cli isn't as stupid as it looks.
# It can take information from the command parameters and output patterns
# to produce nice output like all real IPA commands have.
class exshowuser3(exshowuser):
"""
Example command: exusershow that takes full advantage of the default output
"""
takes_args = (
# We're going to rename the username argument to uid to match
# the attribute name it represent. The cli_name kwarg is what
# users will see in the CLI and label is what the default
# output_for_cli is going to use when printing the attribute value.
Str('uid',
cli_name='username',
label='User login',
),
)
# has_output_params works the same way as takes_args and takes_options,
# but is only used to define output attributes. These won't show up
# as parameters for the command.
has_output_params = (
Str('givenname',
label='First name',
),
Str('sn',
label='Last name',
),
)
# standard_entry includes an entry 'result' (dict), a summary 'summary'
# and the entry primary key 'value'
# It also makes the command automatically add two special options:
# --all and --raw. Look at the description of nearly any real IPA command
# to see what they're about.
has_output = output.standard_entry
# Since --all and --raw are added automatically thanks to standard_entry,
# we need to clear takes_options from the base class otherwise we would
# get a parameter conflict.
takes_options = tuple()
def execute(self, *args, **options):
# Let's just call execute of the base class, extract it's output
# and fit it into the standard_entry output pattern.
output = super(exshowuser3, self).execute(*args, **options)
output['result']['dn'] = output['dn']
return dict(result=output['result'], value=args[0])
# register the command, uncomment this line if you want to try it out
#api.register(exshowuser3)
# Pretty cool, right? But you will probably want to implement a set of commands
# to manage a certain type of entries (like users in the above examples).
# To save you the massive PITA of parameter copy&paste, we introduced
# the Object and Method plugin classes. Let's see how they work.
from ipalib import Object, Method
# First, we're going to create an object that represent the user entry.
class exuser(Object):
"""
Example plugin: user object
"""
# takes_params is an attribute of Object. It is used to define output
# parameters for associated Methods. Methods can also use them to
# to generate their own parameters as you'll see in a while.
takes_params = (
Str('uid',
cli_name='username',
label='User login',
# The primary_key kwarg is used to, well, specify the object's
# primary key.
primary_key=True,
),
Str('givenname?',
cli_name='first',
label='First name',
),
Str('sn?',
cli_name='last',
label='Last name',
),
)
# register the object, uncomment this line if you want to try it out
#api.register(exuser)
# Next, we're going to create a set of methods to manage this type of object
# i.e. to manage user entries. We're only going to do "read" commands, because
# we don't want to damage your user entries - adding, deleting, modifying is a
# bit more complicated and will be covered later in this file.
# Methods are automatically associated with a parent Object based on class
# names. They can then access their parent Object using self.obj.
# Simply said, Methods are just Commands associated with an Object.
class exuser_show(Method):
has_output = output.standard_entry
# get_args is a method of Command used to generate positional arguments
# we're going to use it to extract parameters from the parent
# Object
def get_args(self):
# self.obj.primary_key contains a reference the parameter with
# primary_key kwarg set to True.
# Parameters can be cloned to create new instance with additional
# kwargs. Here we add the attribute kwargs, that tells the framework
# the parameters corresponds to an LDAP attribute. The query kwargs
# tells the framework to skip parameter validation (i.e. do NOT call
# validators).
yield self.obj.primary_key.clone(attribute=True, query=True)
def execute(self, *args, **options):
ldap = self.api.Backend.ldap2
dn = ldap.make_dn_from_attr(
'uid', args[0], self.api.env.container_user
)
if options.get('all', False):
attrs_list = ['*']
else:
attrs_list = [p.name for p in self.output_params()]
(dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
entry_attrs['dn'] = dn
return dict(result=entry_attrs, value=args[0])
# register the command, uncomment this line if you want to try it out
#api.register(exuser_show)
class exuser_find(Method):
# standard_list_of_entries is an output pattern that
# define a dict with a list of entries, their count
# and a truncated flag. The truncated flag is used to mark
# truncated (incomplete) search results - for example due to
# timeouts.
has_output = output.standard_list_of_entries
# get_options is similar to get_args, but is used to generate
# options instead of positional arguments
def get_options(self):
for option in self.obj.params():
yield option.clone(
attribute=True, query=True, required=False
)
def execute(self, *args, **options):
ldap = self.api.Backend.ldap2
# args_options_2_entry is a helper method of Command used
# to create a dictionary from the command parameters that
# have the attribute kwargs set to True.
search_kw = self.args_options_2_entry(*args, **options)
# make_filter will create an LDAP filter from attribute values
# exact=False means the values are surrounded with * when constructing
# the filter and rules=ldap.MATCH_ALL means the filter is going
# to use the & operators. More complex filters can be constructed
# by joining simpler filters using ldap2.combine_filters.
attr_filter = ldap.make_filter(
search_kw, exact=False, rules=ldap.MATCH_ALL
)
if options.get('all', False):
attrs_list = ['*']
else:
attrs_list = [p.name for p in self.output_params()]
# perform the search
(entries, truncated) = ldap.find_entries(
attr_filter, attrs_list, self.api.env.container_user,
scope=ldap.SCOPE_ONELEVEL
)
# find_entries returns DNs and attributes separately, but the output
# patter expects them in one dict. We need to arrange that.
for e in entries:
e[1]['dn'] = e[0]
entries = [e for (dn, e) in entries]
return dict(result=entries, count=len(entries), truncated=truncated)
# register the command, uncomment this line if you want to try it out
#api.register(exuser_find)
# As most commands associated with objects are used to manage entries in LDAP,
# we defined a basic set of base classes for your plugins implementing CRUD
# operations. This is maily to save you from defining your own has_output,
# get_args, get_options and to have a standardized way of doing things for the
# sake of consistency. We won't cover them here, because you probably won't
# need to use them. So why did we botter? Well, you're going to see in
# a while. If interested anyway, check them out in ipalib/crud.py.
# At this point, if you've already seen some of the real plugins, you might
# be going like "WTH is this !@#^&? The user_show plugin is only like 4 lines
# of code and does much more than the exshowuser crap. Well yes, that's because
# it is based on one of the awesome plugin base classes we created to save
# authors from doing all the dirty work. Let's take a look at them.
# COMING SOON: baseldap.py classes, extending existing plugins, etc.

49
doc/examples/python-api.py Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# Authors:
# Jason Gerard DeRose <jderose@redhat.com>
#
# Copyright (C) 2009 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ipalib import api
# 1. Initialize ipalib
#
# Run ./python-api.py --help to see the global options. Some useful options:
#
# -v Produce more verbose output
# -d Produce full debugging output
# -e in_server=True Force running in server mode
# -e xmlrpc_uri=https://foo.com/ipa/xml # Connect to a specific server
api.bootstrap_with_global_options(context='example')
api.finalize()
# You will need to create a connection. If you're in_server, call
# Backend.ldap.connect(), otherwise Backend.xmlclient.connect().
if api.env.in_server:
api.Backend.ldap2.connect(
ccache=api.Backend.krb.default_ccname()
)
else:
api.Backend.xmlclient.connect()
# Now that you're connected, you can make calls to api.Command.whatever():
print 'The admin user:'
print api.Command.user_show(u'admin')

956
freeipa.spec.in Normal file
View File

@ -0,0 +1,956 @@
# Define ONLY_CLIENT to only make the ipa-client and ipa-python subpackages
%{!?ONLY_CLIENT:%global ONLY_CLIENT 0}
%global httpd_conf /etc/httpd/conf.d
%global plugin_dir %{_libdir}/dirsrv/plugins
%if ! (0%{?fedora} > 12 || 0%{?rhel} > 5)
%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from
distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from
distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
%endif
%global POLICYCOREUTILSVER 1.33.12-1
%global gettext_domain ipa
Name: freeipa
Version: __VERSION__
Release: __RELEASE__%{?dist}
Summary: The Identity, Policy and Audit system
Group: System Environment/Base
License: GPLv3+
URL: http://www.freeipa.org/
Source0: freeipa-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%if ! %{ONLY_CLIENT}
BuildRequires: 389-ds-base-devel >= 1.2.9
BuildRequires: svrcore-devel
BuildRequires: /usr/share/selinux/devel/Makefile
BuildRequires: policycoreutils >= %{POLICYCOREUTILSVER}
%endif
BuildRequires: nspr-devel
BuildRequires: nss-devel
BuildRequires: openssl-devel
BuildRequires: openldap-devel
BuildRequires: krb5-devel
BuildRequires: krb5-workstation
BuildRequires: libuuid-devel
%if 0%{?fedora} >= 16
BuildRequires: libcurl-devel >= 7.21.7-2
BuildRequires: xmlrpc-c-devel >= 1.27.4
%else
%if 0%{?fedora} == 15
BuildRequires: libcurl-devel >= 7.21.3-9
BuildRequires: xmlrpc-c-devel >= 1.25.4
%else
BuildRequires: libcurl-devel
BuildRequires: xmlrpc-c-devel
%endif
%endif
BuildRequires: popt-devel
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: m4
BuildRequires: libtool
BuildRequires: gettext
BuildRequires: python-devel
BuildRequires: authconfig
BuildRequires: python-ldap
BuildRequires: python-setuptools
BuildRequires: python-krbV
BuildRequires: python-nss
%if 0%{?fedora} >= 15
BuildRequires: python-netaddr >= 0.7.5-3
%else
BuildRequires: python-netaddr
%endif
BuildRequires: python-kerberos
BuildRequires: python-rhsm
BuildRequires: pyOpenSSL
BuildRequires: pylint
BuildRequires: libipa_hbac-python
%description
IPA is an integrated solution to provide centrally managed Identity (machine,
user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof).
%if ! %{ONLY_CLIENT}
%package server
Summary: The IPA authentication server
Group: System Environment/Base
Requires: %{name}-python = %{version}-%{release}
Requires: %{name}-client = %{version}-%{release}
Requires: %{name}-admintools = %{version}-%{release}
Requires: %{name}-server-selinux = %{version}-%{release}
Requires(pre): 389-ds-base >= 1.2.10-0.4.a4
Requires: openldap-clients
Requires: nss
Requires: nss-tools
Requires: krb5-server
Requires: krb5-server-ldap
Requires: krb5-pkinit-openssl
Requires: cyrus-sasl-gssapi%{?_isa}
Requires: ntp
Requires: httpd
Requires: mod_wsgi
Requires: mod_auth_kerb
Requires: mod_nss >= 1.0.8-10
Requires: python-ldap
Requires: python-krbV
Requires: acl
Requires: python-pyasn1 >= 0.0.9a
%if 0%{?fedora} >= 15
Requires: selinux-policy >= 3.9.16-18
%else
Requires: selinux-policy >= 3.9.7-27
%endif
Requires(post): selinux-policy-base
Requires: slapi-nis >= 0.21
%if 0%{?fedora} >= 15
Requires: pki-ca >= 9.0.15
Requires: pki-silent >= 9.0.15
Requires: pki-setup >= 9.0.15
%else
Requires: pki-ca >= 9.0.5
Requires: pki-silent >= 9.0.5
%endif
Requires: dogtag-pki-common-theme
Requires: dogtag-pki-ca-theme
%if 0%{?rhel}
Requires: subscription-manager
%endif
Requires(preun): python initscripts chkconfig
Requires(postun): python initscripts chkconfig
# We have a soft-requires on bind. It is an optional part of
# IPA but if it is configured we need a way to require versions
# that work for us.
Conflicts: bind-dyndb-ldap < 1.0.0-0.1.b1
Conflicts: bind < 9.8.1-1
Obsoletes: ipa-server >= 1.0
%description server
IPA is an integrated solution to provide centrally managed Identity (machine,
user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof). If you are installing an IPA server you need
to install this package (in other words, most people should NOT install
this package).
%package server-selinux
Summary: SELinux rules for freeipa-server daemons
Group: System Environment/Base
Requires(post): %{name}-server = %{version}-%{release}
Requires(postun): %{name}-server = %{version}-%{release}
Requires(pre): policycoreutils >= %{POLICYCOREUTILSVER}
Obsoletes: ipa-server-selinux >= 1.0
%description server-selinux
IPA is an integrated solution to provide centrally managed Identity (machine,
user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof). This package provides SELinux rules for the
daemons included in freeipa-server
%endif
%package client
Summary: IPA authentication for use on clients
Group: System Environment/Base
Requires: %{name}-python = %{version}-%{release}
Requires: python-ldap
Requires: cyrus-sasl-gssapi%{?_isa}
Requires: ntp
Requires: krb5-workstation
Requires: authconfig
Requires: pam_krb5
Requires: wget
%if 0%{?fedora} >= 16
Requires: libcurl >= 7.21.7-2
Requires: xmlrpc-c >= 1.27.4
%else
%if 0%{?fedora} == 15
Requires: libcurl >= 7.21.3-9
Requires: xmlrpc-c >= 1.25.4
%else
Requires: libcurl
Requires: xmlrpc-c
%endif
%endif
Requires: sssd >= 1.5.1
Requires: certmonger >= 0.26
Requires: nss-tools
Requires: bind-utils
Obsoletes: ipa-client >= 1.0
%description client
IPA is an integrated solution to provide centrally managed Identity (machine,
user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof). If your network uses IPA for authentication,
this package should be installed on every client machine.
%if ! %{ONLY_CLIENT}
%package admintools
Summary: IPA administrative tools
Group: System Environment/Base
Requires: %{name}-python = %{version}-%{release}
Requires: %{name}-client = %{version}-%{release}
Requires: python-krbV
Requires: python-ldap
Obsoletes: ipa-admintools >= 1.0
%description admintools
IPA is an integrated solution to provide centrally managed Identity (machine,
user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof). This package provides command-line tools for
IPA administrators.
%endif
%package python
Summary: Python libraries used by IPA
Group: System Environment/Libraries
%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
Requires: python-kerberos >= 1.1-3
%endif
Requires: authconfig
Requires: gnupg
Requires: iproute
Requires: pyOpenSSL
Requires: python-nss >= 0.11
Requires: python-lxml
%if 0%{?fedora} >= 15
Requires: python-netaddr >= 0.7.5-3
%else
Requires: python-netaddr
%endif
Requires: libipa_hbac-python
Obsoletes: ipa-python >= 1.0
%description python
IPA is an integrated solution to provide centrally managed Identity (machine,
user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof). If you are using IPA you need to install this
package.
%prep
%setup -n freeipa-%{version} -q
%build
export CFLAGS="$CFLAGS %{optflags}"
export CPPFLAGS="$CPPFLAGS %{optflags}"
make version-update
cd ipa-client; ../autogen.sh --prefix=%{_usr} --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir} --libdir=%{_libdir} --mandir=%{_mandir}; cd ..
%if ! %{ONLY_CLIENT}
cd daemons; ../autogen.sh --prefix=%{_usr} --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir} --libdir=%{_libdir} --mandir=%{_mandir} --with-openldap; cd ..
cd install; ../autogen.sh --prefix=%{_usr} --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir} --libdir=%{_libdir} --mandir=%{_mandir}; cd ..
%endif
%if ! %{ONLY_CLIENT}
make IPA_VERSION_IS_GIT_SNAPSHOT=no %{?_smp_mflags} all
cd selinux
# This isn't multi-process make capable yet
make all
%else
make IPA_VERSION_IS_GIT_SNAPSHOT=no %{?_smp_mflags} client
%endif
%install
rm -rf %{buildroot}
%if ! %{ONLY_CLIENT}
make install DESTDIR=%{buildroot}
cd selinux
make install DESTDIR=%{buildroot}
cd ..
%else
make client-install DESTDIR=%{buildroot}
%endif
%find_lang %{gettext_domain}
%if ! %{ONLY_CLIENT}
# Remove .la files from libtool - we don't want to package
# these files
rm %{buildroot}/%{plugin_dir}/libipa_pwd_extop.la
rm %{buildroot}/%{plugin_dir}/libipa_enrollment_extop.la
rm %{buildroot}/%{plugin_dir}/libipa_winsync.la
rm %{buildroot}/%{plugin_dir}/libipa_repl_version.la
rm %{buildroot}/%{plugin_dir}/libipa_uuid.la
rm %{buildroot}/%{plugin_dir}/libipa_modrdn.la
rm %{buildroot}/%{plugin_dir}/libipa_lockout.la
# Some user-modifiable HTML files are provided. Move these to /etc
# and link back.
mkdir -p %{buildroot}/%{_sysconfdir}/ipa/html
mkdir -p %{buildroot}/%{_localstatedir}/cache/ipa/sysrestore
mkdir %{buildroot}%{_usr}/share/ipa/html/
ln -s ../../../..%{_sysconfdir}/ipa/html/ssbrowser.html \
%{buildroot}%{_usr}/share/ipa/html/ssbrowser.html
ln -s ../../../..%{_sysconfdir}/ipa/html/unauthorized.html \
%{buildroot}%{_usr}/share/ipa/html/unauthorized.html
ln -s ../../../..%{_sysconfdir}/ipa/html/browserconfig.html \
%{buildroot}%{_usr}/share/ipa/html/browserconfig.html
ln -s ../../../..%{_sysconfdir}/ipa/html/hbac-deny-remove.html \
%{buildroot}%{_usr}/share/ipa/html/hbac-deny-remove.html
ln -s ../../../..%{_sysconfdir}/ipa/html/ipa_error.css \
%{buildroot}%{_usr}/share/ipa/html/ipa_error.css
# So we can own our Apache configuration
mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d/
/bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa.conf
/bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa-pki-proxy.conf
/bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa-rewrite.conf
install -m755 ipa.init %{buildroot}%{_initrddir}/ipa
%endif
mkdir -p %{buildroot}%{_sysconfdir}/ipa/
/bin/touch %{buildroot}%{_sysconfdir}/ipa/default.conf
mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa-client/sysrestore
%if ! %{ONLY_CLIENT}
mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d
install -pm 644 contrib/completion/ipa.bash_completion %{buildroot}%{_sysconfdir}/bash_completion.d/ipa
mkdir -p %{buildroot}%{_sysconfdir}/cron.d
install -pm 644 ipa-compliance.cron %{buildroot}%{_sysconfdir}/cron.d/ipa-compliance
%endif
%clean
rm -rf %{buildroot}
%if ! %{ONLY_CLIENT}
%post server
if [ $1 = 1 ]; then
/sbin/chkconfig --add ipa
/sbin/chkconfig --add ipa_kpasswd
fi
if [ $1 -gt 1 ] ; then
/usr/sbin/ipa-upgradeconfig || :
/usr/sbin/ipa-ldap-updater --upgrade >/dev/null 2>&1 || :
fi
%preun server
if [ $1 = 0 ]; then
/sbin/chkconfig --del ipa
/sbin/chkconfig --del ipa_kpasswd
/sbin/service ipa stop >/dev/null 2>&1 || :
fi
%postun server
if [ "$1" -ge "1" ]; then
/sbin/service ipa condrestart >/dev/null 2>&1 || :
fi
%pre server-selinux
if [ -s /etc/selinux/config ]; then
. %{_sysconfdir}/selinux/config
FILE_CONTEXT=%{_sysconfdir}/selinux/targeted/contexts/files/file_contexts
if [ "${SELINUXTYPE}" == targeted -a -f ${FILE_CONTEXT} ]; then \
cp -f ${FILE_CONTEXT} ${FILE_CONTEXT}.%{name}
fi
fi
%post server-selinux
semodule -s targeted -i /usr/share/selinux/targeted/ipa_kpasswd.pp /usr/share/selinux/targeted/ipa_httpd.pp /usr/share/selinux/targeted/ipa_dogtag.pp
. %{_sysconfdir}/selinux/config
FILE_CONTEXT=%{_sysconfdir}/selinux/targeted/contexts/files/file_contexts
selinuxenabled
if [ $? == 0 -a "${SELINUXTYPE}" == targeted -a -f ${FILE_CONTEXT}.%{name} ]; then
fixfiles -C ${FILE_CONTEXT}.%{name} restore
rm -f ${FILE_CONTEXT}.%name
fi
%preun server-selinux
if [ $1 = 0 ]; then
if [ -s /etc/selinux/config ]; then
. %{_sysconfdir}/selinux/config
FILE_CONTEXT=%{_sysconfdir}/selinux/targeted/contexts/files/file_contexts
if [ "${SELINUXTYPE}" == targeted -a -f ${FILE_CONTEXT} ]; then \
cp -f ${FILE_CONTEXT} ${FILE_CONTEXT}.%{name}
fi
fi
fi
%postun server-selinux
if [ $1 = 0 ]; then
semodule -s targeted -r ipa_kpasswd ipa_httpd ipa_dogtag
. %{_sysconfdir}/selinux/config
FILE_CONTEXT=%{_sysconfdir}/selinux/targeted/contexts/files/file_contexts
selinuxenabled
if [ $? == 0 -a "${SELINUXTYPE}" == targeted -a -f ${FILE_CONTEXT}.%{name} ]; then
fixfiles -C ${FILE_CONTEXT}.%{name} restore
rm -f ${FILE_CONTEXT}.%name
fi
fi
%endif
%if ! %{ONLY_CLIENT}
%files server
%defattr(-,root,root,-)
%doc COPYING README Contributors.txt
%{_sbindir}/ipa-ca-install
%{_sbindir}/ipa-dns-install
%{_sbindir}/ipa-server-install
%{_sbindir}/ipa-replica-conncheck
%{_sbindir}/ipa-replica-install
%{_sbindir}/ipa-replica-prepare
%{_sbindir}/ipa-replica-manage
%{_sbindir}/ipa-csreplica-manage
%{_sbindir}/ipa-server-certinstall
%{_sbindir}/ipa-ldap-updater
%{_sbindir}/ipa-compat-manage
%{_sbindir}/ipa-nis-manage
%{_sbindir}/ipa-managed-entries
%{_sbindir}/ipa_kpasswd
%{_sbindir}/ipactl
%{_sbindir}/ipa-upgradeconfig
%{_sbindir}/ipa-compliance
%{_sysconfdir}/cron.d/ipa-compliance
%attr(755,root,root) %{_initrddir}/ipa
%attr(755,root,root) %{_initrddir}/ipa_kpasswd
%dir %{python_sitelib}/ipaserver
%{python_sitelib}/ipaserver/*
%dir %{_usr}/share/ipa
%{_usr}/share/ipa/wsgi.py*
%{_usr}/share/ipa/*.ldif
%{_usr}/share/ipa/*.uldif
%{_usr}/share/ipa/*.template
%dir %{_usr}/share/ipa/html
%{_usr}/share/ipa/html/ssbrowser.html
%{_usr}/share/ipa/html/browserconfig.html
%{_usr}/share/ipa/html/unauthorized.html
%{_usr}/share/ipa/html/hbac-deny-remove.html
%{_usr}/share/ipa/html/ipa_error.css
%dir %{_usr}/share/ipa/migration
%{_usr}/share/ipa/migration/error.html
%{_usr}/share/ipa/migration/index.html
%{_usr}/share/ipa/migration/invalid.html
%{_usr}/share/ipa/migration/ipa_migration.css
%{_usr}/share/ipa/migration/migration.py*
%dir %{_usr}/share/ipa/ui
%{_usr}/share/ipa/ui/index.html
%{_usr}/share/ipa/ui/*.png
%{_usr}/share/ipa/ui/*.gif
%{_usr}/share/ipa/ui/*.ico
%{_usr}/share/ipa/ui/*.css
%{_usr}/share/ipa/ui/*.js
%{_usr}/share/ipa/ui/*.eot
%{_usr}/share/ipa/ui/*.svg
%{_usr}/share/ipa/ui/*.ttf
%{_usr}/share/ipa/ui/*.woff
%dir %{_sysconfdir}/ipa
%dir %{_sysconfdir}/ipa/html
%config(noreplace) %{_sysconfdir}/ipa/html/ssbrowser.html
%config(noreplace) %{_sysconfdir}/ipa/html/ipa_error.css
%config(noreplace) %{_sysconfdir}/ipa/html/unauthorized.html
%config(noreplace) %{_sysconfdir}/ipa/html/browserconfig.html
%config(noreplace) %{_sysconfdir}/ipa/html/hbac-deny-remove.html
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-rewrite.conf
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa.conf
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-pki-proxy.conf
%{_usr}/share/ipa/ipa.conf
%{_usr}/share/ipa/ipa-rewrite.conf
%{_usr}/share/ipa/ipa-pki-proxy.conf
%dir %{_usr}/share/ipa/updates/
%{_usr}/share/ipa/updates/*
%attr(755,root,root) %{plugin_dir}/libipa_pwd_extop.so
%attr(755,root,root) %{plugin_dir}/libipa_enrollment_extop.so
%attr(755,root,root) %{plugin_dir}/libipa_winsync.so
%attr(755,root,root) %{plugin_dir}/libipa_repl_version.so
%attr(755,root,root) %{plugin_dir}/libipa_uuid.so
%attr(755,root,root) %{plugin_dir}/libipa_modrdn.so
%attr(755,root,root) %{plugin_dir}/libipa_lockout.so
%dir %{_localstatedir}/lib/ipa
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore
%dir %{_localstatedir}/cache/ipa
%attr(700,apache,apache) %dir %{_localstatedir}/cache/ipa/sessions
%attr(700,root,root) %dir %{_localstatedir}/cache/ipa/kpasswd
%{_mandir}/man1/ipa-replica-conncheck.1.gz
%{_mandir}/man1/ipa-replica-install.1.gz
%{_mandir}/man1/ipa-replica-manage.1.gz
%{_mandir}/man1/ipa-csreplica-manage.1.gz
%{_mandir}/man1/ipa-replica-prepare.1.gz
%{_mandir}/man1/ipa-server-certinstall.1.gz
%{_mandir}/man1/ipa-server-install.1.gz
%{_mandir}/man1/ipa-dns-install.1.gz
%{_mandir}/man1/ipa-ca-install.1.gz
%{_mandir}/man1/ipa-compat-manage.1.gz
%{_mandir}/man1/ipa-nis-manage.1.gz
%{_mandir}/man1/ipa-managed-entries.1.gz
%{_mandir}/man1/ipa-ldap-updater.1.gz
%{_mandir}/man8/ipa_kpasswd.8.gz
%{_mandir}/man8/ipactl.8.gz
%{_mandir}/man1/ipa-compliance.1.gz
%files server-selinux
%defattr(-,root,root,-)
%doc COPYING README Contributors.txt
%{_usr}/share/selinux/targeted/ipa_kpasswd.pp
%{_usr}/share/selinux/targeted/ipa_httpd.pp
%{_usr}/share/selinux/targeted/ipa_dogtag.pp
%endif
%files client
%defattr(-,root,root,-)
%doc COPYING README Contributors.txt
%{_sbindir}/ipa-client-install
%{_sbindir}/ipa-getkeytab
%{_sbindir}/ipa-rmkeytab
%{_sbindir}/ipa-join
%dir %{_usr}/share/ipa
%dir %{_usr}/share/ipa/ipaclient
%dir %{_localstatedir}/lib/ipa-client
%dir %{_localstatedir}/lib/ipa-client/sysrestore
%{_usr}/share/ipa/ipaclient/ipa.cfg
%{_usr}/share/ipa/ipaclient/ipa.js
%dir %{python_sitelib}/ipaclient
%{python_sitelib}/ipaclient/*.py*
%{_mandir}/man1/ipa-getkeytab.1.gz
%{_mandir}/man1/ipa-rmkeytab.1.gz
%{_mandir}/man1/ipa-client-install.1.gz
%{_mandir}/man1/ipa-join.1.gz
%{_mandir}/man5/default.conf.5.gz
%if ! %{ONLY_CLIENT}
%files admintools
%defattr(-,root,root,-)
%doc COPYING README Contributors.txt
%{_bindir}/ipa
%config %{_sysconfdir}/bash_completion.d
%{_mandir}/man1/ipa.1.gz
%endif
%files python -f %{gettext_domain}.lang
%defattr(-,root,root,-)
%doc COPYING README Contributors.txt
%dir %{python_sitelib}/ipapython
%dir %{python_sitelib}/ipapython/platform
%{python_sitelib}/ipapython/*.py*
%{python_sitelib}/ipapython/platform/*.py*
%dir %{python_sitelib}/ipalib
%{python_sitelib}/ipalib/*
%{python_sitearch}/default_encoding_utf8.so
%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
%{python_sitelib}/ipapython-*.egg-info
%{python_sitelib}/freeipa-*.egg-info
%{python_sitearch}/python_default_encoding-*.egg-info
%endif
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
%changelog
* Fri Oct 14 2011 Rob Crittenden <rcritten@redhat.com> - 2.1.1-3
- Set min nvr of 389-ds-base to 1.2.10-0.4.a4 for limits fixes (740942, 742324)
* Fri Oct 7 2011 Adam Young <ayoung@redhat.com> - 2.1.1-2
- Add explicit dependency on pki-setup.
* Mon Sep 12 2011 Alexander Bokovoy <abokovoy@redhat.com> - 2.1.1-1
- Make sure platform adaptation is packaged in -python sub-package
* Fri Sep 9 2011 Martin Kosek <mkosek@redhat.com> - 2.1.0-4
- Add soft dependency for bind and bind-dyndb-ldap required versions
* Wed Aug 31 2011 Rob Crittenden <rcritten@redhat.com> - 2.1.0-3
- Set min nvr of 389-ds-base to 1.2.9.7-1 for BZ 728605
* Mon Aug 29 2011 Rob Crittenden <rcritten@redhat.com> - 2.1.0-2
- Set min nvr of pki-ca to 9.0.12 for fix in BZ 700505
* Tue Aug 23 2011 Jan Cholasta <jcholast@redhat.com> - 2.1.0-1
- Add subscription-manager dependency for RHEL.
* Thu Aug 11 2011 Martin Kosek <mkosek@redhat.com> - 2.0.90-12
- Set min nvr of 389-ds-base to 1.2.9.6 for fix in BZ 725743,
723937, and 725542
- Set min nvr of pki-ca to 9.0.11 for fix in BZ 728332
* Thu Aug 11 2011 Martin Kosek <mkosek@redhat.com> - 2.0.90-11
- Set min nvr of xmlrpc-c and libcurl to make sure GSSAPI delegation
support is in
* Tue Aug 2 2011 Endi S. Dewata <edewata@redhat.com> - 2.0.90-10
- Add *.ico files
* Tue Jul 29 2011 Alexander Bokovoy <abokovoy@redhat.com> - 2.0.90-9
- Add libipa_hbac-python dependency for hbactest plugin
* Thu Jul 28 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-8
- Set min nvr of pki-ca to 9.0.10 on F-15+ to pick up updated
caIPAserviceCert.cfg profile
* Wed Jul 20 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-7
- Make cyrus-sasl-gssapi requires arch-specific
* Thu Jul 14 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-6
- Add ipa-csreplica-manage tool.
* Wed Jul 6 2011 Adam Young <ayoung@redhat.com> - 2.0.90-5
- Add HTML file describing issues with HBAC deny rules
* Fri Jun 17 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-4
- Ship ipa-ca-install utility
* Thu May 12 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-3
- Set min nvr of selinux-policy to 3.9.16-18 on F-15+
- Set min nvr of pki-ca to 9.0.7 on F-15+
* Thu May 5 2011 Martin Kosek <mkosek@redhat.com> - 2.0.90-2
- Add BuildRequires on pylint, python-rhsm to enable a build with enforced
pylint check
* Tue May 3 2011 Rob Crittenden <rcritten@redhat.com> - 2.0.90-1
- Bump version to 2.0.90
* Tue Apr 5 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-47
- Set min version of 389-ds-base to 1.2.8.0-1 for fix in BZ 693466.
* Thu Mar 17 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-46
- Automatically apply updates when the package is upgraded.
* Thu Feb 17 2011 Jakub Hrozek <jhrozek@redhat.com> - 1.99-45
- Set minimum version of python-nss to 0.11 to make sure IPv6 support is in
* Wed Feb 9 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-44
- Set minimum version of sssd to 1.5.1
* Thu Feb 2 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-43
- Set min version of 389-ds-base to 1.2.8
- Set min version of mod_nss 1.0.8-10
- Set min version of selinux-policy to 3.9.7-27
* Thu Jan 27 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-42
- Apply changes discovered in Fedora package review process (#672986)
* Tue Jan 25 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-41
- Re-arrange doc and defattr to clean up rpmlint warnings
- Remove conditionals on older releases
- Move some man pages into admintools subpackage
- Remove some explicit Requires in client that aren't needed
- Consistent use of buildroot vs RPM_BUILD_ROOT
* Thu Jan 19 2011 Adam Young <ayoung@redhat.com> - 1.99-40
- Moved directory install/static to install/ui
* Thu Jan 13 2011 Simo Sorce <ssorce@redhat.com> - 1.99-39
- Remove dependency on nss_ldap/nss-pam-ldapd
- The official client is sssd and that's what we use by default.
* Thu Jan 13 2011 Simo Sorce <ssorce@redhat.com> - 1.99-38
- Remove radius subpackages
* Thu Jan 13 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-37
- Set minimum pki-ca and pki-silent versions to 9.0.0
* Wed Jan 12 2011 Rob Crittenden <rcritten@redhat.com> - 1.99-36
- Drop BuildRequires on mozldap-devel
* Mon Dec 13 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-35
- Add Requires on krb5-pkinit-openssl
* Fri Dec 10 2010 Jr Aquino <jr.aquino@citrix.com> - 1.99-34
- Add ipa-host-net-manage script
* Tue Dec 7 2010 Simo Sorce <ssorce@redhat.com> - 1.99-33
- Add ipa init script
* Fri Nov 19 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-32
- Set minimum level of 389-ds-base to 1.2.7 for enhanced memberof plugin
* Wed Nov 3 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-31
- remove ipa-fix-CVE-2008-3274
* Wed Oct 6 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-30
- Remove duplicate %%files entries on share/ipa/static
- Add python default encoding shared library
* Mon Sep 20 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-29
- Drop requires on python-configobj (not used any more)
- Drop ipa-ldap-updater message, upgrades are done differently now
* Wed Sep 8 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-28
- Drop conflicts on mod_nss
- Require nss-pam-ldapd on F-14 or higher instead of nss_ldap (#606847)
- Drop a slew of conditionals on older Fedora releases (< 12)
- Add a few conditionals against RHEL 6
- Add Requires of nss-tools on ipa-client
* Fri Aug 13 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-27
- Set minimum version of certmonger to 0.26 (to pck up #621670)
- Set minimum version of pki-silent to 1.3.4 (adds -key_algorithm)
- Set minimum version of pki-ca to 1.3.6
- Set minimum version of sssd to 1.2.1
* Tue Aug 10 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-26
- Add BuildRequires for authconfig
* Mon Jul 19 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-25
- Bump up minimum version of python-nss to pick up nss_is_initialize() API
* Thu Jun 24 2010 Adam Young <ayoung@redhat.com> - 1.99-24
- Removed python-asset based webui
* Thu Jun 24 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-23
- Change Requires from fedora-ds-base to 389-ds-base
- Set minimum level of 389-ds-base to 1.2.6 for the replication
version plugin.
* Tue Jun 1 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-22
- Drop Requires of python-krbV on ipa-client
* Mon May 17 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-21
- Load ipa_dogtag.pp in post install
* Mon Apr 26 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-20
- Set minimum level of sssd to 1.1.1 to pull in required hbac fixes.
* Thu Mar 4 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-19
- No need to create /var/log/ipa_error.log since we aren't using
TurboGears any more.
* Mon Mar 1 2010 Jason Gerard DeRose <jderose@redhat.com> - 1.99-18
- Fixed share/ipa/wsgi.py so .pyc, .pyo files are included
* Wed Feb 24 2010 Jason Gerard DeRose <jderose@redhat.com> - 1.99-17
- Added Require mod_wsgi, added share/ipa/wsgi.py
* Thu Feb 11 2010 Jason Gerard DeRose <jderose@redhat.com> - 1.99-16
- Require python-wehjit >= 0.2.2
* Wed Feb 3 2010 Rob Crittenden <rcritten@redhat.com> - 1.99-15
- Add sssd and certmonger as a Requires on ipa-client
* Wed Jan 27 2010 Jason Gerard DeRose <jderose@redhat.com> - 1.99-14
- Require python-wehjit >= 0.2.0
* Fri Dec 4 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-13
- Add ipa-rmkeytab tool
* Tue Dec 1 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-12
- Set minimum of python-pyasn1 to 0.0.9a so we have support for the ASN.1
Any type
* Wed Nov 25 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-11
- Remove v1-style /etc/ipa/ipa.conf, replacing with /etc/ipa/default.conf
* Fri Nov 13 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-10
- Add bash completion script and own /etc/bash_completion.d in case it
doesn't already exist
* Tue Nov 3 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-9
- Remove ipa_webgui, its functions rolled into ipa_httpd
* Mon Oct 12 2009 Jason Gerard DeRose <jderose@redhat.com> - 1.99-8
- Removed python-cherrypy from BuildRequires and Requires
- Added Requires python-assets, python-wehjit
* Mon Aug 24 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-7
- Added httpd SELinux policy so CRLs can be read
* Thu May 21 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-6
- Move ipalib to ipa-python subpackage
- Bump minimum version of slapi-nis to 0.15
* Thu May 6 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-5
- Set 0.14 as minimum version for slapi-nis
* Wed Apr 22 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-4
- Add Requires: python-nss to ipa-python sub-package
* Thu Mar 5 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-3
- Remove the IPA DNA plugin, use the DS one
* Wed Mar 4 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-2
- Build radius separately
- Fix a few minor issues
* Tue Feb 3 2009 Rob Crittenden <rcritten@redhat.com> - 1.99-1
- Replace TurboGears requirement with python-cherrypy
* Sat Jan 17 2009 Tomas Mraz <tmraz@redhat.com> - 1.2.1-3
- rebuild with new openssl
* Fri Dec 19 2008 Dan Walsh <dwalsh@redhat.com> - 1.2.1-2
- Fix SELinux code
* Mon Dec 15 2008 Simo Sorce <ssorce@redhat.com> - 1.2.1-1
- Fix breakage caused by python-kerberos update to 1.1
* Fri Dec 5 2008 Simo Sorce <ssorce@redhat.com> - 1.2.1-0
- New upstream release 1.2.1
* Sat Nov 29 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 1.2.0-4
- Rebuild for Python 2.6
* Fri Nov 14 2008 Simo Sorce <ssorce@redhat.com> - 1.2.0-3
- Respin after the tarball has been re-released upstream
New hash is 506c9c92dcaf9f227cba5030e999f177
* Thu Nov 13 2008 Simo Sorce <ssorce@redhat.com> - 1.2.0-2
- Conditionally restart also dirsrv and httpd when upgrading
* Wed Oct 29 2008 Rob Crittenden <rcritten@redhat.com> - 1.2.0-1
- Update to upstream version 1.2.0
- Set fedora-ds-base minimum version to 1.1.3 for winsync header
- Set the minimum version for SELinux policy
- Remove references to Fedora 7
* Wed Jul 23 2008 Simo Sorce <ssorce@redhat.com> - 1.1.0-3
- Fix for CVE-2008-3274
- Fix segfault in ipa-kpasswd in case getifaddrs returns a NULL interface
- Add fix for bug #453185
- Rebuild against openldap libraries, mozldap ones do not work properly
- TurboGears is currently broken in rawhide. Added patch to not build
the UI locales and removed them from the ipa-server files section.
* Wed Jun 18 2008 Rob Crittenden <rcritten@redhat.com> - 1.1.0-2
- Add call to /usr/sbin/upgradeconfig to post install
* Wed Jun 11 2008 Rob Crittenden <rcritten@redhat.com> - 1.1.0-1
- Update to upstream version 1.1.0
- Patch for indexing memberof attribute
- Patch for indexing uidnumber and gidnumber
- Patch to change DNA default values for replicas
- Patch to fix uninitialized variable in ipa-getkeytab
* Fri May 16 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-5
- Set fedora-ds-base minimum version to 1.1.0.1-4 and mod_nss minimum
version to 1.0.7-4 so we pick up the NSS fixes.
- Add selinux-policy-base(post) to Requires (446496)
* Tue Apr 29 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-4
- Add missing entry for /var/cache/ipa/kpasswd (444624)
- Added patch to fix permissions problems with the Apache NSS database.
- Added patch to fix problem with DNS querying where the query could be
returned as the answer.
- Fix spec error where patch1 was in the wrong section
* Fri Apr 25 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-3
- Added patch to fix problem reported by ldapmodify
* Fri Apr 25 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-2
- Fix Requires for krb5-server that was missing for Fedora versions > 9
- Remove quotes around test for fedora version to package egg-info
* Fri Apr 18 2008 Rob Crittenden <rcritten@redhat.com> - 1.0.0-1
- Update to upstream version 1.0.0
* Tue Mar 18 2008 Rob Crittenden <rcritten@redhat.com> 0.99-12
- Pull upstream changelog 722
- Add Conflicts mod_ssl (435360)
* Thu Feb 29 2008 Rob Crittenden <rcritten@redhat.com> 0.99-11
- Pull upstream changelog 698
- Fix ownership of /var/log/ipa_error.log during install (435119)
- Add pwpolicy command and man page
* Thu Feb 21 2008 Rob Crittenden <rcritten@redhat.com> 0.99-10
- Pull upstream changelog 678
- Add new subpackage, ipa-server-selinux
- Add Requires: authconfig to ipa-python (bz #433747)
- Package i18n files
* Mon Feb 18 2008 Rob Crittenden <rcritten@redhat.com> 0.99-9
- Pull upstream changelog 641
- Require minimum version of krb5-server on F-7 and F-8
- Package some new files
* Thu Jan 31 2008 Rob Crittenden <rcritten@redhat.com> 0.99-8
- Marked with wrong license. IPA is GPLv2.
* Tue Jan 29 2008 Rob Crittenden <rcritten@redhat.com> 0.99-7
- Ensure that /etc/ipa exists before moving user-modifiable html files there
- Put html files into /etc/ipa/html instead of /etc/ipa
* Tue Jan 29 2008 Rob Crittenden <rcritten@redhat.com> 0.99-6
- Pull upstream changelog 608 which renamed several files
* Thu Jan 24 2008 Rob Crittenden <rcritten@redhat.com> 0.99-5
- package the sessions dir /var/cache/ipa/sessions
- Pull upstream changelog 597
* Thu Jan 24 2008 Rob Crittenden <rcritten@redhat.com> 0.99-4
- Updated upstream pull (596) to fix bug in ipa_webgui that was causing the
UI to not start.
* Thu Jan 24 2008 Rob Crittenden <rcritten@redhat.com> 0.99-3
- Included LICENSE and README in all packages for documentation
- Move user-modifiable content to /etc/ipa and linked back to
/usr/share/ipa/html
- Changed some references to /usr to the {_usr} macro and /etc
to {_sysconfdir}
- Added popt-devel to BuildRequires for Fedora 8 and higher and
popt for Fedora 7
- Package the egg-info for Fedora 9 and higher for ipa-python
* Tue Jan 22 2008 Rob Crittenden <rcritten@redhat.com> 0.99-2
- Added auto* BuildRequires
* Mon Jan 21 2008 Rob Crittenden <rcritten@redhat.com> 0.99-1
- Unified spec file
* Thu Jan 17 2008 Rob Crittenden <rcritten@redhat.com> - 0.6.0-2
- Fixed License in specfile
- Include files from /usr/lib/python*/site-packages/ipaserver
* Fri Dec 21 2007 Karl MacMillan <kmacmill@redhat.com> - 0.6.0-1
- Version bump for release
* Wed Nov 21 2007 Karl MacMillan <kmacmill@mentalrootkit.com> - 0.5.0-1
- Preverse mode on ipa-keytab-util
- Version bump for relase and rpm name change
* Thu Nov 15 2007 Rob Crittenden <rcritten@redhat.com> - 0.4.1-2
- Broke invididual Requires and BuildRequires onto separate lines and
reordered them
- Added python-tgexpandingformwidget as a dependency
- Require at least fedora-ds-base 1.1
* Thu Nov 1 2007 Karl MacMillan <kmacmill@redhat.com> - 0.4.1-1
- Version bump for release
* Wed Oct 31 2007 Karl MacMillan <kmacmill@redhat.com> - 0.4.0-6
- Add dep for freeipa-admintools and acl
* Wed Oct 24 2007 Rob Crittenden <rcritten@redhat.com> - 0.4.0-5
- Add dependency for python-krbV
* Fri Oct 19 2007 Rob Crittenden <rcritten@redhat.com> - 0.4.0-4
- Require mod_nss-1.0.7-2 for mod_proxy fixes
* Thu Oct 18 2007 Karl MacMillan <kmacmill@redhat.com> - 0.4.0-3
- Convert to autotools-based build
* Tue Sep 25 2007 Karl MacMillan <kmacmill@redhat.com> - 0.4.0-2
* Fri Sep 7 2007 Karl MacMillan <kmacmill@redhat.com> - 0.3.0-1
- Added support for libipa-dna-plugin
* Fri Aug 10 2007 Karl MacMillan <kmacmill@redhat.com> - 0.2.0-1
- Added support for ipa_kpasswd and ipa_pwd_extop
* Mon Aug 5 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-3
- Abstracted client class to work directly or over RPC
* Wed Aug 1 2007 Rob Crittenden <rcritten@redhat.com> - 0.1.0-2
- Add mod_auth_kerb and cyrus-sasl-gssapi to Requires
- Remove references to admin server in ipa-server-setupssl
- Generate a client certificate for the XML-RPC server to connect to LDAP with
- Create a keytab for Apache
- Create an ldif with a test user
- Provide a certmap.conf for doing SSL client authentication
* Fri Jul 27 2007 Karl MacMillan <kmacmill@redhat.com> - 0.1.0-1
- Initial rpm version

53
install/Makefile.am Normal file
View File

@ -0,0 +1,53 @@
# This file will be processed with automake-1.7 to create Makefile.in
#
AUTOMAKE_OPTIONS = 1.7
NULL =
SUBDIRS = \
conf \
html \
migration \
share \
ui \
tools \
updates \
po \
$(NULL)
install-exec-local:
mkdir -p $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
chmod 700 $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/sessions
chmod 700 $(DESTDIR)$(localstatedir)/cache/ipa/sessions
uninstall-local:
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa
-rmdir $(DESTDIR)$(localstatedir)/cache/ipa/sessions
-rmdir $(DESTDIR)$(localstatedir)/cache/ipa
DISTCLEANFILES = \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
intltool-*.in \
compile \
configure \
COPYING \
INSTALL \
install-sh \
missing \
mkinstalldirs \
config.guess \
ltmain.sh \
config.sub \
depcomp \
Makefile.in \
config.h.* \
aclocal.m4 \
version.m4 \
ipa-client.spec \
py-compile \
$(NULL)

15
install/README.schema Normal file
View File

@ -0,0 +1,15 @@
Ground rules on adding new schema
Brand new schema, particularly when written specifically for IPA, should be
added in share/*.ldif. Any new files need to be explicitly loaded in
ipaserver/install/dsinstance.py. These simply get copied directly into
the new instance schema directory.
Existing schema (e.g. in an LDAP draft) may either be added as a separate
ldif in share or as an update in the updates directory. The advantage of
adding the schema as an update is if 389-ds ever adds the schema then the
installation won't fail due to existing schema failing to load during
bootstrap.
If the new schema requires a new container then this should be added
to install/bootstrap-template.ldif.

16
install/conf/Makefile.am Normal file
View File

@ -0,0 +1,16 @@
NULL =
appdir = $(IPA_DATA_DIR)
app_DATA = \
ipa.conf \
ipa-pki-proxy.conf \
ipa-rewrite.conf \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,27 @@
# VERSION 1 - DO NOT REMOVE THIS LINE
ProxyRequests Off
# matches for ee port
<LocationMatch "^/ca/ee/ca/checkRequest|^/ca/ee/ca/getCertChain|^/ca/ee/ca/getTokenInfo|^/ca/ee/ca/tokenAuthenticate|^/ca/ocsp|^/ca/ee/ca/updateNumberRange">
NSSOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
NSSVerifyClient none
ProxyPassMatch ajp://localhost:9447/
ProxyPassReverse ajp://localhost:9447/
</LocationMatch>
# matches for admin port
<LocationMatch "^/ca/admin/ca/getCertChain|^/ca/admin/ca/getConfigEntries|^/ca/admin/ca/getCookie|^/ca/admin/ca/getStatus|^/ca/admin/ca/securityDomainLogin|^/ca/admin/ca/getDomainXML">
NSSOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
NSSVerifyClient none
ProxyPassMatch ajp://localhost:9447/
ProxyPassReverse ajp://localhost:9447/
</LocationMatch>
# matches for agent port and eeca port
<LocationMatch "^/ca/agent/ca/displayBySerial|^/ca/agent/ca/doRevoke|^/ca/agent/ca/doUnrevoke|^/ca/agent/ca/updateDomainXML|^/ca/eeca/ca/profileSubmitSSLClient">
NSSOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
NSSVerifyClient require
ProxyPassMatch ajp://localhost:9447/
ProxyPassReverse ajp://localhost:9447/
</LocationMatch>

View File

@ -0,0 +1,21 @@
# VERSION 2 - DO NOT REMOVE THIS LINE
RewriteEngine on
RewriteLog /var/log/httpd/rewrite.log
RewriteLogLevel 0
# By default forward all requests to /ipa. If you don't want IPA
# to be the default on your web server comment this line out.
${AUTOREDIR}RewriteRule ^/$$ https://$FQDN/ipa/ui [L,NC,R=301]
# Redirect to the fully-qualified hostname. Not redirecting to secure
# port so configuration files can be retrieved without requiring SSL.
RewriteCond %{HTTP_HOST} !^$FQDN$$ [NC]
RewriteRule ^/ipa/(.*) http://$FQDN/ipa/$$1 [L,R=301]
# Redirect to the secure port if not displaying an error or retrieving
# configuration.
RewriteCond %{SERVER_PORT} !^443$$
RewriteCond %{REQUEST_URI} !^/ipa/(errors|config)
RewriteRule ^/ipa/(.*) https://$FQDN/ipa/$$1 [L,R=301,NC]

121
install/conf/ipa.conf Normal file
View File

@ -0,0 +1,121 @@
#
# VERSION 2 - DO NOT REMOVE THIS LINE
#
# LoadModule auth_kerb_module modules/mod_auth_kerb.so
ProxyRequests Off
#We use xhtml, a file format that the browser validates
DirectoryIndex index.html
# ipa-rewrite.conf is loaded separately
# This is required so the auto-configuration works with Firefox 2+
AddType application/java-archive jar
# FIXME: WSGISocketPrefix is a server-scope directive. The mod_wsgi package
# should really be fixed by adding this its /etc/httpd/conf.d/wsgi.conf:
WSGISocketPrefix /var/run/httpd/wsgi
# Configure mod_wsgi handler for /ipa
WSGIDaemonProcess ipa processes=2 threads=1 maximum-requests=500
WSGIProcessGroup ipa
WSGIApplicationGroup ipa
WSGIImportScript /usr/share/ipa/wsgi.py process-group=ipa application-group=ipa
WSGIScriptAlias /ipa /usr/share/ipa/wsgi.py
WSGIScriptReloading Off
# Turn off mod_msgi handler for errors, config, crl:
<Location "/ipa/errors">
SetHandler None
</Location>
<Location "/ipa/config">
SetHandler None
</Location>
<Location "/ipa/crl">
SetHandler None
</Location>
# Protect /ipa with Kerberos
<Location "/ipa">
AuthType Kerberos
AuthName "Kerberos Login"
KrbMethodNegotiate on
KrbMethodK5Passwd off
KrbServiceName HTTP
KrbAuthRealms $REALM
Krb5KeyTab /etc/httpd/conf/ipa.keytab
KrbSaveCredentials on
Require valid-user
ErrorDocument 401 /ipa/errors/unauthorized.html
</Location>
# This is where we redirect on failed auth
Alias /ipa/errors "/usr/share/ipa/html"
# For the MIT Windows config files
Alias /ipa/config "/usr/share/ipa/html"
# Do no authentication on the directory that contains error messages
<Directory "/usr/share/ipa/html">
SetHandler None
AllowOverride None
Satisfy Any
Allow from all
</Directory>
# For CRL publishing
Alias /ipa/crl "/var/lib/pki-ca/publish"
<Directory "/var/lib/pki-ca/publish">
SetHandler None
AllowOverride None
Options Indexes FollowSymLinks
Satisfy Any
Allow from all
</Directory>
# webUI is now completely static, and served out of that directory
Alias /ipa/ui "/usr/share/ipa/ui"
<Directory "/usr/share/ipa/ui">
SetHandler None
AllowOverride None
Satisfy Any
Allow from all
</Directory>
# Protect our CGIs
<Directory /var/www/cgi-bin>
AuthType Kerberos
AuthName "Kerberos Login"
KrbMethodNegotiate on
KrbMethodK5Passwd off
KrbServiceName HTTP
KrbAuthRealms $REALM
Krb5KeyTab /etc/httpd/conf/ipa.keytab
KrbSaveCredentials on
Require valid-user
ErrorDocument 401 /ipa/errors/unauthorized.html
</Directory>
# migration related pages
Alias /ipa/migration "/usr/share/ipa/migration"
<Directory "/usr/share/ipa/migration">
AllowOverride None
Satisfy Any
Allow from all
Options ExecCGI
AddHandler wsgi-script .py
</Directory>

84
install/configure.ac Normal file
View File

@ -0,0 +1,84 @@
AC_PREREQ(2.59)
m4_include(../version.m4)
AC_INIT([ipa-server],
IPA_VERSION,
[https://hosted.fedoraproject.org/projects/freeipa/newticket])
#AC_CONFIG_SRCDIR([ipaserver/ipaldap.py])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign])
AM_MAINTAINER_MODE
#AC_PROG_CC
#AC_STDC_HEADERS
#AC_DISABLE_STATIC
#AC_PROG_LIBTOOL
#AC_HEADER_STDC
AC_SUBST(VERSION)
AC_PROG_MKDIR_P
AC_PROG_AWK
AC_PROG_SED
AC_PATH_PROG(XGETTEXT, xgettext, [no])
if test "x$XGETTEXT" = "xno"; then
AC_MSG_ERROR([xgettext not found, install gettext])
fi
AC_PATH_PROG(MSGFMT, msgfmt, [no])
if test "x$MSGFMT" = "xno"; then
AC_MSG_ERROR([msgfmt not found, install gettext])
fi
AC_PATH_PROG(MSGINIT, msginit, [no])
if test "x$MSGINIT" = "xno"; then
AC_MSG_ERROR([msginit not found, install gettext])
fi
AC_PATH_PROG(MSGMERGE, msgmerge, [no])
if test "x$MSGMERGE" = "xno"; then
AC_MSG_ERROR([msgmerge not found, install gettext])
fi
AC_PATH_PROG(MSGCMP, msgcmp, [no])
if test "x$MSGCMP" = "xno"; then
AC_MSG_ERROR([msgcmp not found, install gettext])
fi
AC_PATH_PROG(TX, tx, [/usr/bin/tx])
AC_ARG_WITH([gettext_domain],
[AS_HELP_STRING([--with-gettext-domain=name],
[set the name of the i18n message catalog])],
[],
[with_gettext_domain=ipa])
AC_SUBST(GETTEXT_DOMAIN, $with_gettext_domain)
dnl ---------------------------------------------------------------------------
dnl - Set the data install directory since we don't use pkgdatadir
dnl ---------------------------------------------------------------------------
IPA_DATA_DIR="$datadir/ipa"
IPA_SYSCONF_DIR="$sysconfdir/ipa"
AC_SUBST(IPA_DATA_DIR)
AC_SUBST(IPA_SYSCONF_DIR)
# Files
AC_CONFIG_FILES([
Makefile
conf/Makefile
html/Makefile
migration/Makefile
share/Makefile
ui/Makefile
tools/Makefile
tools/man/Makefile
updates/Makefile
po/Makefile
])
AC_OUTPUT

18
install/html/Makefile.am Normal file
View File

@ -0,0 +1,18 @@
NULL =
appdir = $(IPA_SYSCONF_DIR)/html
app_DATA = \
ssbrowser.html \
browserconfig.html \
unauthorized.html \
hbac-deny-remove.html \
ipa_error.css \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<script type="text/javascript" src="../ui/jquery.js"></script>
<link rel="stylesheet" type="text/css" href="../ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_error.css" />
<script type="text/javascript">
$(document).ready(function() {
if (navigator.userAgent.indexOf("Firefox") != -1 ||
navigator.userAgent.indexOf("SeaMonkey") != -1) {
$('.textblockkrb').css('display', 'block');
}
});
</script>
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<div class="textblockkrb" style="display: none;">
<h1>Configure Browser</h1>
<p> Click the below button to configure your browser </p>
<object data="jar:/ipa/errors/configure.jar!/preferences.html"
type="text/html" class="browser-config"></object>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<script type="text/javascript" src="../ui/jquery.js"></script>
<link rel="stylesheet" type="text/css" href="../ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_error.css" />
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<div class="textblockkrb">
<h1>Removal of HBAC Deny Rules.</h1>
<p>FreeIPA has dropped support for DENY rules from the HBAC
specification. </p>
<p>The former design of HBAC specifies that<p>
<ol>
<li> If no ALLOW rules match, access is denied</li>
<li> If one or more ALLOW rules match and no DENY rules match,
access is allowed</li>
<li>If one or more DENY rules match, access is denied</li>
</ol>
<p>Thus, DENY rules exist only to provide exceptions from the ALLOW
rules. There exists no ALLOW+DENY combination that cannot be
constructed from ALLOW rules only.[1]</P>
<p>DENY rules introduce a lot of edge-cases for evaluation. The most
important of which is the availability of the group membership for
the user logging in. Depending on the mechanism used to log in (for
example, GSSAPI over SSH or cross-realm Kerberos trust where the
user is provided by the PAC), SSSD's cache may not have a complete
list of groups for this user. If the login is occurring during
offline mode (where SSSD cannot contact the LDAP server to refresh
the user's groups), SSSD cannot determine whether DENY rules would
match for the user. This therefore translates into a potential
security issue.</p>
<p>We implemented a workaround in the SSSD evaluator to resolve this by
guaranteeing that we do a full lookup of all groups referenced by
rules while we are retrieving the rules from FreeIPA. However, this
requires at least one additional lookup against the LDAP server
(possibly many if there is need to resolve nestings). This results
in a significantly slower login while online.</p>
<p>We also have issues related to source host evaluation. Some
applications will provide an IP address instead of a hostname in the
pam_rhost attribute. Our only recourse here is to perform a
reverse-DNS lookup to try and identify the real hostname(s) of the
server. However, in many real-world environments, reverse DNS is
unavailable or misconfigured. In the case of ALLOW rules, this would
lead to a match failure and an implicit denial. However, a failure
to properly match a DENY rule can result in unexpected access being
granted. This is a potentially serious security issue.</p>
<p>Given these edge cases (and performance issues of the noted
workaround), The FreeIPA team decided to drop DENY rules from the
HBAC specification and limit HBAC only to ALLOW rules (which are
much safer). Beyond the obvious advantages for our implementation,
this should make it less complex for users to write their rules.</p>
<p>[1] Some rules are complex to simulate, such as "Allow access from
all PAM services EXCEPT telnet". But a safer and clearer
implementation approach does all access via whitelist. If a FreeIPA
implementation is using an exception rule, the administrators
should re-evaluate the justification.
</p>
</div>
</div>
</body>
</html>

865
install/html/ipa_error.css Normal file
View File

@ -0,0 +1,865 @@
/* Authors:
* Pavel Zuna <pzuna@redhat.com>
* Adam Young <ayoung@redhat.com>
* Endi Sukma Dewata <edewata@redhat.com>
* Kyle Baker <kybaker@redhat.com>
*
* Copyright (C) 2010 Red Hat
*/
@font-face {
font-family: 'Overpass';
src: url('../ui/overpass_regular-web.eot');
src: url('../ui/overpass_regular-web.eot?#iefix') format('eot'),
url('../ui/overpass_regular-web.woff') format('woff'),
url('../ui/overpass_regular-web.ttf') format('truetype'),
url('../ui/overpass_regular-web.svg#webfontLTZe4IYH') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Overpass Bold';
src: url('../ui/overpass_bold-web.eot');
src: url('../ui/overpass_bold-web.eot?#iefix') format('eot'),
url('../ui/overpass_bold-web.woff') format('woff'),
url('../ui/overpass_bold-web.ttf') format('truetype'),
url('../ui/overpass_bold-web.svg#webfontzAU82Ltw') format('svg');
font-weight: bold;
font-style: normal;
}
body{
background-image:url("../ui/outer-bg.png");
background-repeat:repeat-x;
background-position:left top;
background-color:#F9F9F9;
border-width: 0;
font-family:"Liberation Sans",Arial,Sans;
font-size:11px;
margin: 0;
}
.center-container {
margin-left: auto;
margin-right: auto;
width: 960px;
}
.ui-widget {
font-size: 1em;
}
.input_link {
padding: .4em 1em .4em 2em;
text-decoration: none;
position: relative;
cursor: pointer;
}
.input_link span.ui-icon {
-moz-border-radius: 0.3em;
border: 1px solid #B8B8B8;
margin: -0.9em 0.4em 0em -0.3em;
position: absolute;
left: .2em;
top: 50%;
}
/* ---- Header ---- */
div.header {
background-color:#0C3B00;
width: 100%;
height: 4em;
}
div.header a {
text-decoration: none;
}
div.header a:link {
text-decoration: none;
color: white;
}
div.header a:visited {
text-decoration: none;
color: white;
}
div.header span.header-logo {
padding-left: 2em;
}
div.header span.header-logo a img {
border: 0;
}
div.header span.header-loggedinas {
width: 96em;
color: #fff;
display: block;
padding-left: 71em;
margin-top: -2.6em;
margin-left: auto;
margin-right: 27.6em;
width: 20em;
}
/* ---- Navigation ---- */
div.tabs {
overflow: auto;
width: 100%;
height: 100%;
min-height: 40em;
}
div#content {
margin-top: 0;
position: relative;
width: 100%;
}
ul#viewtype {
padding-left: 2em;
}
ul#viewtype li {
color: #656565;
display: inline;
font-weight: bold;
list-style-type: none;
padding-right: 2em;
}
ul#viewtype li img {
vertical-align: middle;
}
ul#viewtype li a {
font-weight: normal;
}
div.content div.content-buttons {
float: right;
margin-right: 1.5em;
}
div.content div.content-buttons img {
border: 0;
}
h2 {
font-family: "Overpass Bold","Liberation Sans", Arial, sans-serif;
font-size: 1.5em;
font-weight: normal;
color: #333333;
text-transform: uppercase;
margin-left: 1em;
margin-bottom: 0;
text-align: left;
}
.section-expand{
float:left;
-moz-border-radius: 0.3em;
background-color: -moz-linear-gradient(top, #959595, #5e5e5e);
border: 1px solid #b8b8b8;
color: #fff;
margin-right: 0.5em;
margin-top: 0.1em;
}
hr {
background-color: #EEEEEE;
clear: both;
color: #FFFFFF;
height: 0.1em;
margin-left: 1.5em;
margin-right: 1.5em;
margin-top: 1em;
}
.details-section {
margin-left: 4.5em;
margin-right: 1.5em;
margin-top: 1.8em;
white-space: nowrap;
padding-bottom: 1.8em;
padding-right: 1.8em;
}
.undo {
cursor:pointer;
}
dl.entryattrs {
clear: both;
margin-left: 1.5em;
margin-top: 1.8em;
white-space: nowrap;
}
dl.entryattrs dt {
clear: left;
float: left;
padding-bottom: 1.8em;
padding-right: 1.8em;
text-align: right;
width: 16em;
margin: 0.5em -0.5em 0 -6em;
}
dl.entryattrs dd {
float: left;
padding-bottom: 1.8em;
}
dl.entryattrs dd.first {
margin-left: 0;
margin-top: 0.7em;
}
dl.entryattrs dd.other {
clear: both;
margin-left: 10.7em;
}
dl.entryattrs input {
margin-right: 0.5em;
margin-top: -1.2em;
min-width: 27.5em;
}
span.attrhint {
font-size: 8pt;
left: 5em;
margin-left: 12.5em;
position: absolute;
overflow-x: hidden;
}
/*Navigation */
.tabs1 .ui-tabs-nav{
padding-left: 2.5em;
padding-top: 2em;
margin: 0;
border: none;
background-image: url("../ui/Mainnav-background.png");
-moz-border-radius: 0;
}
.ui-tabs {
padding:0;
}
#the positions for these are in the large icon image,
#and need to be specified in pixels.
.ui-icon-plus {
background-position: -16px -129px;
}
.ui-icon-minus {
background-position: -48px -129px;
}
.ui-icon-trash {
background-position: -176px -97px;
}
.ui-widget-content .ui-icon {
background-image: url("../ui/ui-icons_222222_256x240.png");
background-color: #e2e2e2;
}
.ui-widget-content {
}
.ui-widget-content a {
text-decoration: none;
color: #1d85d5;
font-weight: normal;
}
.ui-widget-header {
background: url("../ui/modal-background.png") repeat scroll 50% 50% #1f9123;
border: 1px solid #244c16;
color: #EEEEEE;
font-weight: bold;
}
.tabs1 .ui-tabs-nav {
height: 3em;
}
.ui-widget input, .ui-widget select,
.ui-widget textarea, .ui-widget button {
font-family: "Liberation Sans", Arial, sans-serif;
font-size: 1.3em;
margin-right: .1em;
}
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
-moz-border-radius: .3em;
background: -moz-linear-gradient(top, #959595, #5e5e5e);
border: 1px solid #777777;
color: #fff;
font-weight: normal;
}
.tabs1 .ui-tabs-nav li {
-moz-border-radius: 0 !important;
background-image: url("../ui/Mainnav-offtab.png");
margin: 0;
border-width: 0;
text-align: center;
vertical-align:baseline;
}
.tabs1 .ui-tabs-nav li.ui-tabs-selected {
padding: 0 0;
background-image: url("../ui/Mainnav-ontab.png");
text-align: center;
margin: 0;
}
.tabs1 .ui-tabs-nav li a{
-moz-border-radius: 0 !important;
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
width:5.5em;
padding: none;
color: #7E7E7E;
margin: 0 auto;
text-align:center;
font-size:1.5em;
}
.tabs1 .ui-tabs-nav li > a:link, span.main-nav-off > a:visited{
color: #7E7E7E;
}
.tabs1 .ui-tabs-nav li.ui-tabs-selected a{
color: #3D752A;
}
.tabs1 .ui-tabs-panel {
display: block;
border-width: 0;
padding: 0 0 0 0;
background: none;
overflow-x: hidden;
}
.tabs2 .ui-tabs-nav {
padding: 0.3em 6em 0 4em;
margin: 0;
height: 2.4em;
background-image: url("../ui/Subnav-background.png");
}
.tabs2 .ui-tabs-nav li {
width:auto;
padding-left: 1em;
margin: 0;
background: #326122 !important;
color: white;
}
.tabs2 .ui-tabs-nav li.ui-tabs-selected {
padding-left: 1em;
height: 1em;
background: #326122 !important;
}
.tabs2 .ui-tabs-nav li a{
width:auto;
padding: 0.4em 0.6em ;
-moz-border-radius: 2em !important;
border-radius: 2em !important;
color: white;
font-size: 1em;
font-family: "Liberation Sans", Arial, Sans;
}
.tabs2 .ui-tabs-nav li > a:link, span.main-nav-off > a:visited{
color:white;
}
.tabs2 .ui-tabs-nav li a:hover{
background: none repeat scroll 0 0 #1C3612;
}
.tabs2 .ui-tabs-nav li.ui-tabs-selected a{
background: none repeat scroll 0 0 #1C3612;
color: white;
}
span.sub-nav-off > a:link, span.sub-nav-off > a:visited{
color:white;
}
span.main-nav-off > a:link, span.main-nav-off > a:visited{
color:white;
}
span.main-separator{
background: #333339;
padding:0.1em;
}
/* Entity */
.entity-container{
position: relative;
left: 22em;
width: 80%;
margin: 0.06em;
padding: 0.06em;
background: #e8e8e8;
}
.action-panel {
position: fixed;
height: 33em;
left: auto;
border: none;
float: none;
margin-top: 6.3em;
margin-left: -19.5em;
margin-right: 0;
padding-left: 0;
position: fixed;
width: 18em;
background-image:url("../ui/panel-background.png");
background-repeat:no-repeat;
background-position:right;
}
.action-panel h3{
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
color: #333333;
margin: 0;
background: #EEEEEE;
padding: .5em;
border-right: 1px solid #dfdfdf;
text-transform: uppercase;
font-size: 1.2em;
}
.action-panel ul {
list-style-type:none;
padding-left: .5em;
}
.action-panel h3{
margin: 0;
background: #e8e8e8;
}
.action-panel li {
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
font-size: 1.1em;
color: #1d85d5;
list-style-type: none;
min-height: 2.1em;
padding: none;
}
.action-panel li.search-facet {
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
color: #1D85D5;
cursor: pointer;
text-transform: uppercase;
font-size: 1.2em;
}
.action-panel li.entity-facet {
font-family: "Liberation Sans",Arial,sans-serif;
color: #1d85d5;
cursor: pointer;
margin-left:1.2em;
text-transform: none;
}
.action-panel li.entity-facet-selected {
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
color: black;
text-transform: uppercase;
cursor: pointer;
}
.action-panel li.entity-facet-disabled {
font-family: "Liberation Sans",Arial,sans-serif;
color: gray;
cursor: default;
text-decoration: none;
text-transform: none;
}
.action-panel li.entity-facet-relation-label {
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
color: #8a8a8a;
cursor: default;
text-transform: uppercase;
margin-left:1.8em;
}
.action-panel li.facet-group-member {
margin-left: 2.9em
}
.action-button {
background: none;
background-image:none;
font-family: "Liberation Sans", Arial, sans-serif;
font-size: 0.9em;
}
.action-controls {
position: relative;
display:inline;
}
.client {
font-size: 10px;
margin-top: 0.4em;
float: left;
min-width: 70em;
}
/* Migration */
body#header-bg {
background: url("../ui/Static-Background.png") repeat-x scroll left top #EDEDED;
}
.container_1 {
margin-left: auto;
margin-right: auto;
width: 960px;
background: url("../ui/centered-bg.png") no-repeat scroll 0 7em transparent;
min-height: 40em;
}
#formwindow {
-moz-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.6);
background: none repeat scroll 0 0 #FFFFFF;
border-color: #FFFFFF #F0F0F0 #F0F0F0;
border-right: 1px solid #F0F0F0;
border-style: solid;
border-width: 1px;
color: #3F3F3F;
margin: 40px auto 100px;
width: 450px;
}
.formcontent {
padding: 0 1em 2em 2em;
}
#error-box {
-moz-border-radius: 0.3em 0.3em 0.3em 0.3em;
background-color: #FFEBE8;
border: 1px solid #DD3C10;
margin: 0 2em 1em;
padding: 1em 1em 0 0;
}
.# {
background: url("../ui/ipalogo.png") no-repeat scroll left top transparent;
border: none;
float: left;
height: 36px;
width: 205px;
}
#formwindow h4 {
background-color: #F0F0F0;
font-size: 1.6em;
padding: 18px 15px 14px 22px;
text-transform: uppercase;
margin-top: 0;
}
#login li {
padding-bottom: 15px;
text-align: right;
width: 370px;
list-style-type: none;
}
#login li input {
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset;
margin-left: 15px;
padding: 2px 10px;
width: 248px;
}
#login li label, #modal li label {
font-weight: bold;
font-size: 1.2em;
list-style-type: none;
}
form#login {
display: inline-block;
padding-bottom: 15px;
width: 418px;
}
.formbutton input {
float: right;
margin: 1em 1em 1em 0;
-moz-border-radius: 0.3em 0.3em 0.3em 0.3em;
background: -moz-linear-gradient(center top, #959595, #5e5e5e) repeat scroll 0 0 transparent;
border: 1px solid #777777;
color: #ffffff;
font-weight: normal;
padding: 0.5em 0.8em;
}
.textblock {
text-align: center;
margin-top: 6em;
font-size: 1.1em;
}
.textblockkrb {
text-align: left;
margin-top: 5em;
font-size: 1.1em;
padding-left: 3em;
padding-right: 3em;
}
.textblockkrb ul li {
list-style-type: none;
padding: .15em;
}
h3 {
color: #333333;
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
font-size: 1.5em;
font-weight: normal;
text-transform: uppercase;
}
h5 {
color: #333333;
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
font-size: 1em;
font-weight: normal;
text-transform: uppercase;
margin-bottom: 3em;
margin-left: 5em;
margin-top: -3em;
}
/* Browser configuration */
object.browser-config {
width: 100%;
}
/* Search */
.search-controls {
-moz-border-radius: .7em .7em 0 0;
height:2.5em;
background: -moz-linear-gradient(top, #eeeeee, #dfdfdf);
position: relative;
padding: 1em 1.5em;
margin-top: 1.5em;
}
.search-table > a:link,a:visted{
color:black;
}
.search-table{
padding: 0;
width:100%;
border: none;
}
.search-table td{
padding-left: 0.5em;
}
.search-table th{
padding-left: 0.5em;
background-color:#f6f6f6;
color:#333333;
text-align: left;
border: 1px solid #dfdfdf;
}
.search-table tfoot tr td span{
border-top: 1px solid #dfdfdf;
padding: 0.9em 0 0 1em;
display: block;
margin-top: 2em;
}
.search-table tr:nth-child(even){
# background-color:#CCC;
}
.search-table tr:nth-child(odd){
# background-color:#FFF;
}
.entity-views{
list-style-type:none;
}
.entity-views li {
display:inline;
cursor: pointer;
padding: 0.4em;
}
.strikethrough { text-decoration: line-through; }
.key-status-valid {
list-style-type: circle;
color: #008000;
}
.key-status-missing {
list-style-type: circle;
color: #daa520;
}
.key-status-active {
list-style-type: disc;
}
.certificate-status-valid {
list-style-type: circle;
color: #008000;
}
.certificate-status-revoked {
list-style-type: circle;
color: #ff0000;
}
.certificate-status-missing {
list-style-type: circle;
color: #daa520;
}
.certificate-status-active {
list-style-type: disc;
}
dl.modal {
clear: both;
margin-left: 1em;
margin-top: 1em;
white-space: nowrap;
}
dl.modal dt {
clear: left;
float: left;
padding-bottom: 0;
padding-right: 0;
text-align: right;
width: 10em;
}
dl.modal dd {
float: left;
padding-bottom: 0;
margin-left: 0.8em;
}
.ui-widget-content {
border:0;
}
table.scrollable thead {
display: block;
}
table.scrollable tbody {
display: block;
overflow: auto;
}
.adder-dialog-filter {
height: 2.5em;
}
.adder-dialog-results {
position: relative;
height: 20.0em;
}
.adder-dialog-available {
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 25.0em;
}
.adder-dialog-buttons {
position: absolute;
top: 1.5em;
left: 25em;
right: 25;
bottom: 0;
text-align: center;
}
.adder-dialog-selected {
border: 1px solid black;
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 25em;
}
.adder-dialog-internal {
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
bottom: 4.5em;
width: 25em;
}
.adder-dialog-external {
border: 1px solid black;
position: absolute;
left: 0;
bottom: 0;
width: 25em;
height: 4em;
}

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<script type="text/javascript" src="../ui/jquery.js"></script>
<link rel="stylesheet" type="text/css" href="../ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_error.css" />
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<div class="textblockkrb">
<h3>Browser Kerberos Setup</h3>
<img alt="Internet Explorer" src="../ui/ie-icon.png"><h5>Internet Explorer Configuration</h5>
<p>Once you are able to log into the workstation with your kerberos key you are now able to use that ticket in Internet Explorer. </p>
<strong>Login to the Windows machine using an account of your Kerberos realm (administrative domain)</strong><br>
<strong>In Internet Explorer, click Tools, and then click Internet Options.</strong>
<br>
<ul>
<li> 1. Click the Security tab </li>
<li> 2. Click Local intranet </li>
<li> 3. Click Sites </li>
<li> 4. Click Advanced </li>
<li> 5. Add your domain to the list </li>
<br>
<li> 1. Click the Security tab </li>
<li> 2. Click Local intranet </li>
<li> 3. Click Custom Level </li>
<li> 4. Select Automatic logon only in Intranet zone </li>
<br>
<li> Visit a kerberized web site using IE (You must use the fully-qualified Domain Name in the URL)</li>
<li><strong> You are all set. </strong></li>
</ul>
<br>
<img alt="Firefox" src="../ui/firefox-icon.png"><h5>Firefox Configuration</h5>
<p>You can configure Firefox to use Kerberos for Single Sign-on. The following instructions will guide you in configuring your web browser <br>
to send your Kerberos credentials to the appropriate Key Distribution Center which enables Single Sign-on. </p>
<ul><li> 1. In the address bar of Firefox, type <tt>about:config</tt> to display the list of current configuration options.</li>
<li> 2. In the Filter field, type <tt>negotiate</tt> to restrict the list of options. </li>
<li> 3. Double-click the <tt>network.negotiate-auth.trusted-uris</tt> entry to display the Enter string value dialog box. </li>
<li> 4. Enter the name of the domain against which you want to authenticate, for example, <tt>.example.com.</tt> </li>
<li> 5. Repeat the above procedure for the <tt>network.negotiate-auth.delegation-uris</tt> entry, using the same domain. </li>
<br>
<li><strong> You are all set. </strong></li>
</ul>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<script type="text/javascript" src="../ui/jquery.js"></script>
<link rel="stylesheet" type="text/css" href="../ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_error.css" />
<script type="text/javascript">
$(document).ready(function() {
$("#import-cert-auth-link").click(function() {
$("#first-time").css("display", "none");
$("#next-link").css("display", "block");
return true;
});
});
</script>
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<div class="textblockkrb">
<h1>Unable to verify your Kerberos credentials</h1>
<p>
Please make sure that you have valid Kerberos tickets (obtainable via <b>kinit</b>),
and that you have configured your browser correctly.
</p>
<div id="first-time">
<b>If this is your first time:</b>
<ul>
<li><a id="import-cert-auth-link" href="/ipa/errors/ca.crt">Click here to
Import the IPA Certificate Authority.</a></li>
<li>Make sure you select <b>all three</b> checkboxes.</li>
<li>Click the <b>OK</b> Button.</li>
</ul>
</div>
<div id="next-link" style="display: none;">
<p><a href="browserconfig.html">Click here to continue.</a></p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,18 @@
NULL =
appdir = $(IPA_DATA_DIR)/migration
app_DATA = \
error.html \
index.html \
invalid.html \
ipa_migration.css \
migration.py \
$(NULL)
EXTRA_DIST = \
$(app_DATA) \
$(NULL)
MAINTAINERCLEANFILES = \
*~ \
Makefile.in

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<link rel="stylesheet" type="text/css" href="../ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_migration.css" />
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<br>
<br>
<div id="formwindow">
<h4>We're Sorry</h4>
<div class="formcontent">
<p>
<strong>There was a problem with your request. Please, try again later.</strong>
</p>
<p>
<label>If the problem persists, contact your administrator.</label>
</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<link rel="stylesheet" type="text/css" href="../ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_migration.css" />
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<div class="textblock">
<h3>Password Migration</h3>
<p>If you have been sent here by your administrator, your personal
information is being migrated to a new identity management solution
(IPA).</p>
<p>Please, enter your credentials in the form below to complete the
process. Upon successful login your kerberos account will be
activated.</p>
</div>
<div id="formwindow">
<h4>Login</h4>
<form id="login" action="migration.py" method="post" name="">
<ul>
<li>
<label for="username">Username:</label>
<input type="text" name="username" value="" accesskey="u" />
</li>
<li>
<label for="password">Password:</label>
<input type="password" name="password" value="" accesskey="p" />
</li>
<ul>
<div class="formbutton">
<input name="submit" value="Migrate" type="submit" />
</div>
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IPA: Identity Policy Audit</title>
<link rel="stylesheet" type="text/css" href="..ui/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="ipa_migration.css" />
</head>
<body id="header-bg">
<div class="container_1">
<div class="header-logo">
<img src="../ui/ipalogo.png" /><img src="../ui/ipabanner.png" />
</div>
<div class="textblock">
<h3>Password Migration</h3>
<p>If you have been sent here by your administrator, your personal
information is being migrated to a new identity management solution
(IPA).</p>
<p>Please, enter your credentials in the form below to complete the
process. Upon successful login your kerberos account will be
activated.</p>
</div>
<div id="formwindow">
<h4>Login</h4>
<div id="error-box">
<div class="formcontent">
<p><strong>Please re-enter your username or password</strong></p>
<p>The password or username you entered is incorrect. Please try again (make sure your caps lock is off).</p>
<p>If the problem persists, contact your administrator.</p>
</div>
</div>
<form id="login" action="migration.py" method="post" name="">
<ul>
<li>
<label for="username">Username:</label>
<input type="text" name="username" value="" accesskey="u" />
</li>
<li>
<label for="password">Password:</label>
<input type="password" name="password" value="" accesskey="p" />
</li>
<ul>
<div class="formbutton">
<input name="submit" value="Migrate" type="submit" />
</div>
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,863 @@
/* Authors:
* Pavel Zuna <pzuna@redhat.com>
* Adam Young <ayoung@redhat.com>
* Endi Sukma Dewata <edewata@redhat.com>
* Kyle Baker <kybaker@redhat.com>
*
* Copyright (C) 2010 Red Hat
*/
@font-face {
font-family: 'Overpass';
src: url('../ui/overpass_regular-web.eot');
src: url('../ui/overpass_regular-web.eot?#iefix') format('eot'),
url('../ui/overpass_regular-web.woff') format('woff'),
url('../ui/overpass_regular-web.ttf') format('truetype'),
url('../ui/overpass_regular-web.svg#webfontLTZe4IYH') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Overpass Bold';
src: url('../ui/overpass_bold-web.eot');
src: url('../ui/overpass_bold-web.eot?#iefix') format('eot'),
url('../ui/overpass_bold-web.woff') format('woff'),
url('../ui/overpass_bold-web.ttf') format('truetype'),
url('../ui/overpass_bold-web.svg#webfontzAU82Ltw') format('svg');
font-weight: bold;
font-style: normal;
}
body{
background-image:url("../ui/outer-bg.png");
background-repeat:repeat-x;
background-position:left top;
background-color:#F9F9F9;
border-width: 0;
font-family:"Liberation Sans",Arial,Sans;
font-size:11px;
margin: 0;
}
.center-container {
margin-left: auto;
margin-right: auto;
width: 960px;
}
.ui-widget {
font-size: 1em;
}
.input_link {
padding: .4em 1em .4em 2em;
text-decoration: none;
position: relative;
cursor: pointer;
}
.input_link span.ui-icon {
-moz-border-radius: 0.3em;
border: 1px solid #B8B8B8;
margin: -0.9em 0.4em 0em -0.3em;
position: absolute;
left: .2em;
top: 50%;
}
/* ---- Header ---- */
div.header {
background-color:#0C3B00;
width: 100%;
height: 4em;
}
div.header a {
text-decoration: none;
}
div.header a:link {
text-decoration: none;
color: white;
}
div.header a:visited {
text-decoration: none;
color: white;
}
div.header span.header-logo {
padding-left: 2em;
}
div.header span.header-logo a img {
border: 0;
}
div.header span.header-loggedinas {
width: 96em;
color: #fff;
display: block;
padding-left: 71em;
margin-top: -2.6em;
margin-left: auto;
margin-right: 27.6em;
width: 20em;
}
/* ---- Navigation ---- */
div.tabs {
overflow: auto;
width: 100%;
height: 100%;
min-height: 40em;
}
div#content {
margin-top: 0;
position: relative;
width: 100%;
}
ul#viewtype {
padding-left: 2em;
}
ul#viewtype li {
color: #656565;
display: inline;
font-weight: bold;
list-style-type: none;
padding-right: 2em;
}
ul#viewtype li img {
vertical-align: middle;
}
ul#viewtype li a {
font-weight: normal;
}
div.content div.content-buttons {
float: right;
margin-right: 1.5em;
}
div.content div.content-buttons img {
border: 0;
}
h2 {
font-family: "Overpass Bold","Liberation Sans", Arial, sans-serif;
font-size: 1.5em;
font-weight: normal;
color: #333333;
text-transform: uppercase;
margin-left: 1em;
margin-bottom: 0;
text-align: left;
}
.section-expand{
float:left;
-moz-border-radius: 0.3em;
background-color: -moz-linear-gradient(top, #959595, #5e5e5e);
border: 1px solid #b8b8b8;
color: #fff;
margin-right: 0.5em;
margin-top: 0.1em;
}
hr {
background-color: #EEEEEE;
clear: both;
color: #FFFFFF;
height: 0.1em;
margin-left: 1.5em;
margin-right: 1.5em;
margin-top: 1em;
}
.details-section {
margin-left: 4.5em;
margin-right: 1.5em;
margin-top: 1.8em;
white-space: nowrap;
padding-bottom: 1.8em;
padding-right: 1.8em;
}
.undo {
cursor:pointer;
}
dl.entryattrs {
clear: both;
margin-left: 1.5em;
margin-top: 1.8em;
white-space: nowrap;
}
dl.entryattrs dt {
clear: left;
float: left;
padding-bottom: 1.8em;
padding-right: 1.8em;
text-align: right;
width: 16em;
margin: 0.5em -0.5em 0 -6em;
}
dl.entryattrs dd {
float: left;
padding-bottom: 1.8em;
}
dl.entryattrs dd.first {
margin-left: 0;
margin-top: 0.7em;
}
dl.entryattrs dd.other {
clear: both;
margin-left: 10.7em;
}
dl.entryattrs input {
margin-right: 0.5em;
margin-top: -1.2em;
min-width: 27.5em;
}
span.attrhint {
font-size: 8pt;
left: 5em;
margin-left: 12.5em;
position: absolute;
overflow-x: hidden;
}
/*Navigation */
.tabs1 .ui-tabs-nav{
padding-left: 2.5em;
padding-top: 2em;
margin: 0;
border: none;
background-image: url("../ui/Mainnav-background.png");
-moz-border-radius: 0;
}
.ui-tabs {
padding:0;
}
#the positions for these are in the large icon image,
#and need to be specified in pixels.
.ui-icon-plus {
background-position: -16px -129px;
}
.ui-icon-minus {
background-position: -48px -129px;
}
.ui-icon-trash {
background-position: -176px -97px;
}
.ui-widget-content .ui-icon {
background-image: url("../ui/ui-icons_222222_256x240.png");
background-color: #e2e2e2;
}
.ui-widget-content {
}
.ui-widget-content a {
text-decoration: none;
color: #1d85d5;
font-weight: normal;
}
.ui-widget-header {
background: url("../ui/modal-background.png") repeat scroll 50% 50% #1f9123;
border: 1px solid #244c16;
color: #EEEEEE;
font-weight: bold;
}
.tabs1 .ui-tabs-nav {
height: 3em;
}
.ui-widget input, .ui-widget select,
.ui-widget textarea, .ui-widget button {
font-family: "Liberation Sans", Arial, sans-serif;
font-size: 1.3em;
margin-right: .1em;
}
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
-moz-border-radius: .3em;
background: -moz-linear-gradient(top, #959595, #5e5e5e);
border: 1px solid #777777;
color: #fff;
font-weight: normal;
}
.tabs1 .ui-tabs-nav li {
-moz-border-radius: 0 !important;
background-image: url("../ui/Mainnav-offtab.png");
margin: 0;
border-width: 0;
text-align: center;
vertical-align:baseline;
}
.tabs1 .ui-tabs-nav li.ui-tabs-selected {
padding: 0 0;
background-image: url("../ui/Mainnav-ontab.png");
text-align: center;
margin: 0;
}
.tabs1 .ui-tabs-nav li a{
-moz-border-radius: 0 !important;
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
width:5.5em;
padding: none;
color: #7E7E7E;
margin: 0 auto;
text-align:center;
font-size:1.5em;
}
.tabs1 .ui-tabs-nav li > a:link, span.main-nav-off > a:visited{
color: #7E7E7E;
}
.tabs1 .ui-tabs-nav li.ui-tabs-selected a{
color: #3D752A;
}
.tabs1 .ui-tabs-panel {
display: block;
border-width: 0;
padding: 0 0 0 0;
background: none;
overflow-x: hidden;
}
.tabs2 .ui-tabs-nav {
padding: 0.3em 6em 0 4em;
margin: 0;
height: 2.4em;
background-image: url("../ui/Subnav-background.png");
}
.tabs2 .ui-tabs-nav li {
width:auto;
padding-left: 1em;
margin: 0;
background: #326122 !important;
color: white;
}
.tabs2 .ui-tabs-nav li.ui-tabs-selected {
padding-left: 1em;
height: 1em;
background: #326122 !important;
}
.tabs2 .ui-tabs-nav li a{
width:auto;
padding: 0.4em 0.6em ;
-moz-border-radius: 2em !important;
border-radius: 2em !important;
color: white;
font-size: 1em;
font-family: "Liberation Sans", Arial, Sans;
}
.tabs2 .ui-tabs-nav li > a:link, span.main-nav-off > a:visited{
color:white;
}
.tabs2 .ui-tabs-nav li a:hover{
background: none repeat scroll 0 0 #1C3612;
}
.tabs2 .ui-tabs-nav li.ui-tabs-selected a{
background: none repeat scroll 0 0 #1C3612;
color: white;
}
span.sub-nav-off > a:link, span.sub-nav-off > a:visited{
color:white;
}
span.main-nav-off > a:link, span.main-nav-off > a:visited{
color:white;
}
span.main-separator{
background: #333339;
padding:0.1em;
}
/* Entity */
.entity-container{
position: relative;
left: 22em;
width: 80%;
margin: 0.06em;
padding: 0.06em;
background: #e8e8e8;
}
.action-panel {
position: fixed;
height: 33em;
left: auto;
border: none;
float: none;
margin-top: 6.3em;
margin-left: -19.5em;
margin-right: 0;
padding-left: 0;
position: fixed;
width: 18em;
background-image:url("../ui/panel-background.png");
background-repeat:no-repeat;
background-position:right;
}
.action-panel h3{
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
color: #333333;
margin: 0;
background: #EEEEEE;
padding: .5em;
border-right: 1px solid #dfdfdf;
text-transform: uppercase;
font-size: 1.2em;
}
.action-panel ul {
list-style-type:none;
padding-left: .5em;
}
.action-panel h3{
margin: 0;
background: #e8e8e8;
}
.action-panel li {
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
font-size: 1.1em;
color: #1d85d5;
list-style-type: none;
min-height: 2.1em;
padding: none;
}
.action-panel li.search-facet {
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
color: #1D85D5;
cursor: pointer;
text-transform: uppercase;
font-size: 1.2em;
}
.action-panel li.entity-facet {
font-family: "Liberation Sans",Arial,sans-serif;
color: #1d85d5;
cursor: pointer;
margin-left:1.2em;
text-transform: none;
}
.action-panel li.entity-facet-selected {
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
color: black;
text-transform: uppercase;
cursor: pointer;
}
.action-panel li.entity-facet-disabled {
font-family: "Liberation Sans",Arial,sans-serif;
color: gray;
cursor: default;
text-decoration: none;
text-transform: none;
}
.action-panel li.entity-facet-relation-label {
font-family: "Overpass Bold", "Liberation Sans", Arial, Sans;
color: #8a8a8a;
cursor: default;
text-transform: uppercase;
margin-left:1.8em;
}
.action-panel li.facet-group-member {
margin-left: 2.9em
}
.action-button {
background: none;
background-image:none;
font-family: "Liberation Sans", Arial, sans-serif;
font-size: 0.9em;
}
.action-controls {
position: relative;
display:inline;
}
.client {
font-size: 10px;
margin-top: 0.4em;
float: left;
min-width: 70em;
}
/* Migration */
body#header-bg {
background: url("../ui/Static-Background.png") repeat-x scroll left top #EDEDED;
}
.container_1 {
margin-left: auto;
margin-right: auto;
width: 960px;
background: url("../ui/centered-bg.png") no-repeat scroll 0 7em transparent;
min-height: 40em;
}
#formwindow {
-moz-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.6);
background: none repeat scroll 0 0 #FFFFFF;
border-color: #FFFFFF #F0F0F0 #F0F0F0;
border-right: 1px solid #F0F0F0;
border-style: solid;
border-width: 1px;
color: #3F3F3F;
margin: 40px auto 100px;
width: 450px;
}
.formcontent {
padding: 0 1em 2em 2em;
}
#error-box {
-moz-border-radius: 0.3em 0.3em 0.3em 0.3em;
background-color: #FFEBE8;
border: 1px solid #DD3C10;
margin: 0 2em 1em;
padding: 1em 1em 0 0;
}
.# {
background: url("../ui/ipalogo.png") no-repeat scroll left top transparent;
border: none;
float: left;
height: 36px;
width: 205px;
}
#formwindow h4 {
background-color: #F0F0F0;
font-size: 1.6em;
padding: 18px 15px 14px 22px;
text-transform: uppercase;
margin-top: 0;
}
#login li {
padding-bottom: 15px;
text-align: right;
width: 370px;
list-style-type: none;
}
#login li input {
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset;
margin-left: 15px;
padding: 2px 10px;
width: 248px;
}
#login li label, #modal li label {
font-weight: bold;
font-size: 1.2em;
list-style-type: none;
}
form#login {
display: inline-block;
padding-bottom: 15px;
width: 418px;
}
.formbutton input {
float: right;
margin: 1em 1em 1em 0;
-moz-border-radius: 0.3em 0.3em 0.3em 0.3em;
background: -moz-linear-gradient(center top, #959595, #5e5e5e) repeat scroll 0 0 transparent;
border: 1px solid #777777;
color: #ffffff;
font-weight: normal;
padding: 0.5em 0.8em;
}
.textblock {
text-align: center;
margin-top: 6em;
font-size: 1.1em;
}
.textblockkrb {
text-align: left;
margin-top: 5em;
font-size: 1.1em;
padding-left: 3em;
}
.textblockkrb ul li {
list-style-type: none;
padding: .15em;
}
h3 {
color: #333333;
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
font-size: 1.5em;
font-weight: normal;
text-transform: uppercase;
}
h5 {
color: #333333;
font-family: "Overpass Bold", "Liberation Sans", Arial, sans-serif;
font-size: 1em;
font-weight: normal;
text-transform: uppercase;
margin-bottom: 3em;
margin-left: 5em;
margin-top: -3em;
}
/* Search */
.search-controls {
-moz-border-radius: .7em .7em 0 0;
height:2.5em;
background: -moz-linear-gradient(top, #eeeeee, #dfdfdf);
position: relative;
padding: 1em 1.5em;
margin-top: 1.5em;
}
.search-table > a:link,a:visted{
color:black;
}
.search-table{
padding: 0;
width:100%;
border: none;
}
.search-table td{
padding-left: 0.5em;
}
.search-table th{
padding-left: 0.5em;
background-color:#f6f6f6;
color:#333333;
text-align: left;
border: 1px solid #dfdfdf;
}
.search-table tfoot tr td span{
border-top: 1px solid #dfdfdf;
padding: 0.9em 0 0 1em;
display: block;
margin-top: 2em;
}
.search-table tr:nth-child(even){
# background-color:#CCC;
}
.search-table tr:nth-child(odd){
# background-color:#FFF;
}
.entity-views{
list-style-type:none;
}
.entity-views li {
display:inline;
cursor: pointer;
padding: 0.4em;
}
.strikethrough { text-decoration: line-through; }
.key-status-valid {
list-style-type: circle;
color: #008000;
}
.key-status-missing {
list-style-type: circle;
color: #daa520;
}
.key-status-active {
list-style-type: disc;
}
.certificate-status-valid {
list-style-type: circle;
color: #008000;
}
.certificate-status-revoked {
list-style-type: circle;
color: #ff0000;
}
.certificate-status-missing {
list-style-type: circle;
color: #daa520;
}
.certificate-status-active {
list-style-type: disc;
}
dl.modal {
clear: both;
margin-left: 1em;
margin-top: 1em;
white-space: nowrap;
}
dl.modal dt {
clear: left;
float: left;
padding-bottom: 0;
padding-right: 0;
text-align: right;
width: 10em;
}
dl.modal dd {
float: left;
padding-bottom: 0;
margin-left: 0.8em;
}
.ui-widget-content {
border:0;
}
table.scrollable thead {
display: block;
}
table.scrollable tbody {
display: block;
overflow: auto;
}
.adder-dialog-filter {
height: 2.5em;
}
.adder-dialog-results {
position: relative;
height: 20.0em;
}
.adder-dialog-available {
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 25.0em;
}
.adder-dialog-buttons {
position: absolute;
top: 1.5em;
left: 25em;
right: 25;
bottom: 0;
text-align: center;
}
.adder-dialog-selected {
border: 1px solid black;
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 25em;
}
.adder-dialog-internal {
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
bottom: 4.5em;
width: 25em;
}
.adder-dialog-external {
border: 1px solid black;
position: absolute;
left: 0;
bottom: 0;
width: 25em;
height: 4em;
}

572
install/migration/jquery-ui.css vendored Normal file
View File

@ -0,0 +1,572 @@
/*
* jQuery UI CSS Framework @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,%20Arial,%20sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=0px&bgColorHeader=333333&bgTextureHeader=14_loop.png&bgImgOpacityHeader=8&borderColorHeader=a3a3a3&fcHeader=eeeeee&iconColorHeader=bbbbbb&bgColorContent=f9f9f9&bgTextureContent=04_highlight_hard.png&bgImgOpacityContent=100&borderColorContent=cccccc&fcContent=222222&iconColorContent=222222&bgColorDefault=111111&bgTextureDefault=02_glass.png&bgImgOpacityDefault=40&borderColorDefault=777777&fcDefault=e3e3e3&iconColorDefault=ededed&bgColorHover=1c1c1c&bgTextureHover=02_glass.png&bgImgOpacityHover=55&borderColorHover=000000&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=cccccc&fcActive=222222&iconColorActive=222222&bgColorHighlight=ffeb80&bgTextureHighlight=06_inset_hard.png&bgImgOpacityHighlight=55&borderColorHighlight=ffde2e&fcHighlight=363636&iconColorHighlight=4ca300&bgColorError=cd0a0a&bgTextureError=06_inset_hard.png&bgImgOpacityError=45&borderColorError=9e0505&fcError=ffffff&iconColorError=ffcf29&bgColorOverlay=aaaaaa&bgTextureOverlay=04_highlight_hard.png&bgImgOpacityOverlay=40&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=03_highlight_soft.png&bgImgOpacityShadow=50&opacityShadow=20&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana, Arial, sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana, Arial, sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #cccccc; background: #f9f9f9 url(ui-bg_highlight-hard_100_f9f9f9_1x100.png) 50% top repeat-x; color: #222222; }
.ui-widget-content a { color: #222222; }
.ui-widget-header { border: 1px solid #a3a3a3; background: #333333 url(ui-bg_loop_8_333333_21x21.png) 50% 50% repeat; color: #eeeeee; font-weight: bold; }
.ui-widget-header a { color: #eeeeee; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #777777; background: #111111 url(ui-bg_glass_40_111111_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #e3e3e3; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #e3e3e3; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #000000; background: #1c1c1c url(ui-bg_glass_55_1c1c1c_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }
.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #cccccc; background: #ffffff url(ui-bg_flat_65_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #222222; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #222222; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #ffde2e; background: #ffeb80 url(ui-bg_inset-hard_55_ffeb80_1x100.png) 50% bottom repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #9e0505; background: #cd0a0a url(ui-bg_inset-hard_45_cd0a0a_1x100.png) 50% bottom repeat-x; color: #ffffff; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(ui-icons_bbbbbb_256x240.png); }
.ui-state-default .ui-icon { background-image: url(ui-icons_ededed_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(ui-icons_ffffff_256x240.png); }
.ui-state-active .ui-icon {background-image: url(ui-icons_222222_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(ui-icons_4ca300_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(ui-icons_ffcf29_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-tl { -moz-border-radius-topleft: 0px; -webkit-border-top-left-radius: 0px; border-top-left-radius: 0px; }
.ui-corner-tr { -moz-border-radius-topright: 0px; -webkit-border-top-right-radius: 0px; border-top-right-radius: 0px; }
.ui-corner-bl { -moz-border-radius-bottomleft: 0px; -webkit-border-bottom-left-radius: 0px; border-bottom-left-radius: 0px; }
.ui-corner-br { -moz-border-radius-bottomright: 0px; -webkit-border-bottom-right-radius: 0px; border-bottom-right-radius: 0px; }
.ui-corner-top { -moz-border-radius-topleft: 0px; -webkit-border-top-left-radius: 0px; border-top-left-radius: 0px; -moz-border-radius-topright: 0px; -webkit-border-top-right-radius: 0px; border-top-right-radius: 0px; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 0px; -webkit-border-bottom-left-radius: 0px; border-bottom-left-radius: 0px; -moz-border-radius-bottomright: 0px; -webkit-border-bottom-right-radius: 0px; border-bottom-right-radius: 0px; }
.ui-corner-right { -moz-border-radius-topright: 0px; -webkit-border-top-right-radius: 0px; border-top-right-radius: 0px; -moz-border-radius-bottomright: 0px; -webkit-border-bottom-right-radius: 0px; border-bottom-right-radius: 0px; }
.ui-corner-left { -moz-border-radius-topleft: 0px; -webkit-border-top-left-radius: 0px; border-top-left-radius: 0px; -moz-border-radius-bottomleft: 0px; -webkit-border-bottom-left-radius: 0px; border-bottom-left-radius: 0px; }
.ui-corner-all { -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(ui-bg_highlight-hard_40_aaaaaa_1x100.png) 50% top repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(ui-bg_highlight-soft_50_aaaaaa_1x100.png) 50% top repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
* jQuery UI Resizable @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
* jQuery UI Selectable @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Selectable#theming
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
* jQuery UI Accordion @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Accordion#theming
*/
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }/*
* jQuery UI Autocomplete @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Autocomplete#theming
*/
.ui-autocomplete { position: absolute; cursor: default; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* jQuery UI Menu @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
/*
* jQuery UI Button @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Button#theming
*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
* jQuery UI Dialog @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
* jQuery UI Slider @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Slider#theming
*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
* jQuery UI Tabs @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Tabs#theming
*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/*
* jQuery UI Datepicker @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/*
* jQuery UI Progressbar @VERSION
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Progressbar#theming
*/
.ui-progressbar { height:2em; text-align: left; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }

View File

@ -0,0 +1,120 @@
# Authors:
# Pavel Zuna <pzuna@redhat.com>
#
# Copyright (C) 2009 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Password migration script
"""
import cgi
import errno
import glob
import ldap
import wsgiref
import logging
from ipapython.ipautil import get_ipa_basedn
BASE_DN = ''
LDAP_URI = 'ldaps://localhost:636'
def convert_exception(error):
"""
Convert an LDAP exception into something more readable.
"""
if not isinstance(error, ldap.TIMEOUT):
desc = error.args[0]['desc'].strip()
info = error.args[0].get('info', '').strip()
else:
desc = ''
info = ''
return '%s (%s)' % (desc, info)
def wsgi_redirect(start_response, loc):
start_response('302 Found', [('Location', loc)])
return []
def get_ui_url(environ):
full_url = wsgiref.util.request_uri(environ)
index = full_url.rfind(environ.get('SCRIPT_NAME',''))
if index == -1:
raise ValueError('Cannot strip the script URL from full URL "%s"' % full_url)
return full_url[:index] + "/ipa/ui"
def get_base_dn():
"""
Retrieve LDAP server base DN.
"""
global BASE_DN
if BASE_DN:
return BASE_DN
try:
conn = ldap.initialize(LDAP_URI)
conn.simple_bind_s('', '')
BASE_DN = get_ipa_basedn(conn)
except ldap.LDAPError, e:
logging.error('migration context search failed: %s' % e)
return ''
finally:
conn.unbind_s()
return BASE_DN
def bind(username, password):
base_dn = get_base_dn()
if not base_dn:
logging.error('migration unable to get base dn')
raise IOError(errno.EIO, 'Cannot get Base DN')
bind_dn = 'uid=%s,cn=users,cn=accounts,%s' % (username, base_dn)
try:
conn = ldap.initialize(LDAP_URI)
conn.simple_bind_s(bind_dn, password)
except (ldap.INVALID_CREDENTIALS, ldap.UNWILLING_TO_PERFORM,
ldap.NO_SUCH_OBJECT), e:
logging.error('migration invalid credentials for %s: %s' % (bind_dn, convert_exception(e)))
raise IOError(errno.EPERM, 'Invalid LDAP credentials for user %s' % username)
except ldap.LDAPError, e:
logging.error('migration bind failed: %s' % convert_exception(e))
raise IOError(errno.EIO, 'Bind error')
finally:
conn.unbind_s()
def application(environ, start_response):
global LDAP_URI
if environ.get('REQUEST_METHOD', None) != 'POST':
return wsgi_redirect(start_response, 'index.html')
form_data = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
if not form_data.has_key('username') or not form_data.has_key('password'):
return wsgi_redirect(start_response, 'invalid.html')
slapd_sockets = glob.glob('/var/run/slapd-*.socket')
if slapd_sockets:
LDAP_URI = 'ldapi://%s' % slapd_sockets[0].replace('/', '%2f')
try:
bind(form_data['username'].value, form_data['password'].value)
except IOError as err:
if err.errno == errno.EPERM:
return wsgi_redirect(start_response, 'invalid.html')
if err.errno == errno.EIO:
return wsgi_redirect(start_response, 'error.html')
ui_url = get_ui_url(environ)
return wsgi_redirect(start_response, ui_url)

22
install/po/LINGUAS Normal file
View File

@ -0,0 +1,22 @@
# Languages in the sort order on Transifex
as # Assamese
bn_IN # Bengali (India)
zh_CN # Chinese (China)
zh_TW # Chinese (Taiwan)
nl # Dutch
fr # French
de # German
el # Greek
gu # Gujarati
id # Indonesian
ja_JP # Japanese (Japan)
ja # Japanese
kn # Kannada
fa # Persian
pl # Polish
pt_BR # Portuguese (Brazilian)
pt # Portuguese
ru # Russian
es # Spanish (Castilian)
sv # Swedish
uk # Ukrainian

208
install/po/Makefile.in Normal file
View File

@ -0,0 +1,208 @@
prefix = @prefix@
exec_prefix = ${prefix}
datarootdir = ${prefix}/share
datadir = ${datarootdir}
localedir = ${datarootdir}/locale
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL@ -m 644
AWK = @AWK@
SED = @SED@
MKDIR_P = @MKDIR_P@
XGETTEXT = @XGETTEXT@
MSGFMT = @MSGFMT@
MSGINIT = @MSGINIT@
MSGMERGE = @MSGMERGE@
MSGCMP = @MSGCMP@
TX = @TX@
DOMAIN = @GETTEXT_DOMAIN@
MSGMERGE_UPDATE = $(MSGMERGE) --update
COPYRIGHT_HOLDER = Red Hat
PACKAGE_NAME = $(DOMAIN)
PACKAGE_BUGREPORT = https://hosted.fedoraproject.org/projects/freeipa/newticket
XGETTEXT_OPTIONS = \
--add-comments="TRANSLATORS:" \
--copyright-holder="$(COPYRIGHT_HOLDER)" \
--package-name="$(PACKAGE_NAME)" \
--msgid-bugs-address="$(PACKAGE_BUGREPORT)"
languages = $(shell $(SED) 's/\#.*//' LINGUAS) # The sed command removes comments
po_files = $(patsubst %, %.po, $(languages))
mo_files = $(patsubst %.po, %.mo, $(po_files))
po_count=$(words $(po_files))
PY_FILES = $(shell cd ../..; git ls-files | grep -v -e "^tests/" -e "^doc/" -e "^install/po/" -e "^ipapython/test/" -e "setup.py" -e "setup-client.py" | grep "\.py$$" | tr '\n' ' '; cd install/po)
C_FILES = $(shell cd ../..; git ls-files | grep "\.c$$" | tr '\n' ' '; cd install/po)
H_FILES = $(shell cd ../..; git ls-files | grep "\.h$$" | tr '\n' ' '; cd install/po)
PY_EXPLICIT_FILES = \
ipa \
install/tools/ipa-replica-manage \
install/tools/ipa-server-certinstall \
install/tools/ipa-replica-conncheck \
install/tools/ipa-replica-install \
install/tools/ipa-nis-manage \
install/tools/ipa-upgradeconfig \
install/tools/ipa-replica-prepare \
install/tools/ipa-compat-manage \
install/tools/ipa-managed-entries \
install/tools/ipa-server-install \
install/tools/ipa-ldap-updater \
install/tools/ipa-dns-install \
install/tools/ipa-ca-install \
ipa-client/ipa-install/ipa-client-install
PYTHON_POTFILES = $(PY_FILES) $(PY_EXPLICIT_FILES)
C_POTFILES = $(C_FILES) $(H_FILES)
.SUFFIXES:
.SUFFIXES: .po .mo
.PHONY: all create-po update-po update-pot install mostlyclean clean distclean test_lang test mo-files debug
all:
SUFFIXES = .po .mo
.po.mo:
@echo Creating $@; \
$(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
$(po_files): $(DOMAIN).pot
@if [ ! -f $@ ]; then \
lang=`echo $@ | $(SED) -r -e 's/\.po$$//'` # Strip .po suffix ; \
echo Creating nonexistent $@, you should add this file to your SCM repository; \
$(MSGINIT) --locale $$lang --no-translator -i $(DOMAIN).pot -o $@; \
fi; \
echo Merging $(DOMAIN).pot into $@; \
$(MSGMERGE) --no-fuzzy-matching -o $@ $@ $(DOMAIN).pot
create-po: $(DOMAIN).pot
@for po_file in $(po_files); do \
if [ ! -e $$po_file ]; then \
lang=`echo $$po_file | $(SED) -r -e 's/\.po$$//'` # Strip .po suffix ; \
echo Creating nonexistent $$po_file, you should add this file to your SCM repository; \
$(MSGINIT) --locale $$lang --no-translator -i $(DOMAIN).pot -o $$po_file; \
fi; \
done
pull-po:
cd ../..; $(TX) pull -f
update-po: update-pot
$(MAKE) $(po_files)
update-pot:
@rm -f $(DOMAIN).pot.update
@pushd ../.. ; \
$(XGETTEXT) $(XGETTEXT_OPTIONS) \
--output install/po/$(DOMAIN).pot.update \
--language="python" \
$(PYTHON_POTFILES) \
&& \
$(XGETTEXT) $(XGETTEXT_OPTIONS) \
--output install/po/$(DOMAIN).pot.update \
--join-existing \
--language="c" \
--from-code="UTF-8" \
--keyword='_' \
$(C_POTFILES) ; \
popd ; \
$(SED) '/^"POT-Creation-Date: .*"$$/d' $(DOMAIN).pot.update > $(DOMAIN).pot.update.tmp ; \
$(SED) -i -r -e 's%("Content-Type: text/plain; charset=)(.*)(\\n")%\1UTF-8\3%' $(DOMAIN).pot.update.tmp ; \
$(SED) '/^"POT-Creation-Date: .*"$$/d' $(DOMAIN).pot > $(DOMAIN).pot.tmp ; \
if ! cmp -s $(DOMAIN).pot.update.tmp $(DOMAIN).pot.tmp ; then \
echo "$(DOMAIN).pot updated" ; \
mv $(DOMAIN).pot.update $(DOMAIN).pot ; \
# Replace the charset with UTF-8 ; \
$(SED) -i -r -e 's%("Content-Type: text/plain; charset=)(.*)(\\n")%\1UTF-8\3%' $(DOMAIN).pot ; \
else \
echo "$(DOMAIN).pot unmodified" ; \
fi || :
@rm -f $(DOMAIN).pot.update $(DOMAIN).pot.update.tmp $(DOMAIN).pot.tmp
msg-stats:
@pot_count=`$(MSGFMT) --statistics $(DOMAIN).pot 2>&1 | \
$(AWK) '{match($$0, /([0-9]+) translated messages, ([0-9]+) untranslated messages/, groups); \
printf "%s\n", groups[2];}'` ; \
echo "$(DOMAIN).pot has $$pot_count messages. There are $(po_count) po translation files." ; \
for po_file in $(po_files); do \
$(MSGFMT) --statistics $$po_file 2>&1 | \
$(AWK) -v po_file=$$po_file -v pot_count=$$pot_count -v pot_file=$(DOMAIN).pot \
'BEGIN {po_name = gensub(/\.po$$/, "", 1, po_file);} \
match($$0, /([[:digit:]]+) translated/, group) {translated = group[1]} \
match($$0, /([[:digit:]]+) untranslated/, group) {untranslated = group[1]} \
match($$0, /([[:digit:]]+) fuzzy/, group) {fuzzy = group[1]} \
END {pot_untranslated = pot_count - translated; \
ratio = sprintf("%d/%d", translated, pot_count); \
printf "%-7s %11s %5.1f%% %5d untranslated, %5d fuzzy\n", \
po_name ":", ratio, translated/pot_count*100.0, pot_untranslated, fuzzy;}'; \
done
mo-files: $(mo_files)
install: $(mo_files)
@for lang in $(languages); do \
dstdir=$(DESTDIR)$(localedir)/$$lang/LC_MESSAGES; \
$(MKDIR_P) $$dstdir; \
$(INSTALL) $$lang.mo $$dstdir/$(DOMAIN).mo; \
done
mostlyclean:
rm -rf *.mo test.po test_locale
rm -f $(DOMAIN).pot.update $(DOMAIN).pot.update.tmp $(DOMAIN).pot.tmp
clean: mostlyclean
distclean: clean
rm -f Makefile
maintainer-clean: distclean
# We test our translations by taking the original untranslated string
# (e.g. msgid) and prepend a prefix character and then append a suffix
# character. The test consists of asserting that the first character in the
# translated string is the prefix, the last character in the translated string
# is the suffix and the everything between the first and last character exactly
# matches the original msgid.
#
# We use unicode characters not in the ascii character set for the prefix and
# suffix to enhance the test. To make reading the translated string easier the
# prefix is the unicode right pointing arrow and the suffix left pointing arrow,
# thus the translated string looks like the original string enclosed in
# arrows. In ASCII art the string "foo" would render as:
# -->foo<--
#
# Unicode right pointing arrow: u'\u2192', utf-8 = '\xe2\x86\x92'
# Unicode left pointing arrow: u'\u2190', utf-8 = '\xe2\x86\x90'
#
# The sed command below performs the prefix and suffix substitution.
#
# When msginit is invoked with an English target locale it copies the msgid
# into the msgstr. This is an undocumented feature of msginit. Otherwise the
# msgstr will be set to the empty string (i.e. untranslated). We depend on
# the msgid being copied to the msgstr.
test_lang:
rm -rf test.po test_locale
$(MSGINIT) --no-translator -i $(DOMAIN).pot -l en_US -o test.po
$(SED) -i -r -e 's/Language: en_US/Language: xh_ZA/' test.po
$(SED) -i -r -e 's/^msgstr[ \t]+"(..*)"[ \t]*$$/msgstr "\xe2\x86\x92\1\xe2\x86\x90"/' test.po
$(SED) -i -r -e 's/^msgstr[ \t]+"(.*)(\\n)(.)"[ \t]*$$/msgstr "\1\xe2\x86\x90\2"/' test.po
$(MKDIR_P) test_locale/xh_ZA/LC_MESSAGES
$(MSGFMT) -o test_locale/xh_ZA/LC_MESSAGES/ipa.mo test.po
test: test_lang
./test_i18n.py
debug:
@echo Python potfiles:
@echo PY_FILES = $(PY_FILES)
@echo PY_EXPLICIT_FILES = $(PY_EXPLICIT_FILES)
@echo C potfiles:
@echo C_FILES = $(C_FILES)
@echo H_FILES = $(H_FILES)

131
install/po/README Normal file
View File

@ -0,0 +1,131 @@
Q: I've added a new source file, how do I make sure it's strings get translated?
A: Edit Makefile.in and add the source file to the appropriate *_POTFILES list.
Then run "make update-po".
NOTE: Now this i only necessary for python files that lack the .py
extension. All .py, .c and .h files are automatically sourced.
Q: How do I pick up new strings to translate from the source files after the
source have been modified?
A: make update-po
This regenerates the pot template file by scanning all the source files.
Then the new strings are merged into each .po file from the new pot file.
Q: How do I just regenerate the pot template file without regenerating all the
.po files?
A: make update-pot
Q: How do I add a new language for translation?
A: Edit the LINGUAS file and add the new language. Then run "make create-po".
This will generate a new .po file for each language which doesn't have one
yet. Be sure to add the new .po file(s) to the source code repository. For
certain languages, you may have to edit the Plurals line. See:
http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html
However, if this line is wrong, it is often an indicator that the locale
value is incorrect. For example, using 'jp' for Japanese in stead of 'ja'
will result in an invailid Plural's line.
Q: What files must be under source code control?
A: The files Makefile.in, LINGUAS control the build, they must be in the SCM.
The *.pot and *.po files are used by translators, they must be in SCM so the
translator can checkout out a .po files, add the translations, and then check
the .po file back in.
Be careful, .po files may be automatically updated when the source files
change (or the .pot changes, usually the .pot file changes only as a result
of rescanning the source files). This mean a .po file might be automatically
updated while a translator has the file out for editing, all the caveats
about SCM merging apply.
Q: Which are automatically generated and thus do not need to be in SCM?
A: The *.mo files are automatically generated on demand from their corresponding
.po file.
Q: What role does the .pot file play?
A: The .pot file is called a template file. It is generated by scanning all the
source files (e.g. *.py *.c *.h) in the project using xgettext. xgettext
locates every translatable string (e.g. strings marked with _()) and adds
that string along with metadata about it's location to the .pot file. Thus
the .pot file is a collection of every translatable string in the project. If
you edit a source file and add a translatable string you will have to
regenerate the .pot file in order to pick up the new string.
Q: What is the relationship between a .po file and the .pot file?
A: A .po file contains the translations for particular language. It derives from
the .pot file. When the .pot file is updated with new strings to translate
each .po will merge the new strings in. The .po file is where translators
work providing translations for their language. Thus it's important the .po
not be recreated from scratch and is kept in SCM, otherwise the translators
work will be lost.
Let's use an example for French, it's .po file will be fr.po.
1) Developer creates main.c with one translatable sting _("Begin").
2) Produce the .pot file by running xgettext on main.c
3) .pot file contains one msgid, "Begin"
4) fr.po is created from the .pot file, it also contains one msgid, "Begin"
5) Translator edits fr.po and provide the French translation of "Begin".
6) Developer adds new translatable sting _("End") to main.c
7) Generate a new .pot file by running xgettext on main.c
8) .pot file contains two msgid's, "Begin", and "End"
9) fr.po is missing the new msgid in the .pot file, so the .pot is merged
into fr.po by running msgmerge. This copies into fr.po the new "End" msgid
but preserves the existing translations in fr.po (e.g. "Begin"). The fr.po
will now have 2 msgid's one which is translated already (e.g. "Begin") and
one that untranslated (e.g. "End").
10) Sometime later the French translator comes back to see if he/she needs to
add more translations to fr.po. They see there is a missing translation,
they check fr.po out from SCM, add the missing translation, and then
check fr.po back into SCM.
This means at any given moment the set of .po files will have varying degrees
of translation completeness. Because the .po files are merged when the source
code files are updated existing translations are not lost. It also means a
.po file which was fully translated may need new translations after a .pot
update. It is permissible to have incomplete translations in a message
catalog, at run time if a translation for a particular string is available in
the message catalog the user will be presented with the string in their
language. However if the string is not yet translated in the .po file then
they just get the original string (typically in English).
Q: What are .mo files?
A: .mo files are the content of a .po file but in "machine" format for fast
run time access (mo = Machine Object, po = Portable Object). .mo files are
what gets installed along with the package. Think of a .po as a source file
which is compiled into a object file for run time use.
Q: Why don't we use gettexize and autopoint?
A: Because the framework they produce is too limited. Specifically there is no
way to pass the source language to xgettext when it scans a file. xgettext
only knows how to automatically determine the language from the source files
extension. However we have many files without extensions, thus we have to
group all Python (et. al.) files together and run xgettext on every file *we*
know to Python (because xgettext can't figure this out itself if there is no
file extension). There is another added benefit of avoiding gettextize and
autopoint, simplicity. Managing translations is complex and hard enough as it
is, gettextize and autopoint adds another whole layer of complexity which
just further obscures things.
Q: Who created the awful mess and who do I ask when things don't work as I
expect or I have further questions?
A: John Dennis <jdennis@redhat.com>

7813
install/po/as.po Normal file

File diff suppressed because it is too large Load Diff

7815
install/po/bn_IN.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
bn_IN: Bengali India
sankarshan mukhopadhyay <sankarshan@fedoraproject.org>
es: Spanish
Héctor Daniel Cabrera <logan@fedoraproject.org>
id: Indonesian
Teguh DC <dheche@songolimo.net>
kn: Kannada
gundachandru <gundachandru@gmail.com>
pl: Polish
Piotr Drąg <piotrdrag@gmail.com>
ru: Russian
Andrew Martynov <andrewm@inventa.ru>
uk: Ukrainian
Yuri Chornoivan <yurchor@ukr.net>
zh_CN: Chinese Simplified
Jake Li <gnozil@gmail.com>

7814
install/po/de.po Normal file

File diff suppressed because it is too large Load Diff

7813
install/po/el.po Normal file

File diff suppressed because it is too large Load Diff

9672
install/po/es.po Normal file

File diff suppressed because it is too large Load Diff

7794
install/po/fa.po Normal file

File diff suppressed because it is too large Load Diff

7814
install/po/fr.po Normal file

File diff suppressed because it is too large Load Diff

7813
install/po/gu.po Normal file

File diff suppressed because it is too large Load Diff

7814
install/po/he.po Normal file

File diff suppressed because it is too large Load Diff

7798
install/po/id.po Normal file

File diff suppressed because it is too large Load Diff

7880
install/po/ipa.pot Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More