zentyal-ca-2.3.6+quantal1/0000775000000000000000000000000012017147622012205 5ustar zentyal-ca-2.3.6+quantal1/COPYING0000664000000000000000000004311012017147622013237 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. zentyal-ca-2.3.6+quantal1/debian/0000775000000000000000000000000012017147622013427 5ustar zentyal-ca-2.3.6+quantal1/debian/zentyal-ca.postinst0000664000000000000000000000043512017147622017305 0ustar #!/bin/bash set -e #DEBHELPER# case "$1" in configure) # initial setup /usr/share/zentyal/initial-setup --no-restart ca $2 # restart module invoke-rc.d zentyal ca restart || true dpkg-trigger --no-await zentyal-core ;; esac exit 0 zentyal-ca-2.3.6+quantal1/debian/compat0000664000000000000000000000000212017147622014625 0ustar 5 zentyal-ca-2.3.6+quantal1/debian/copyright0000664000000000000000000000214112017147622015360 0ustar This package was debianized by Zentyal Packaging Maintainers Fri, 02 Feb 2007 17:00:00 +0100. It was downloaded from http://www.zentyal.org/ Files: * Upstream Author: eBox Technologies S.L. Copyright (C) 2007-2012 eBox Technologies S.L. License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-2 file. The Debian packaging is: (C) 2007-2011, Zentyal Packaging Maintainers and is licensed under the GPL, see `/usr/share/common-licenses/GPL-2'. zentyal-ca-2.3.6+quantal1/debian/control0000664000000000000000000000176612017147622015044 0ustar Source: zentyal-ca Section: web Priority: optional Maintainer: Zentyal Packaging Maintainers Uploaders: Jorge Salamero Sanz Build-Depends: zbuildtools Standards-Version: 3.9.2 Homepage: http://www.zentyal.org/ Vcs-Browser: http://git.zentyal.org/zentyal.git/tree/quantal:/main/ca Vcs-Git: git://git.zentyal.org/zentyal.git Package: zentyal-ca Architecture: all Replaces: ebox-ca (<< 2.0.100) Breaks: ebox-ca (<< 2.0.100) Depends: zentyal-core (>= 2.3), zentyal-core (<< 2.3.100), libdate-calc-perl, libfile-copy-recursive-perl, ${misc:Depends} Description: Zentyal - Certification Authority Zentyal is a Linux small business server that can act as a Gateway, Unified Threat Manager, Office Server, Infrastructure Manager, Unified Communications Server or a combination of them. One single, easy-to-use platform to manage all your network services. . This module adds a Certification Authority management using OpenSSL to issue, revoke and renew certificates. zentyal-ca-2.3.6+quantal1/debian/source/0000775000000000000000000000000012017147622014727 5ustar zentyal-ca-2.3.6+quantal1/debian/source/format0000664000000000000000000000001512017147622016136 0ustar 3.0 (native) zentyal-ca-2.3.6+quantal1/debian/zentyal-ca.postrm0000664000000000000000000000044412017147622016746 0ustar #!/bin/bash set -e #DEBHELPER# case "$1" in purge) # purge configuration /usr/share/zentyal/purge-module ca # delete the issued certificates rm -fr /var/lib/zentyal/CA ;; remove) dpkg-trigger --no-await zentyal-core ;; esac exit 0 zentyal-ca-2.3.6+quantal1/debian/rules0000775000000000000000000000010612017147622014504 0ustar #!/usr/bin/make -f include /usr/share/zbuildtools/1/rules/zentyal.mk zentyal-ca-2.3.6+quantal1/debian/changelog0000664000000000000000000001630312017147622015304 0ustar zentyal-ca (2.3.6+quantal1) quantal; urgency=low * New upstream release for Quantal -- Jorge Salamero Sanz Tue, 28 Aug 2012 15:48:43 +0200 zentyal-ca (2.3.6) precise; urgency=low * New upstream release -- José A. Calvo Thu, 23 Aug 2012 02:26:19 +0200 zentyal-ca (2.3.5) precise; urgency=low * New upstream release -- José A. Calvo Thu, 14 Jun 2012 04:49:47 +0200 zentyal-ca (2.3.4) precise; urgency=low * New upstream release -- José A. Calvo Thu, 22 Mar 2012 20:38:09 +0100 zentyal-ca (2.3.1) precise; urgency=low * New upstream release -- José A. Calvo Tue, 06 Mar 2012 11:57:02 +0100 zentyal-ca (2.3-1) precise; urgency=low * Updated Standards-Version to 3.9.2 -- José A. Calvo Wed, 08 Feb 2012 16:11:04 +0100 zentyal-ca (2.3) precise; urgency=low * New upstream release -- José A. Calvo Mon, 30 Jan 2012 01:42:08 +0100 zentyal-ca (2.2.2) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 24 Oct 2011 14:09:58 +0200 zentyal-ca (2.2.1) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 23 Sep 2011 15:21:42 +0200 zentyal-ca (2.2) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 13 Sep 2011 04:37:24 +0200 zentyal-ca (2.1.6) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 10 Sep 2011 19:11:10 +0200 zentyal-ca (2.1.5) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 31 Aug 2011 15:19:04 +0200 zentyal-ca (2.1.4) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 24 Aug 2011 03:17:41 +0200 zentyal-ca (2.1.3) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 19 Jul 2011 14:02:40 +0200 zentyal-ca (2.1.2) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 29 Jun 2011 19:36:17 +0200 zentyal-ca (2.1.1) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 03 May 2011 17:03:52 +0200 zentyal-ca (2.1) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 22 Feb 2011 03:10:52 +0100 ebox-ca (2.0.3) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 21 Dec 2010 00:43:51 +0100 ebox-ca (2.0.2) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 19 Dec 2010 04:27:58 +0100 ebox-ca (2.0.1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 25 Nov 2010 00:19:14 +0100 ebox-ca (2.0) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 30 Aug 2010 21:48:15 +0200 ebox-ca (1.5.4-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 23 Aug 2010 02:14:23 +0200 ebox-ca (1.5.3-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 23 Jul 2010 11:11:43 +0200 ebox-ca (1.5.2-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 16 Jun 2010 21:00:02 +0200 ebox-ca (1.5.1-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 23 May 2010 19:32:22 +0200 ebox-ca (1.5-0ubuntu1) lucid; urgency=low [Javier Uruen Val] * New upstream release (LP: #521807) * debian/control - Bump eBox dependency - Update description -- Javier Uruen Val Sun, 07 Feb 2010 18:51:11 +0100 ebox-ca (1.3.5-0ubuntu1) karmic; urgency=low [Javier Uruen Val] * New upstream release [LP: TBD] * cdbs/ebox.mk - GConf schemas are not used anymore - Remove SCHEMASPATH variable - Remove schemadir variable - Use new upstart directory and file naming convention * debian/control - Bump standards version - Bump eBox depenency * debian/ebox-ca.postinst - Fix indentation - Do not pkill gconfd as it's not necessary anymore - Run ebox trigger * remove debian/ebox-ca.prerm - Not needed anymore as we don't use gconf schemas * debian/ebox-ca.postrm - Run eobx trigger * debian/rules - Do not include debian/cdbs/gnome.mk * debian/watch - Change URL -- Javier Uruen Val Wed, 05 Aug 2009 12:29:43 +0200 ebox-ca (0.12-0ubuntu1) jaunty; urgency=low [ Javier Uruen Val ] * New upstream version. Closes (LP: #318730) * debian/control: - bump standard version. - drop dpatch build dependency. * debian/watch: - add watch file. -- Mathias Gug Mon, 26 Jan 2009 18:37:48 -0500 ebox-ca (0.11.99-0ubuntu2) hardy; urgency=low * debian/patches/01_fix_private_dir_permissions.dpatch - Fix private key's directory permissions. -- Chuck Short Thu, 10 Apr 2008 11:14:45 -0400 ebox-ca (0.11.99-0ubuntu1) hardy; urgency=low * New upstream release. -- Chuck Short Wed, 27 Feb 2008 11:34:55 -0500 ebox-ca (0.11.99-0ubuntu1~ppa1) hardy; urgency=low * New upstream release. -- Chuck Short Wed, 27 Feb 2008 11:31:07 -0500 ebox-ca (0.11.99-0ubuntu1~ppa1) hardy; urgency=low * New upstream release -- Javier Uruen Val Mon, 25 Feb 2008 12:15:46 +0100 ebox-ca (0.11.99) unstable; urgency=low * New upstream release -- Enrique José Hernández Blasco Tue, 8 Jan 2008 16:14:37 +0100 ebox-ca (0.11-0ubuntu1~ppa1) hardy; urgency=low * New upstream release -- Javier Uruen Val Wed, 28 Nov 2007 15:23:31 +0100 ebox-ca (0.10.99) unstable; urgency=low * New upstream release -- Javier Uruen Val Thu, 01 Nov 2007 21:38:12 +0100 ebox-ca (0.10) unstable; urgency=low * New upstream release -- Javier Uruen Val Wed, 10 Oct 2007 21:53:46 +0200 ebox-ca (0.9.100) unstable; urgency=low * New upstream release -- Javier Uruen Val Tue, 04 Sep 2007 13:57:25 +0200 ebox-ca (0.9.99) unstable; urgency=low * New upstream release -- Javier Amor Garcia Tue, 24 Jul 2007 11:41:10 +0200 ebox-ca (0.9.3) unstable; urgency=low * New upstream release -- Javier Uruen Val Sun, 24 Jun 2007 16:38:46 +0200 ebox-ca (0.9.2) unstable; urgency=low * New upstream release -- Javier Uruen Val Tue, 12 Jun 2007 18:59:26 +0200 ebox-ca (0.9.1) unstable; urgency=low * New upstream release -- Javier Uruen Val Tue, 15 May 2007 13:02:23 +0200 ebox-ca (0.9) unstable; urgency=low * Upstream release -- Enrique José Hernández Blasco Tue, 20 Mar 2007 17:23:06 +0100 ebox-ca (0.8.99) unstable; urgency=low * Upstream release -- Enrique José Hernández Blasco Fri, 2 Feb 2007 15:52:09 +0100 ebox-ca (0.1) unstable; urgency=low * Initial Release. -- Enrique José Hernández Blasco Mon, 30 Oct 2006 18:42:39 +0100 zentyal-ca-2.3.6+quantal1/src/0000775000000000000000000000000012017147622012774 5ustar zentyal-ca-2.3.6+quantal1/src/templates/0000775000000000000000000000000012017147622014772 5ustar zentyal-ca-2.3.6+quantal1/src/templates/formRenew.mas0000664000000000000000000000432412017147622017443 0ustar <%args> $metaDataCert # A hash reference to the list certificate returned element $passRequired => '' # Set if the CA is password aware <%init> use EBox::Gettext; my @renewTable = ( [ name => 'commonName', input => 'hidden', value => $metaDataCert->{dn}->attribute('commonName') ], [ name => 'isCACert', input => 'hidden', value => $metaDataCert->{isCACert} ], [ name => 'expireDays', printableName => __('Days to expire'), input => 'text' ]); if ( $passRequired ) { push ( @renewTable, [ name => 'caPassphrase', input => 'password', printableName => __('CA Passphrase') ]); } push ( @renewTable, [ printableName => '', component => '/ca/forceRenew.mas:buttons', action => 'renew', printableAction => __('Renew') ], );

<% __('Renew a certificate') %>

% if ( $metaDataCert->{'isCACert'} ) {
<% __('This operation will resign all the issued user certificates with the same expiration date') %>
% }
% if ( $metaDataCert->{'isCACert'} ) { <% __('Organization Name') %>: <% $metaDataCert->{dn}->attribute('organizationName') %> % } % else { <% __("Common Name") %>: <%$metaDataCert->{dn}->attribute('commonName') %> % } <% __("Expiration Date") %>: % my $date = $metaDataCert->{'expiryDate'}; <% sprintf("%04d-%02d-%02d %02d:%02d:%02d", $date->year(), $date->month(), $date->day(), $date->hours(), $date->minutes(), $date->seconds()) %>
% if ( exists $metaDataCert->{'subjAltNames'} ) {

<% __('Subject Alternative Names') %>: % my $subjAltNamesStr = join(', ', map { "$_->{type}:$_->{value}" } @{$metaDataCert->{'subjAltNames'}}); <% $subjAltNamesStr %>
% }
<& formTable.mas, rows => \@renewTable &>
zentyal-ca-2.3.6+quantal1/src/templates/formRevoke.mas0000664000000000000000000000501112017147622017610 0ustar <%args> $metaDataCert # A hash reference to the list certificate returned element @reasons => qw (unspecified) $passRequired => '' # Set if the CA is password aware <%init> use EBox::Gettext;

<% __('Revoke a Certificate') %>

% if ( $metaDataCert->{"isCACert"} ) {
<% __("This operation will revoke all the issued user certificates and the CA Certificate.") %>
% }
<% __('Revoke the selected certificate') %>
% if ( $metaDataCert->{'isCACert'} ) { <% __('Organization Name') %>: <% $metaDataCert->{dn}->attribute('organizationName') %> % } % else { <% __("Common Name") %>: <%$metaDataCert->{dn}->attribute('commonName') %> % } <% __("Expiration Date") %>: % my $date = $metaDataCert->{'expiryDate'}; <% sprintf("%04d-%02d-%02d %02d:%02d:%02d", $date->year(), $date->month(), $date->day(), $date->hours(), $date->minutes(), $date->seconds()) %>
% if ( exists $metaDataCert->{'subjAltNames'} ) {

<% __('Subject Alternative Names') %>: % my $subjAltNamesStr = join(', ', map { "$_->{type}:$_->{value}" } @{$metaDataCert->{'subjAltNames'}}); <% $subjAltNamesStr %>
% }
<%perl> my @selectOptions; foreach my $option (@reasons) { push (@selectOptions, {value => $option, printableValue => $option }); } my @revokeTable = ( [ name => 'commonName', input => 'hidden', value => $metaDataCert->{dn}->attribute('commonName') ], [ name => 'isCACert', input => 'hidden', value => $metaDataCert->{isCACert} ], [ name => 'reason', printableName => __('Reason'), input => 'select', options => \@selectOptions, ] ); if ( $passRequired ) { push ( @revokeTable, [ name => 'caPassphrase', input => 'password', printableName => __('CA Passphrase')]); } push ( @revokeTable, [ printableName => '', component => '/ca/forceRenew.mas:buttons', action => 'revoke', printableAction => __('Revoke') ], );
<& formTable.mas, rows => \@revokeTable &>
zentyal-ca-2.3.6+quantal1/src/templates/createCA.mas0000664000000000000000000000311112017147622017137 0ustar <%args> <%init> use EBox::Gettext; my @createTable = ( [ name => 'orgName', input => 'text', printableName => __('Organization Name') ], [ name => 'countryName', input => 'text', printableName => __('Country code'), size => '2', optional => 1 ], [ name => 'localityName', input => 'text', printableName => __('City'), optional => 1 ], [ name => 'stateName', input => 'text', printableName => __('State'), optional => 1 ], [ name => 'expiryDays', input => 'text', printableName => __('Days to expire'), value => '3650', size => '5' ], # [ name => 'caPassphrase', input => 'password', # printableName => __('Passphrase') ], # [ name => 'reCAPassphrase', input => 'password', # printableName => __('Re-passphrase') ], [ name => 'ca', input => 'submit', value => __('Create') ] );
<% __("This page only appears once at starting up the Certification Authority.\nChanges take effect immediately.") %>

<% __('Create Certification Authority Certificate') %>

<& formTable.mas, rows => \@createTable &>
zentyal-ca-2.3.6+quantal1/src/templates/index.mas0000664000000000000000000001675612017147622016622 0ustar <%doc> Template to show the current certificate list and issue a new certificate. <%args> $certs # A reference to the list with all available certificates $caNeeded => 0 # set whether the CA certificate is needed to issue $passRequired => '' # set if the CA is password-aware $caExpirationDays => '' # default value for certificate expiration <%init> use EBox::Gettext; use EBox::CA::DN; <%perl> # Common args to issueCertificate and issueCACertificate my @issueTable = ( [ name => 'caNeeded', input => 'hidden', value => $caNeeded ], ); my $printableName = __('Organization Name'); $printableName = __('Common Name') unless ($caNeeded); push ( @issueTable, [ name => 'name', printableName => $printableName, input => 'text' ], ); # if ( $passRequired or $caNeeded ) { # push ( @issueTable, [ name => 'caPassphrase', input => 'password', # printableName => __('CA Passphrase') ]); # # } if ( $caNeeded ) { # push ( @issueTable, [ name => 'reCAPassphrase', # input => 'password', # printableName => __('Re CA Passphrase')] # ); push( @issueTable, [ name => 'countryName', input => 'text', printableName => __('Country code'), size => 2, optional => 1 ]); push( @issueTable, [ name => 'localityName', input => 'text', printableName => __('City'), optional => 1 ]); push( @issueTable, [ name => 'stateName', input => 'text', printableName => __('State'), optional => 1 ]); } push( @issueTable,[ name => 'expiryDays', printableName => __('Days to expire'), value => $caExpirationDays, size => 5, input => 'text' ]); unless ( $caNeeded ) { push( @issueTable, [ name => 'subjectAltName', input => 'text', printableName => __('Subject Alternative Names'), help => __x('Multi-value separated by ' . 'commas, only valid types are: ' . '{dns}, {ip} and {email}. For ' . 'instance, {example}', dns => 'DNS', ip => 'IP', email => 'email', example => 'DNS:host.domain.com,IP:10.2.2.2' ), optional => 1 ]); } push ( @issueTable, [ name => 'certificate', value => __('Issue'), input => 'submit' ] ); # Set the list of suitable certificates to reissue my %validCerts = map { $_->{'dn'}->attribute('commonName') => 1 } grep { $_->{state} eq 'V' } @{$certs};
<% __("Certificate changes will take effect immediately.") %> % if ( $caNeeded ) { <% '.' . __('The CA passphrase is optional') %> % }
% if ($caNeeded) {

<% __('Issue the Certification Authority Certificate') %>

% } else {

<% __('Issue a New Certificate') %>

% }
<& formTable.mas, rows => \@issueTable &>

<% __("Current Certificate List") %>

<% __("Date refers to Expiration Date in Valid/Expired and to Revokation Date in Revoked certificates respectively") %>.
% # XXX: We need the CN of the CA certificates to filter revoked ones % my $caCertName = ''; % foreach my $cert (@{$certs}) { % # It is needed to check if it is the CA certificate % if ( $cert->{'isCACert'} and $cert->{'state'} eq 'V' ) { % $caCertName = $cert->{'dn'}->attribute('commonName'); % } <%perl> my $stateStr = ""; if ( $cert->{"state"} eq "V" ) { $stateStr = __("Valid"); } elsif ( $cert->{"state"} eq "E" ) { $stateStr = __("Expired"); } elsif ( $cert->{"state"} eq "R" ) { $stateStr = __("Revoked"); } % }
<% __("Name") %> <% __("State") %> <% __("Date") %> <% __("Actions") %>
{'subjAltNames'}) and @{$cert->{'subjAltNames'}} > 0 ) { % my $subjAltNamesStr = join(', ', map { qq{$_->{type}: $_->{value}} } @{$cert->{'subjAltNames'}}); title="<% $subjAltNamesStr %>" % } > % if ( $cert->{'isCACert'} ) { <% $cert->{'dn'}->attribute('commonName') %> <% __('from') %> <% $cert->{'dn'}->attribute('organizationName') %> % } % else { <% $cert->{'dn'}->attribute('commonName') %> % } {"state"} eq 'R' ) { title="<% __x('Reason: {reason}', reason => $cert->{'reason'}) %>" % } > <% $stateStr %> <%perl> my $date = undef; if ( $cert->{"state"} =~ m/[VE]/ ) { $date = $cert->{"expiryDate"}; } else { $date = $cert->{"revokeDate"}; } <% sprintf("%04d-%02d-%02d %02d:%02d:%02d", $date->year(), $date->month(), $date->day(), $date->hours(), $date->minutes(), $date->seconds()) %> % if ( $cert->{"state"} eq "V" ) { <% __('Revoke') %> - % } % if ( $cert->{"state"} eq "V" ) { <% __('Download Key(s) and Certificate') %> - % } % if ( $cert->{"state"} eq "V" ) { <% __('Renew') %> - % } % if (($cert->{'state'} eq 'E' or $cert->{'state'} eq 'R') % and not $caNeeded % and ($cert->{'dn'}->attribute('commonName') ne $caCertName) % and (not exists($validCerts{$cert->{'dn'}->attribute('commonName')})) ) { <% __('Reissue') %> % }
<%perl> my @legendElements = ( { imgFile => "/data/images/deny-active.gif", imgName => __("Revoke") }, { imgFile => "/data/images/install.gif", imgName => __("Download Key(s) and Certificate") }, { imgFile => "/data/images/reload.png", imgName => __("Renew or reissue") } ); <& legendTable.mas, elements => \@legendElements, nColumns => 3 &> zentyal-ca-2.3.6+quantal1/src/templates/forceRenew.mas0000664000000000000000000000446012017147622017577 0ustar <%doc> Template used to ask for forcing the renewal of a certificate which is currently in used by some other module. <%args> $metaDataCert $expireDays $caPassphrase => undef <%init> use EBox::Gettext; # We're using a hack to show a cancel button (We can inherit from # formTable to do so...) my @forceRenewTable = ( [ name => 'commonName', input => 'hidden', value => $metaDataCert->{dn}->attribute('commonName') ], [ name => 'isCACert', input => 'hidden', value => $metaDataCert->{isCACert} ], [ name => 'expireDays', input => 'hidden', value => $expireDays ], [ name => 'renewForced', input => 'hidden', value => 'true' ], [ printableName => '', component => '/ca/forceRenew.mas:buttons', action => 'renew', printableAction => __('Renew') ], ); if ( defined ( $caPassphrase )) { push ( @forceRenewTable, [ name => 'caPassphrase', input => 'hidden', value => $caPassphrase ], ); }

<%perl> if ( $metaDataCert->{isCACert} ) { print __("Renewing CA certificate"); } else { print __("Renewing certificate"); }

% if ( $metaDataCert->{isCACert} ) { <% __x('This renewal can affect the functionality from other modules, please confirm you are very sure to renew the whole certification authority from {on} and leave the service(s) unstable or disabled', on => $metaDataCert->{dn}->attribute('organizationName')) %> % } % else { <% __x("The certificate whose common name is {cn} you tried to renew is being used by some module, please confirm you are sure to renew and leave the service(s) unstable or disabled", cn => $metaDataCert->{dn}->attribute('commonName')) %> % }

<& formTable.mas, rows => \@forceRenewTable &>

<%method buttons> <%doc> The input buttons one after one <%args> $action $printableAction => undef <%init> if ( not defined ( $printableAction )) { $printableAction = $action; } <& /input/submit.mas, name => $action, value => $printableAction &> <& /input/submit.mas, name => 'cancel', value => __('Cancel') &> zentyal-ca-2.3.6+quantal1/src/templates/formReissue.mas0000664000000000000000000000315512017147622020003 0ustar <%args> $metaDataCert # A hash reference to the list certificate returned element $passRequired => '' # Set if the CA is password aware <%init> use EBox::Gettext; my $subjAltNamesStr = join(',', map { "$_->{type}:$_->{value}" } @{$metaDataCert->{'subjAltNames'}}); my @reissueTable = ( [ name => 'name', input => 'hidden', value => $metaDataCert->{dn}->attribute('commonName') ], [ name => 'expiryDays', printableName => __('Days to expire'), input => 'text'], [ name => 'subjectAltName', printableName => __('Subject Alternative Names'), input => 'text', value => $subjAltNamesStr, optional => 1 ]); if ( $passRequired ) { push ( @reissueTable, [ name => 'caPassphrase', input => 'password', printableName => __('CA Passphrase') ]); } push ( @reissueTable, [ printableName => '', component => '/ca/forceRenew.mas:buttons', action => 'certificate', printableAction => __('Reissue') ], );

<% __('Reissue a certificate') %>

<% __("Common Name") %>: <% $metaDataCert->{dn}->attribute('commonName') %>
% if ( exists $metaDataCert->{'subjAltNames'} ) {

<% __('Subject Alternative Names') %>: <% $subjAltNamesStr %>
% }
<& formTable.mas, rows => \@reissueTable &>
zentyal-ca-2.3.6+quantal1/src/templates/forceRevoke.mas0000664000000000000000000000310212017147622017742 0ustar <%args> $metaDataCert $isCACert => 1 $reason => "unknown" $caPassphrase => undef <%init> use EBox::Gettext;

<%perl> if ( $isCACert ) { print __("Revoking CA certificate"); } else { print __("Revoking certificate"); }

% if ( $isCACert ) { <% __x('This revokation can affect the functionality from other modules, please confirm you are very sure to revoke the whole certification authority from {on} and leave the service(s) unstable or disabled', on => $metaDataCert->{dn}->attribute('organizationName')) %> % } % else { <% __x("The certificate whose common name is {cn} you tried to revoke is being used by some module, please confirm you are sure to revoke and leave the service(s) unstable or disabled", cn => $metaDataCert->{dn}->attribute('commonName')) %> % }

% if ( defined ( $caPassphrase )) { % }

zentyal-ca-2.3.6+quantal1/src/scripts/0000775000000000000000000000000012017147622014463 5ustar zentyal-ca-2.3.6+quantal1/src/scripts/updateDB.pl0000775000000000000000000000243512017147622016517 0ustar #!/usr/bin/perl # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Description: # # This script is intended to update certificate DB to change expired # certificates from valid to expired state use EBox; use EBox::Global; use Getopt::Long; use Pod::Usage; my $usage = 0; my $correct = GetOptions( "usage|help" => \$usage, ); if ( $usage or (not $correct)) { pod2usage(1); } EBox::init(); my $ca = EBox::Global->modInstance('ca'); if ( $ca->isCreated() ) { $ca->updateDB(); } 1; __END__ =head1 NAME updateDB.pl - Update certificate DB =head1 SYNOPSIS updateDB.pl [--usage|help] Options: -- usage|help Print this help and exit =cut zentyal-ca-2.3.6+quantal1/src/EBox/0000775000000000000000000000000012017147622013631 5ustar zentyal-ca-2.3.6+quantal1/src/EBox/CA.pm0000664000000000000000000024223412017147622014461 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CA; use strict; use warnings; use base qw(EBox::Module::Service); use File::Slurp; use Perl6::Junction qw(any); use Date::Calc::Object qw(:all); use File::Copy::Recursive qw(dircopy fcopy); use File::Temp; use EBox::CA::DN; use EBox::Gettext; use EBox::Config; use EBox::Menu::Item; use EBox::Dashboard::ModuleStatus; use EBox::Exceptions::External; use EBox::Exceptions::Internal; use EBox::Exceptions::DataMissing; use EBox::Exceptions::DataInUse; use EBox; use EBox::CA::Certificates; use EBox::Validate; use EBox::Sudo; use EBox::AuditLogging; use constant OPENSSLPATH => "/usr/bin/openssl"; my $output_shell = EBox::Config->tmp() . 'openssl-shell.out'; my $error_shell = EBox::Config->tmp() . 'openssl-shell.err'; use constant CATOPDIR => EBox::Config->home() . "CA/"; use constant SSLCONFFILE => EBox::Config->conf() . "openssl.cnf"; # CATOPDIR . "../openssl.cnf"; # All paths related to CATOPDIR use constant REQDIR => CATOPDIR . "reqs/"; use constant PRIVDIR => CATOPDIR . "private/"; use constant CRLDIR => CATOPDIR . "crl/"; use constant NEWCERTSDIR => CATOPDIR . "newcerts/"; use constant CERTSDIR => CATOPDIR . "certs/"; # Place to put the public keys use constant KEYSDIR => CATOPDIR . "keys/"; # Place to put the P12 keystore use constant P12DIR => CATOPDIR . "p12/"; use constant CAREQ => REQDIR . "careq.pem"; use constant CACERT => CATOPDIR . "cacert.pem"; use constant INDEXFILE => CATOPDIR . "index.txt"; use constant CRLNOFILE => CATOPDIR . "crlnumber"; use constant SERIALNOFILE => CATOPDIR . "serial"; use constant LASTESTCRL => CRLDIR . "latest.pem"; # Keys from CA use constant CAPRIVKEY => PRIVDIR . "cakey.pem"; use constant CAPUBKEY => KEYSDIR . "capubkey.pem"; # Directory mode to allow only owner and readable from others # Others means openvpn user (nobody) use constant DIRMODE => 00751; use constant PRIVATEDIRMODE => 00700; use constant CHMOD_DIRMODE => '751'; use constant CHMOD_PRIVATEDIRMODE => '700'; # Use Certification Version 3 use constant EXTENSIONS_V3 => "1"; # Default values for some fields use constant CA_CN_DEF => "Certification Authority Certificate"; # Index for every field (split with tabs) within the index.txt file use constant STATE_IDX => 0; use constant EXPIRE_DATE_IDX => 1; use constant REV_DATE_REASON_IDX => 2; use constant SERIAL_IDX => 3; use constant FILE_IDX => 4; use constant SUBJECT_IDX => 5; # Catch the openssl executable my $openssl; if ( defined $ENV{OPENSSL} ) { $openssl = $ENV{OPENSSL}; } else { $openssl = OPENSSLPATH; $ENV{OPENSSL} = $openssl; } # Group: Public methods # Constructor: _create # # Create a new EBox::CA object # # Returns: # # A recent EBox::CA object sub _create { my $class = shift; my $self = $class->SUPER::_create(name => 'ca', printableName => __n('Certification Authority'), @_); bless($self, $class); # Reasons to revoke $self->{reasons} = ["unspecified", "keyCompromise", "CACompromise", "affiliationChanged", "superseded", "cessationOfOperation", "certificateHold", "removeFromCRL"]; # Set a maximum to establish the certificate expiration # Related to Year 2038 bug $self->{maxDays} = $self->_maxDays(); $self->{audit} = EBox::Global->modInstance('audit'); return $self; } # Method: isCreated # # Check whether the Certification Infrastructure has been # created or not # # Returns: # # boolean - True, if the Certification Infrastructure has been # created. Undef, otherwise. # sub isCreated { my ($self, $name) = @_; my $retValue = ((-d CATOPDIR) and (-f CACERT) and (-f CAPRIVKEY) and (-f CAPUBKEY)); return $retValue; } # Method: isAvailable # # Check whether the Certification Infrastructure is available or not # # Returns: # # boolean - True, if the Certification Infrastructure is available. # Undef, otherwise. # sub isAvailable { my ($self) = @_; return 0 unless $self->isCreated(); my $currentState = $self->currentCACertificateState(); if ( $currentState =~ m/[RE]/) { return 0; } else { return 1; } } # Method: createCA # # Create a Certification Authority with a self-signed certificate # and if it not setup, create the directory hierarchy for the CA # # Parameters: # # countryName - country name {2 letter code} (eg, ES) *(Optional)* # stateName - state or province name (eg, Aragon) *(Optional)* # localityName - locality name (eg, Zaragoza) *(Optional)* # orgName - organization name (eg, company name) # orgNameUnit - organizational unit name (eg, section name) *(Optional)* # commonName - common name from the CA *(Optional)* # caKeyPassword - passphrase for generating keys *(Optional)* # days - expire day of self signed certificate *(Optional)* # # - Named parameters # # Returns: # # 1 - if everything is newly created # 2 - if the CA certificate already exists # # Exceptions: # # - if any required parameter is # missing sub createCA { my ($self, %args) = @_; throw EBox::Exceptions::DataMissing(data => __('Organization Name')) unless defined($args{orgName}); $self->_checkCertificateFieldsCharacters(%args); if (! -d CATOPDIR) { # Create the directory hierchary mkdir (CATOPDIR, DIRMODE); # Let's assume the subdirectories have the same name mkdir (CERTSDIR, DIRMODE); mkdir (CRLDIR, DIRMODE); mkdir (NEWCERTSDIR, DIRMODE); mkdir (KEYSDIR, DIRMODE); mkdir (PRIVDIR, PRIVATEDIRMODE); mkdir (REQDIR, DIRMODE); mkdir (P12DIR, PRIVATEDIRMODE); # Create index and crl number open ( my $OUT, ">" . INDEXFILE); close ($OUT); open ( $OUT, ">" . CRLNOFILE); print $OUT "01\n"; close ($OUT); } # Save the current CA password for private key (It can be undef) $self->{caKeyPassword} = $args{caKeyPassword}; return 2 if (-f CACERT); # Define the distinguished name -> default values in configuration file $args{commonName} = CA_CN_DEF unless ( $args{commonName} ); $self->{dn} = EBox::CA::DN->new ( countryName => $args{countryName}, stateName => $args{stateName}, localityName => $args{localityName}, organizationName => $args{orgName}, organizationNameUnit => $args{orgNameUnit}, commonName => $args{commonName}); # Make the CA certificate $args{days} = 3650 unless ( defined ($args{days}) ); if ( $args{days} > $self->{maxDays} ) { $args{days} = $self->{maxDays}; # Warning -> Year 2038 Bug # http://www.mail-archive.com/openssl-users@openssl.org/msg45886.html EBox::warn(__x("Days set to the maximum allowed {days}: Year 2038 Bug", days => $self->{maxDays})); } # To create the request the distinguished name is needed my $output = $self->_createRequest(reqFile => CAREQ, genKey => 1, privKey => CAPRIVKEY, keyPassword => $self->{caKeyPassword}, dn => $self->{dn}, needPass => defined($self->{caKeyPassword}) ); if ($output) { throw EBox::Exceptions::External($self->_filterErrorFromOpenSSL($output)); } # Sign the selfsign certificate # $self->_signRequest(userReqFile => CAREQ, # days => $args{days}, # userCertFile => CACERT, # selfsigned => "1"); # OpenSSL v. 0.9.7 # We should create the serial my $serialNumber = $self->_createSerial(); $self->_signSelfSignRequest(userReqFile => CAREQ, days => $args{days}, userCertFile => CACERT, serialNumber => $serialNumber, keyFile => CAPRIVKEY); # OpenSSL v. 0.9.7 Bugs -> Resolved in openssl ca utility later on # OpenSSL v. 0.9.7 -> Write down in index.txt $self->_putInIndex(dn => $self->{dn}, certFile => CACERT, serialNumber => $serialNumber); # Create the serial file if ( ! -f SERIALNOFILE ) { $self->_writeDownNextSerial(CACERT); } # Create the serial attribute file if ( ! (-f INDEXFILE . ".attr") ) { $self->_writeDownIndexAttr(INDEXFILE . ".attr"); } # Generate the public key file $self->_getPubKey(CAPRIVKEY, CAPUBKEY, $self->{caKeyPassword}); # Set the CA certificate expiration date $self->{caExpirationDate} = $self->_obtain(CACERT, 'endDate'); # Generate CRL $self->generateCRL(); $self->_audit('createCA', $self->{dn}->attribute('orgName')); #unlink (CAREQ); $self->_setPasswordRequired(defined($self->{caKeyPassword})); return 1; } # Method: destroyCA # # Destroys CA structure (the opposite operation to method) # # Returns: # # 1 - if everything is completely destroys # 2 - nothing to destroy # # Exceptions: # # EBox::Exceptions::External - if the remove operation cannot complete # sub destroyCA { my ($self, $caKeyPassword) = @_; if (not $self->isCreated()) { return 2; } my $orgName = $self->caDN()->attribute('orgName'); # Delete CA directory if (system('rm -rf ' . CATOPDIR) != 0) { throw EBox::Exceptions::External(__("The remove operation cannot be finished. Reason: ") . $!); } $self->_audit('destroyCA', $orgName); # Set internal attribute to undefined $self->{dn} = undef; $self->{caExpirationDate} = undef; $self->{caKeyPassword} = undef; return 1; } # Method: initialSetup # # Overrides: # EBox::Module::Base::initialSetup # sub initialSetup { my @cmds; my @dirs = (CATOPDIR, CERTSDIR, CRLDIR, NEWCERTSDIR, KEYSDIR, REQDIR); foreach my $dir (@dirs) { if (-d $dir) { push (@cmds, 'chmod ' . CHMOD_DIRMODE . " $dir "); } } if (-d PRIVDIR) { push (@cmds, 'chmod ' . CHMOD_PRIVATEDIRMODE . ' ' . PRIVDIR); } EBox::Sudo::root(@cmds); unless (-d P12DIR) { mkdir (P12DIR, PRIVATEDIRMODE) } } # Method: passwordRequired # # Check if the password is required to sign the certificates # within this Certification Authority # # Returns: # # boolean - true if pass is required, false otherwise # sub passwordRequired { my ($self) = @_; my $caState = $self->currentCACertificateState(); # Password could be required only if a CA has been created return undef unless ($caState eq 'V'); return $self->get_bool('pass_required'); } # Method: revokeCACertificate # # Revoke the self-signed CA certificate and subsequently all the # issued certificates # # Parameters: # # reason - the reason to revoke the certificate. It can be: # unspecified, keyCompromise, CACompromise, # affiliationChanged, superseeded, cessationOfOperation # or certificationHold (Optional) # caKeyPassword - the CA passpharse (Optional) # force - Force the revokation (Optional) # # Returns: # # undef if OK # # Exceptions: # # EBox::Exceptions::DataInUse - if the CA certificate is being # used by other modules sub revokeCACertificate { my ($self, %args) = @_; # Check certificates is not in used if ($args{force}) { $self->_freeCert(undef, 1); } elsif( $self->_certsInUse(undef, 1) ){ throw EBox::Exceptions::DataInUse(); } # Revoked all issued and valid certificates # Revoked only valid ones # We ensure not to revoke the CA cert before the others my $listCerts = $self->listCertificates( state => 'V', excludeCA => 1); foreach my $element (@{$listCerts}) { $self->revokeCertificate(commonName => $element->{dn}->attribute('commonName'), reason => "cessationOfOperation", caKeyPassword => $args{caKeyPassword}, force => $args{force}, ); } my $retVal = $self->revokeCertificate(commonName => "", reason => $args{reason}, caKeyPassword => $args{caKeyPassword}, certFile => CACERT, force => $args{force}); $self->{caExpirationDate} = undef; $self->_audit('revokeCACertificate', "reason: $args{reason}"); return $retVal; } # Method: issueCACertificate # # Issue a self-signed CA certificate # # Parameters: # # commonName - the CA common name (Optional) # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Zaragoza) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # # orgName - organization name (eg, company) # # orgNameUnit - organizational unit name (eg, section) # (Optional) # days - days to hold the same certificate (Optional) # caKeyPassword - key passpharse for CA (Optional) # genPair - if you want to generate a new key pair (Optional) # # Returns: # # the new certificate file path # # Exceptions: # # DataMissing - if any required parameter is missing # External - if a CA certificate is already valid sub issueCACertificate { my ($self, %args) = @_; throw EBox::Exceptions::DataMissing(data => __('Organization Name')) unless defined( $args{orgName} ); $self->_checkCertificateFieldsCharacters(%args); # Define the distinguished name -> default values in configuration file $args{commonName} = CA_CN_DEF unless ( $args{commonName} ); $self->{dn} = EBox::CA::DN->new ( countryName => $args{countryName}, stateName => $args{stateName}, localityName => $args{localityName}, organizationName => $args{orgName}, organizationNameUnit => $args{orgNameUnit}, commonName => $args{commonName}); if ( $self->currentCACertificateState() eq 'V') { throw EBox::Exceptions::External( __('The CA certificates should be revoked or has expired before issuing a new certificate')); } # Erase the previous cert request to generate a new key pair if ( $args{genPair} ) { unlink (CAREQ); unlink (CAPRIVKEY); } my $ret = $self->issueCertificate(commonName => $self->{dn}->attribute('commonName'), countryName => $self->{dn}->attribute('countryName'), localityName => $self->{dn}->attribute('localityName'), orgName => $self->{dn}->attribute('orgName'), orgNameUnit => $self->{dn}->attribute('orgNameUnit'), keyPassword => $args{caKeyPassword}, days => $args{days}, caKeyPassword => $args{caKeyPassword}, privateKeyFile => CAPRIVKEY, requestFile => CAREQ, certFile => CACERT); # Expiration CA certificate $self->{caExpirationDate} = $self->_obtain(CACERT, 'endDate'); $self->_audit('issueCACertificate', $self->{dn}->attribute('orgName')); $self->_setPasswordRequired( defined( $args{caKeyPassword} )); return $ret; } # Method: renewCACertificate # # Renew the self-signed CA certificate. Re-signs all the issued # certificates with the same expiration date or the CA # expiration date if the issued certificate is later. # # # Parameters: # # commonName - the CA common name *(Optional)* # countryName - country name {2 letter code} (eg, ES) *(Optional)* # stateName - state or province name (eg, Zaragoza) *(Optional)* # localityName - locality name (eg, Zaragoza) *(Optional)* # # orgName - organization name (eg, company) *(Optional)* # # orgNameUnit - organizational unit name (eg, section) # *(Optional)* # days - days to hold the same certificate *(Optional)* # caKeyPassword - key passpharse for CA *(Optional)* # force - forcing the renewal of all the certificates *(Optional)* # # Returns: # # the new certificate file path or undef if any error happened # # Exceptions: # # DataMissing - if no caKeyPassword is given # sub renewCACertificate { my ($self, %args) = @_; $self->{caKeyPassword} = $args{caKeyPassword}; my $listCerts = $self->listCertificates(); my $renewedCert = $self->renewCertificate( countryName => $args{countryName}, stateName => $args{stateName}, localityName => $args{localityName}, orgName => $args{orgName}, orgNameUnit => $args{orgNameUnit}, days => $args{days}, caKeyPassword => $args{caKeyPassword}, certFile => CACERT, reqFile => CAREQ, privateKeyFile => CAPRIVKEY, keyPassword => $args{caKeyPassword}, overwrite => "1", force => $args{force}, ); # Expiration CA certificate $self->{caExpirationDate} = $self->_obtain(CACERT, 'endDate'); # Re-signs all the issued certificates with the same expiry date foreach my $element (@{$listCerts}) { # Renew the previous ones that remains valid if ($element->{state} eq 'V' and -f ( KEYSDIR . $element->{dn}->attribute('commonName') . ".pem") ) { my $userExpiryDate = $element->{expiryDate}; $userExpiryDate = $self->{caExpirationDate} if ( $element->{expiryDate} gt $self->{caExpirationDate} ); $self->renewCertificate( commonName => $element->{dn}->attribute('commonName'), endDate => $userExpiryDate, caKeyPassword => $args{caKeyPassword}, force => $args{force}, ); } } $self->_audit('renewCACertificate', $self->caDN()->attribute('orgName')); return $renewedCert; } # Method: CAPublicKey # # Return the public key and certificate file from the # Certificate Authority # # Parameters: # # caKeyPassword - the passphrase to access to private key # (Optional) # # Returns: # # Path to the file which contains the CA Public Key in PEM # format or undef if it was not possible to retrieve # sub CAPublicKey { my ($self, $caKeyPassword) = @_; if (-f CAPUBKEY) { return CAPUBKEY; } # If does NOT exist, we have to generate it $caKeyPassword = $self->{caKeyPassword} unless ( defined ($caKeyPassword) ); return $self->_getPubKey(CAPRIVKEY, CAPUBKEY, $caKeyPassword); } # Method: issueCertificate # # Create a new certificate for an requester # # Parameters: # # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Zaragoza) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # # orgName - organization name (eg, company) (Optional) # # orgNameUnit - organizational unit name (eg, section) # (Optional) # # commonName - common name from the organization # keyPassword - passphrase for the private key (Optional) # days - expiration days of certificate (Optional) # Only valid if endDate is not present # endDate - explicity expiration date (Optional) # A Date::Calc::Object # caKeyPassword - passphrase for CA to sign (Optional) # privateKeyFile - path to the private key file if there is already # a private key file in the CA (Optional) # # requestFile - path to save the new certificate request # (Optional) # certFile - path to store the new certificate file (Optional) # # subjAltNames - Array ref containing the subject alternative # names in a hash ref with the following keys: # # type - String the type must be one of these: DNS, IP or email # # value - String the value following these # constraints: # DNS - Domain name # IP - IPv4 address # email - email address # # Returns: # # undef if no problem has happened # # Exceptions: # # External - if the CA passpharse CANNOT be located # if the expiration date from certificate to issue is later than CA # certificate expiration date # if any error happens in signing request process # # DataMissing - if any required parameter is missing # sub issueCertificate { my ($self, %args) = @_; # Treat arguments throw EBox::Exceptions::DataMissing(data => __('Common Name')) unless defined( $args{commonName} ); $self->_checkCertificateFieldsCharacters(%args); EBox::warn("Two ways to declare expiration date through days and endDate. " . "Using endDate...") if ( defined( $args{days} ) and defined( $args{endDate} ) ); my $days = undef; if (not defined($args{endDate}) ) { $days = $args{days}; $days = 365 unless $days; if ( $days > $self->{maxDays} ) { $days = $self->{maxDays}; # It should be $self->{maxDays} but OpenSSL 0.9.7 lacks in other point # Another workaround should be, put the enddate explicity # Warning -> Year 2038 Bug # http://www.mail-archive.com/openssl-users@openssl.org/msg45886.html EBox::warn(__x("Days set to the maximum allowed {days}: Year 2038 Bug", days => $self->{maxDays})); } } # Check the expiration date unless issuing the CA my $userExpDay = undef; if (not defined($args{certFile}) or $args{certFile} ne CACERT) { if ( defined($days) ) { $userExpDay = Date::Calc::Object->now(); $userExpDay += [0, 0, $days, 0, 0, 0]; } elsif (defined $args{endDate}) { $userExpDay = $args{endDate}; } $userExpDay = $self->_isLaterThanCA($userExpDay); # Set to undef since we are using userExpDay as endDate $days = undef; } if ( defined($args{caKeyPassword}) ) { $self->{caKeyPassword} = $args{caKeyPassword}; } # Name the private key and the user requests by common name my $privKey = PRIVDIR . "$args{commonName}.pem"; $privKey = $args{privateKeyFile} if ($args{privateKeyFile}); my $pubKey = KEYSDIR . "$args{commonName}.pem"; my $userReq = REQDIR . "$args{commonName}.pem"; $userReq = $args{requestFile} if ($args{requestFile}); my $certFile = $args{certFile}; # Define the distinguished name # We take the default values from CA dn my $dn = $self->caDN()->copy(); $dn->attribute("countryName", $args{countryName}) if (defined($args{countryName})); $dn->attribute("stateName", $args{stateName}) if (defined($args{stateName})); $dn->attribute("localityName", $args{localityName}) if (defined($args{localityName})); $dn->attribute("orgName", $args{orgName}) if (defined($args{orgName})); $dn->attribute("orgNameUnit", $args{orgNameUnit}) if (defined($args{orgNameUnit})); $dn->attribute("commonName", $args{commonName}) if (defined($args{commonName})); if (defined($args{subjAltNames})) { $self->_checkSubjAltNames($args{subjAltNames}); } # Create the certificate request my $genKey = 1; if ( defined($args{privateKeyFile} )) { if (-f $args{privateKeyFile} ) { $genKey = 0; } } my $output = $self->_createRequest(reqFile => $userReq, genKey => $genKey, privKey => $privKey, keyPassword => $args{keyPassword}, dn => $dn, needPass => defined($args{keyPassword}) ); if ( defined($output) ) { throw EBox::Exceptions::External($self->_filterErrorFromOpenSSL($output)); } # Signs the request my $selfSigned = "0"; if ( defined ($certFile) ) { $selfSigned = $certFile eq CACERT; } $output = $self->_signRequest( userReqFile => $userReq, days => $days, userCertFile => $certFile, selfsigned => $selfSigned, endDate => $userExpDay, subjAltNames => $args{subjAltNames}, ); # Check if something goes wrong if ( defined($output) ) { throw EBox::Exceptions::External($output); } # Generate the public key file (if it is a newly created private # key) if (not defined($args{privateKeyFile}) ) { my $result = $self->_getPubKey($privKey, $pubKey, $args{keyPassword}); } # Generate the P12 key store unless ( $selfSigned ) { $self->_generateP12Store($privKey, $self->getCertificateMetadata(cn => $args{commonName}), $args{keyPassword}); } $self->_audit('issueCertificate', $args{commonName}); return $self->_findCertFile($args{"commonName"}); } # Method: revokeCertificate # # Revoke a certificate given the common name # # Parameters: # # commonName - the common name with the certificate to revoke # reason - the reason to revoke the certificate. It can be: # unspecified, keyCompromise, CACompromise, # affiliationChanged, superseeded, cessationOfOperation # or certificationHold (Optional) # caKeyPassword - the CA passpharse (Optional) # certFile - the Certificate to revoke (Optional) # force - Force the revokation (Optional) # renew - This revoke is due to a renew (Optional) # # Returns: # # undef if OK # # Exceptions: # # - if the certificate does NOT exist # if the reason is NOT a standard one # if any error occurred when revokation is done # if any error occurred when creating the CRL is done # - if any required # parameter is missing # - if the # certificate to revoke is being used by other modules # sub revokeCertificate { my ($self, %args) = @_; my $commonName = $args{commonName}; my $reason = $args{reason}; my $caKeyPassword = $args{caKeyPassword}; my $certFile = $args{certFile}; throw EBox::Exceptions::DataMissing(data => __('Common Name') ) unless defined($commonName); if ( defined($caKeyPassword) ) { $self->{caKeyPassword} = $caKeyPassword; } # RFC 3280 suggests not to use an unspecified reason when the reason # is unknown. $reason = "unspecified" unless defined($reason); $certFile = $self->_findCertFile($commonName) unless defined ($certFile); throw EBox::Exceptions::External(__x('Certificate with common name {cn} does NOT exist', cn => $commonName)) unless (defined($certFile) and -f $certFile); throw EBox::Exceptions::External(__x("Reason {reason} is NOT an applicable reason.\n" . "Options:" . $self->{reasons}, reason => $reason)) unless $reason eq any(@{$self->{reasons}}); # Tell observers the following certificate will be revoked my $dn = $self->caDN(); my $isCACert = $dn->attribute('commonName') eq $commonName; if ( $args{force} ) { $self->_freeCert($commonName, $isCACert); } elsif ( not $args{renew} and $self->_certsInUse($commonName, $isCACert) ) { throw EBox::Exceptions::DataInUse("Certificate $commonName in use"); } # TODO: Different kinds of revokations (CACompromise, # keyCompromise...) # We can set different arguments regard to revocation reason my $cmd = "ca"; $self->_commonArgs("ca", \$cmd); $cmd .= "-revoke \'$certFile\' "; if ( defined($self->{caKeyPassword}) ) { $cmd .= "-passin env:PASS "; } $cmd .= "-crl_reason $reason " if (defined($reason)); # Tell openssl to revoke $ENV{'PASS'} = $self->{caKeyPassword} if (defined($self->{caKeyPassword})); my ($retValue, $output) = $self->_executeCommand(command => $cmd); delete ($ENV{'PASS'}); # If any error is shown from revocation, the result is got back throw EBox::Exceptions::External($self->_filterErrorFromOpenSSL($output)) if ($retValue eq "ERROR"); # Generate a new Certification Revocation List $self->generateCRL(); # Mark this module as changed $self->setAsChanged(); # TODO: audit logging # action: revokeCertificate # cn: $args{commonName} # reason: $args{reason} # force: $force return undef; } # Method: generateCRL # # Generate a new Certification Revocation List. It will create a # new one with today date and link to the latest. # sub generateCRL { my ($self) = @_; # Localtime (my $year, my $month, my $day) = Date::Calc::Today(); my $date = sprintf("%04d-%02d-%02d", $year, $month, $day); my $cmd= "ca"; $self->_commonArgs("ca", \$cmd); $cmd .= "-gencrl "; if ( defined($self->{caKeyPassword}) ) { $cmd .= "-passin env:PASS "; } $cmd .= "-out " . CRLDIR . $date . "-crl.pem"; $ENV{'PASS'} = $self->{caKeyPassword} if (defined($self->{caKeyPassword})); my ($retValue, $output) = $self->_executeCommand(command => $cmd); delete ($ENV{'PASS'}); throw EBox::Exceptions::External($self->_filterErrorFromOpenSSL($output)) if ($retValue eq "ERROR"); # Set the link to the last unlink (LASTESTCRL) if ( -l LASTESTCRL ); symlink ( CRLDIR . $date . "-crl.pem", LASTESTCRL ); } # Method: listCertificates # # List the certificates that are ready on the system sorted # putting the CA certificate first and then valid certificates. # This list can be filtered according to an state, including or # not the CA certificate. # # Parameters: # # state - 'R', 'V' or 'E' in order to show only revoked, valid # or expired certificates. All are included if not set this # attribute (Optional) # # excludeCA - boolean indicating whether the valid CA certificate # should be excluded in the response (Optional) # # - Named parameters # # Returns: # # A reference to an array containing hashes which have the following # elements # # - dn - an object # - state - 'V' from Valid, 'R' from Revoked or 'E' from Expired # - expiryDate - the expiry date in a if state valid # # - revokeDate - the revocation date in a Date hash if state is # revoked # - reason - reason to revoke if state is revoked # - isCACert - boolean indicating that it is the valid CA certificate # - path - certificate path # - serialNumber - serial number within CA # - subjAltNames - array ref the subject alternative names containing hash ref # with the following valid types: # type => 'DNS', 'IP' and 'email' # value => the subject alternative name # # Exceptions: # # - thrown if the CA is not created # sub listCertificates { my ($self, %args) = @_; # Getting the arguments my $state = $args{'state'}; my $excludeCA = $args{'excludeCA'}; # Check parameter state is correct (R, V or E) if (defined($state) and $state !~ m/[RVE]/ ) { throw EBox::Exceptions::Internal("State should be R, V or E"); } unless (-f INDEXFILE) { throw EBox::Exceptions::External( __('The Certification Authority Infrastructure is not available, create it first') ); } my @lines = read_file( INDEXFILE ); my @out = (); foreach ( @lines ) { # Parse line with a tab my @line = split(/\t/); my %element; # Get current state $element{'state'} = $line[STATE_IDX]; $element{'dn'} = EBox::CA::DN->parseDN($line[SUBJECT_IDX]); $element{'serialNumber'} = $line[SERIAL_IDX]; # Set paramaters regarding to certificate validity if (($element{'state'} eq 'V') or ($element{'state'} eq 'E')) { $element{'expiryDate'} = $self->_parseDate($line[EXPIRE_DATE_IDX]); $element{'isCACert'} = $self->_isCACert($element{'dn'}); } elsif ( $element{'state'} eq 'R' ) { my $field = $line[REV_DATE_REASON_IDX]; my ($revDate, $reason) = split(',', $field); $element{'revokeDate'} = $self->_parseDate($revDate); $element{'reason'} = $reason; } # Setting certificate path if ($element{'isCACert'}) { $element{'path'} = CACERT; } else { $element{'path'} = CERTSDIR . $line[SERIAL_IDX] . ".pem"; } # Setting the subject alternative names $element{'subjAltNames'} = $self->_obtain( $element{'path'}, 'subjAltNames' ); push (@out, \%element); } # Setting the filters if ( defined($state) ) { # Filter according to state @out = grep { $_->{state} eq $state } @out; } if ( $excludeCA ) { # Filter the valid CA certificate @out = grep { not $_->{isCACert} } @out; } # Sort the array to have CA certs first (put latest first) my @sortedOut = sort { $b->{state} cmp $a->{state} } @out; return \@sortedOut; } # Method: getCertificateMetadata # # Given an attribute to filter, returns an unique certificate metadata. # JUST ONE parameter can be passed. To return the CA # certificate, it is recommended to use . # # Parameters: # # cn - Common Name (Optional) # dn - EBox::CA::DN Distinguished Name (Optional) # serialNumber - A serial number within CA (Optional) # # Returns: # # A reference to a hash with the following elements # # - dn - an object # - state - 'V' from Valid, 'R' from Revoked or 'E' from Expired # - expiryDate - the expiry date in a if state valid # # - revokeDate - the revocation date in a Date hash if state is # revoked # - reason - reason to revoke if state is revoked # - path - certificate path # - serialNumber - serial number within CA # - subjAltNames - array ref the subject alternative names containing hash ref # with the following valid types: # type => 'DNS', 'IP' and 'email' # value => the subject alternative name # # Exceptions: # # - if any required parameter is missing sub getCertificateMetadata { my ($self, %args) = @_; if (scalar(keys %args) == 0) { throw EBox::Exceptions::DataMissing(data => __("Either common name, distinguished name or serial number has been passed") ); } elsif ( scalar(keys %args) > 1 ) { throw EBox::Exceptions::Internal("Only one parameter is necessary"); } elsif ( defined($args{'dn'}) and not $args{'dn'}->isa('EBox::CA::DN') ) { throw EBox::Exceptions::Internal("dn parameter should be an instance of EBox::CA::DN"); } my $cn = $args{'cn'}; my $dn = $args{'dn'}; my $serialNumber = $args{'serialNumber'}; my $listCertsRef = $self->listCertificates(); my $retCert = undef; if (defined($cn)) { # Looking for a particular cn ($retCert) = grep { $_->{'dn'}->attribute('commonName') eq $cn } @{$listCertsRef}; } elsif(defined($dn)) { # Looking for a particular dn ($retCert) = grep { $_->{'dn'}->equals($dn) } @{$listCertsRef}; } elsif( defined($serialNumber) ) { ($retCert) = grep { $_->{'serialNumber'} eq $serialNumber } @{$listCertsRef}; } return $retCert; } # Method: getCACertificateMetadata # # Return the current metadata related to the valid Certification # Authority. # # # Returns: # # - A reference to a hash which the following elements # # - dn - an object # - expiryDate - the expiry date in a if state valid # - path - certificate path # # - Undef if the CA is expired or inexistent -> Check state # calling to # sub getCACertificateMetadata { my ($self) = @_; my $certsRef = $self->listCertificates(); # Find the CA certificate in the list my ($CACert) = grep { $_->{'isCACert'} } @{$certsRef} ; return $CACert; } # Method: getKeys # # Get the keys (public and private) from CA given an specific common name. # # Parameters: # # commonName - the common name to identify the key # # Returns: # # a reference to a hash containing the public and private key file # paths (*privateKey* and *publicKey*) stored in _PEM_ format # # Exceptions: # # External - if the keys do NOT exist # DataMissing - if any required parameter is missing # sub getKeys { my ($self, $commonName) = @_; throw EBox::Exceptions::DataMissing(data => __("Common Name")) unless defined ($commonName); my %keys; if (-f PRIVDIR . "$commonName.pem" ) { $keys{privateKey} = PRIVDIR . "$commonName.pem"; } $keys{publicKey} = KEYSDIR . "$commonName.pem"; throw EBox::Exceptions::External(__x('Certificate with common name {cn} does NOT exist', cn => $commonName)) unless (-f $keys{publicKey}); # Remove private key when the CGI has sent the private key # DONE in removePrivateKey method return \%keys; } # Method: getP12KeyStore # # Given the common name, it returns the PKCS12 keystore file path. # # The PKCS12 is a common format used by web browser to store # certificates and private keys. # # Parameters: # # commonName - String the common name # # Returns: # # String - the PKCS 12 key store path # sub getP12KeyStore { my ($self, $commonName) = @_; my $target = P12DIR . "${commonName}.p12"; if (-f $target) { return $target; } else { my $privKey = $self->getKeys($commonName)->{privateKey}; my $certMetadata = $self->getCertificateMetadata(cn => $commonName); return $self->_generateP12Store($privKey, $certMetadata); } } # Method: removePrivateKey # # Remove the private key from an user # # Parameters: # # commonName - the common name to identify the private key # # Returns: # # 1 - If everything OK # # Exceptions: # # Internal - if the private key does NOT exist # DataMissing - if common name is not provided # sub removePrivateKey { my ($self, $commonName) = @_; throw EBox::Exceptions::DataMissing(data => __('Common Name')) unless defined ($commonName); if (-f PRIVDIR . "$commonName.pem" ) { unlink (PRIVDIR . "$commonName.pem"); return 1; } else { throw EBox::Exceptions::Internal("Private key does NOT exist"); } } # Method: renewCertificate # # Renew a certificate from a user. # If any Distinguished Name is needed to change, it is done. # # Parameters: # # commonName - the common name from the user. Not needed # if a certificate file is given *(Optional)* # # countryName - country name {2 letter code} (eg, ES) *(Optional)* # stateName - state or province name (eg, Zaragoza) *(Optional)* # localityName - locality name (eg, Zaragoza) *(Optional)* # # orgName - organization name (eg, company) *(Optional)* # # orgNameUnit - organizational unit name (eg, section) # *(Optional)* # days - days to hold the same certificate *(Optional)* # Only if enddate not appeared # endDate - the exact date when the cert expired *(Optional)* # Only if days not appeared # It is a Date::Calc::Object # caKeyPassword - key passpharse for CA *(Optional)* # certFile - the certificate file to renew *(Optional)* # reqFile - the request certificate file which to renew *(Optional)* # # privateKeyFile - the private key file *(Optional)* # keyPassword - the private key passpharse. It is used when # a new request is issued, not compulsory anyway. *(Optional)* # # overwrite - overwrite the current certificate file. Only if # the certFile is passed *(Optional)* # # force - this forces the revokation pass to do the renewal *(Optional)* # subjAltNames - Array ref containing hash ref with key type of certificate # and value the value for the subject alt name *(Optional)* # # Returns: # # the new certificate file path # # Exceptions: # # - if the user does NOT exist, # if the expiration date for the certificate to renew is later than CA certificate expiration date # if the certificate to renew does NOT exist # if any error occurred when certificate renewal is done # - if any parameter is an unexcepted type # - if no CA passphrase is given to renew the certificate # - if the certificate to renew is in use and no forcing is done # sub renewCertificate { my ($self, %args) = @_; if (not defined($args{endDate}) ) { $args{days} = 365 unless defined ($args{days}); if ( $args{days} > $self->{maxDays} ) { $args{days} = $self->{maxDays}; # Warning -> Year 2038 Bug # http://www.mail-archive.com/openssl-users@openssl.org/msg45886.html EBox::warn(__x("Days set to the maximum allowed {days}: Year 2038 Bug", days => $self->{maxDays})); } } throw EBox::Exceptions::Internal("End date parameter (" . ref($args{endDate}) . " is not a Date::Calc object") if (defined($args{endDate}) and not UNIVERSAL::isa($args{endDate}, "Date::Calc")); # Check the expiration date unless issuing the CA cert my $userExpDay = undef; unless (defined($args{certFile}) and $args{certFile} eq CACERT) { if ( defined($args{days}) ) { $userExpDay = Date::Calc::Object->now() + [0, 0, +$args{days}, 0, 0, 0]; } elsif ( defined($args{endDate}) ) { $userExpDay = $args{endDate}; } $userExpDay = $self->_isLaterThanCA($userExpDay); # Set to undef since we are using userExpDay as endDate $args{days} = undef; } throw EBox::Exceptions::DataMissing(data => __('Common Name') . " " . __("or") . " " . __('Certificate file')) unless defined ($args{commonName}) or defined ($args{certFile}); if ( defined($args{caKeyPassword}) ) { $self->{caKeyPassword} = $args{caKeyPassword}; } my $overwrite = $args{overwrite} if ($args{certFile}); my $userCertFile; if ( defined($args{certFile})) { $userCertFile = $args{certFile}; } else { $userCertFile = $self->_findCertFile($args{commonName}); } my $selfsigned = "0"; $selfsigned = "1" if ($userCertFile eq CACERT); my $userDN = $self->_obtain($userCertFile, 'DN'); if ( not defined($userDN) ) { throw EBox::Exceptions::External(__("The certificate to renew does NOT exist")); } my $dnFieldHasChanged = '0'; if ( defined($args{countryName}) and $args{countryName} ne $userDN->attribute('countryName')) { $dnFieldHasChanged = "1"; $userDN->attribute('countryName', $args{countryName}); } if (defined($args{stateName}) and $args{stateName} ne $userDN->attribute('stateName')) { $dnFieldHasChanged = "1"; $userDN->attribute('stateName', $args{stateName}); } if (defined($args{localityName}) and $args{localityName} ne $userDN->attribute('localityName')) { $dnFieldHasChanged = "1"; $userDN->attribute('localityName', $args{localityName}); } if (defined($args{orgName}) and $args{orgName} ne $userDN->attribute('orgName')) { $dnFieldHasChanged = "1"; $userDN->attribute('orgName', $args{orgName}); } if (defined($args{orgNameUnit}) and $args{orgNameUnit} ne $userDN->attribute('orgNameUnit')) { $dnFieldHasChanged = "1"; $userDN->attribute('orgNameUnit', $args{orgNameUnit}); } # Revoke old certificate my $retVal = $self->revokeCertificate(commonName => $userDN->attribute('commonName'), reason => "superseded", certFile => $userCertFile, caKeyPassword => $args{caKeyPassword}, force => $args{force}, renew => "yes", ); if (defined($retVal) ) { throw EBox::Exceptions::External(__x("Certificate with this common name {cn} does NOT exist in this CA" , cn => $userDN->attribute('commonName'))); } # Sign a new one my $userReq; if ( defined($args{reqFile}) ) { $userReq = $args{reqFile}; } else { $userReq = REQDIR . $userDN->attribute('commonName') . ".pem"; } # Overwrite the current certificate? Useful? my $newCertFile = undef; if ($overwrite) { $newCertFile = $userCertFile; } my $subjAltNames; if (defined($args{subjAltNames})) { $subjAltNames = $args{subjAltNames}; $dnFieldHasChanged = "1"; } else { $subjAltNames = $self->_obtain($userCertFile, 'subjAltNames'); } if (-f $userReq) { # If the request exists, we can renew the certificate without # having the private key # New subject my $newSubject = undef; if ( $dnFieldHasChanged ) { $newSubject = $userDN; # For OpenSSL 0.9.7, we need to create the request my $privKeyFile = PRIVDIR . $userDN->attribute('commonName') . ".pem"; $privKeyFile = $args{privateKeyFile} if ($args{privateKeyFile}); #throw EBox::Exceptions::External(__("The private key passpharse needed to create a new request. No renewal was made. Issue a new certificate with new keys")) # if ( not defined($args{keyPassword}) ); my $output = $self->_createRequest(reqFile => $userReq, genKey => 0, privKey => $privKeyFile, keyPassword => $args{keyPassword}, dn => $newSubject, needPass => defined($args{keyPassword}) ); if ($output) { throw EBox::Exceptions::External($self->_filterErrorFromOpenSSL($output)); } if (defined $newCertFile and ($newCertFile eq CACERT)) { $self->{dn} = $newSubject; } } my $output = $self->_signRequest( userReqFile => $userReq, days => $args{days}, userCertFile => $newCertFile, selfsigned => $selfsigned, createSerial => 0, # Not in OpenSSL 0.9.7 newSubject => $newSubject, endDate => $userExpDay, subjAltNames => $subjAltNames, ); if (defined($output) ) { throw EBox::Exceptions::External($output); } } else { # If we don't keep the request, we should create a new one with # the private key if exists. If not, we need to recreate all... # by using issuing a new certificate with a new request my $privKeyFile = PRIVDIR . $userDN->attribute('commonName') . ".pem"; $privKeyFile = $args{privateKeyFile} if ($args{privateKeyFile}); $self->issueCertificate( countryName => $userDN->attribute('countryName'), stateName => $userDN->attribute('stateName'), localityName => $userDN->attribute('localityName'), orgName => $userDN->attribute('orgName'), orgNameUnit => $userDN->attribute('orgNameUnit'), commonName => $userDN->attribute('commonName'), keyPassword => $args{keyPassword}, days => $args{days}, privateKeyFile => $privKeyFile, requestFile => $userReq, certFile => $newCertFile, endDate => $userExpDay, subjAltNames => $subjAltNames, ); } if (not defined($newCertFile) ) { $newCertFile = $self->_findCertFile($userDN->attribute('commonName')); } $self->_audit('renewCertificate', $args{commonName}); # Tells other modules the following certs have been renewed my $isCACert = 0; $isCACert = 1 if (defined($args{certFile}) and $args{certFile} eq CACERT); my $global = EBox::Global->getInstance(); my @mods = @{$global->modInstancesOfType('EBox::CA::Observer')}; foreach my $mod (@mods) { $mod->certificateRenewed($userDN->attribute('commonName'), $isCACert); } EBox::CA::Certificates->certificateRenewed($userDN->attribute('commonName'), $isCACert); return $newCertFile; } # Method: updateDB # # Update the index.txt file to mark the expired certificates # Called by the controller. # # Parameters: # # caKeyPassword - key passpharse for CA (Optional) # # Returns: # # undef if everything OK # # Exceptions: # # EBox::Exceptions::External - if the CA passpharse is incorrect sub updateDB { my ($self, %args) = @_; # Manage the parameters my $caKeyPassword = $args{caKeyPassword}; if ( defined($caKeyPassword) ) { $self->{caKeyPassword} = $caKeyPassword; } my @expiredCertsBefore = @{$self->listCertificates(state => 'E')}; my $cmd = "ca"; $self->_commonArgs("ca", \$cmd); $cmd .= "-updatedb "; if ( defined($self->{caKeyPassword}) ){ $cmd .= "-passin env:PASS "; } $ENV{'PASS'} = $self->{caKeyPassword} if defined($self->{caKeyPassword}); my ($retVal, $output) = $self->_executeCommand( command => $cmd ); delete( $ENV{'PASS'} ); my @expiredCertsAfter = @{$self->listCertificates(state => 'E')}; my %seen; my @diff; foreach my $item (@expiredCertsBefore) { $seen{$item->{serialNumber}} = 1; } foreach my $item (@expiredCertsAfter) { next if ( $seen{$item->{serialNumber}} ); push(@diff, $item); } # Tells other modules the following certs have expired my $global = EBox::Global->getInstance(); my @mods = @{$global->modInstancesOfType('EBox::CA::Observer')}; foreach my $cert (@diff) { foreach my $mod (@mods) { $mod->certificateExpired($cert->{dn}->attribute('commonName'), $cert->{isCACert}); } EBox::CA::Certificates->certificateExpired($cert->{dn}->attribute('commonName'), $cert->{isCACert}); } if ($retVal eq "ERROR") { throw EBox::Exceptions::External($self->_filterErrorFromOpenSSL($output)); } return undef; } # Method: currentCACertificateState # # Return the current state for the CA Certificate # # Returns: # # The current CA Certificate state # - R - Revoked # - E - Expired # - V - Valid # - ! - Inexistent # sub currentCACertificateState { my ($self) = @_; my $serialCert = $self->_obtain(CACERT, 'serial'); return '!' unless (defined($serialCert)); my $certRef = $self->getCertificateMetadata(serialNumber => $serialCert); if ( not defined($certRef) ) { return "!"; } else { return $certRef->{'state'}; } } # Method: revokeReasons # # Return the current list of possible revoke reasons # # Returns: # # A reference to the array containing the current list of possible revoke reasons # sub revokeReasons { my ($self) = @_; return $self->{reasons}; } # Method: getCurrentCRL # # Return the current Certification Revokation List (CRL) # # Returns: # # Path to the current CRL or undef if there is no CRL # sub getCurrentCRL { my ($self) = @_; if ( -e LASTESTCRL ) { return LASTESTCRL; } else { return undef; } } # Method: caExpirationDays # # Return the CA expiration date in days since the current date # # Returns: # # Number of days until CA expiration # sub caExpirationDays { my ($self) = @_; my ($curY, $curM, $curD) = Date::Calc::Today(); my ($exY, $exM, $exD) = $self->caExpirationDate()->date(); return Date::Calc::Delta_Days($curY, $curM, $curD, $exY, $exM, $exD); } # Method: menu # # Add CA module to Zentyal menu # # Parameters: # # root - the where to leave our items # sub menu { my ($self, $root) = @_; my $folder = new EBox::Menu::Folder('name' => 'CA', 'text' => $self->printableName(), 'separator' => 'Infrastructure', 'order' => 422); $folder->add(new EBox::Menu::Item('url' => 'CA/Index', 'text' => __('General'))); $folder->add(new EBox::Menu::Item('url' => 'CA/View/Certificates', 'text' => __('Services Certificates'))); $root->add($folder); } # Method: dumpConfig # # Dump CA configuration and certificates # # Parameters: # # dir - Directory where the modules backup files are dumped # sub dumpConfig { my ($self, $dir) = @_; # Call super $self->SUPER::dumpConfig($dir); if ( -d CATOPDIR ) { # Storing all OpenSSL directory tree where Certs/keys and DB are stored dircopy(CATOPDIR, $dir . "/CA"); } if ( -f SSLCONFFILE ) { # Storing OpenSSL config file fcopy(SSLCONFFILE, $dir . "/openssl.cnf"); } # Dump services certificates my @srvCerts = @{EBox::CA::Certificates->srvsCerts()}; my @paths; foreach my $certificate (@srvCerts) { if (-f $certificate->{'path'}) { push(@paths, $certificate->{'path'}); } } if (@paths) { my $pathsStr = join(' ', @paths); EBox::Sudo::root("tar cf $dir/srvCerts.tar $pathsStr"); } } # Method: restoreConfig # # Restore its configuration from the backup file. Those files are # the same were created with dumpConfig # # Parameters: # dir - Directory where are located the backup files # sub restoreConfig { my ($self, $dir) = @_; # Call super $self->SUPER::restoreConfig($dir); # Destroy previous CA $self->destroyCA(); # Restoring all OpenSSL directory tree where Certs/keys and DB are stored my $dataDir = $dir . '/CA'; if (-d $dataDir) { dircopy($dataDir, CATOPDIR); } # Restoring OpenSSL config file fcopy($dir . "/openssl.cnf", SSLCONFFILE); # Restore services certificates if (-f "$dir/srvCerts.tar") { my @commands; push(@commands, "tar xfp $dir/srvCerts.tar -C /"); my @srvCerts = @{EBox::CA::Certificates->srvsCerts()}; foreach my $certificate (@srvCerts) { my $certPath = $certificate->{'path'}; if (-f $certPath) { my $certUser = $certificate->{'user'}; my $certGroup = $certificate->{'group'}; push(@commands, "chown $certUser:$certGroup $certPath"); } } EBox::Sudo::root(@commands); } } # Method: showModuleStatus # # Indicate to ServiceManager if the module must be shown in Module # status configuration. # # Overrides: # EBox::Module::Service::showModuleStatus # sub showModuleStatus { # we don't want it to appear in module status return undef; } # Method: addModuleStatus # # Overrides to show a custom status for CA module # # Overrides: # # # sub addModuleStatus { my ($self, $section) = @_; my $caStatus = __('Not created'); if ( $self->isAvailable() ) { $caStatus = __('Available'); } elsif ( $self->isCreated() ) { $caStatus = __('Created but not available'); } $section->add(new EBox::Dashboard::ModuleStatus( module => $self->name(), printableName => $self->printableName(), nobutton => 1, statusStr => $caStatus)); } # Method: report # # Returns: # ref hash with the following fields: # CAState: state of the CA certificate. It will be ono of this states: # - R - Revoked # - E - Expired # - V - Valid # - ! - Inexistent # # nValidCertificates: number of valid active certificates # nRevokedCertificates: number of revoked certificates # nExpiredCertificates: number of expired certifcates # # Overrides: # # # sub report { my ($self) = @_; my $currentCA = $self->currentCACertificateState(); my ($nValid, $nRevoked, $nExpired) = (0, 0, 0); if ($currentCA ne '!') { $nValid = @{ $self->listCertificates(state => 'V', excludeCA => 1) }; $nRevoked = @{ $self->listCertificates(state => 'R', excludeCA => 1) }; $nExpired = @{ $self->listCertificates(state => 'E', excludeCA => 1) }; } return { CAState => $currentCA, nValidCertifcates => $nValid, nRevokedCertificates => $nRevoked, nExpiredCertificates => $nExpired, }; } # Group: Protected methods sub _checkCertificateFieldsCharacters { my ($self, %args) = @_; my @fieldsToCheck = qw(orgName commonName countryName stateName localityName organizationName organizationNameUnit); foreach my $field (@fieldsToCheck) { if (exists $args{$field}) { $self->_checkValidCharacters($args{$field}, $field); } } if (exists $args{subjAltNames} and (defined $args{subjAltNames})) { foreach my $alt (@{ $args{subjAltNames} }) { my $name = 'Subject alternative name of type ' . $alt->{type}; my $value = $alt->{value}; $self->_checkValidCharacters($value, $name); } } } # openssl does not support UTF-8 a my $validRe = qr/^[A-Za-z0-9 .?&+:\-\@\*]*$/; sub _checkValidCharacters { my ($self, $string, $stringName) = @_; $stringName or $stringName = __('String'); if (not $string =~ $validRe) { throw EBox::Exceptions::InvalidData( data => $stringName, value => $string, advice => __('The field contains invalid ' . 'characters. All ASCII alphanumeric characters, ' . 'plus these non alphanumeric chars: .?&+:-@* ' . 'and spaces are allowed.' ) ); } } # Method: _supportActions # # Overrides # # This method determines if the service will have a button to start/restart # it in the module status widget. By default services will have the button # unless this method is overriden to return undef sub _supportActions { return undef; } # Method: _setConf # # Overrides: # # # sub _setConf { my ($self) = @_; EBox::CA::Certificates->genCerts(); } # Group: Private methods # Obtain the public key given the private key # # Return public key path if it is correct or undef if password is # incorrect sub _getPubKey # (privKeyFile, pubKeyFile, password?) { my ($self, $privKeyFile, $pubKeyFile, $password) = @_; $pubKeyFile = EBox::Config->tmp() . "pubKey.pem" unless (defined ($pubKeyFile)); my $cmd = "rsa "; $cmd .= "-in \'$privKeyFile\' "; $cmd .= "-out \'$pubKeyFile\' "; $cmd .= "-outform PEM "; $cmd .= "-pubout "; if ( defined($password) ) { $cmd .= "-passin env:PASS "; } $ENV{'PASS'} = $password if (defined($password)); my ($retVal) = $self->_executeCommand(command => $cmd); delete( $ENV{'PASS'} ); return undef if ($retVal eq "ERROR"); return $pubKeyFile; } # Generate the pkcs12 key store with password if required sub _generateP12Store { my ($self, $privKeyFile, $certMetadata, $password) = @_; my $certFile = $certMetadata->{path}; my $cn = $certMetadata->{dn}->attribute('commonName'); my $target = P12DIR . "${cn}.p12"; my $cmd = 'pkcs12 -export '; $cmd .= "-in '$certFile' "; $cmd .= "-inkey '$privKeyFile' "; $cmd .= "-name '$cn' "; $cmd .= "-out '$target' "; $password = '' unless (defined($password)); $cmd .= "-passout pass:$password "; $ENV{'PASS'} = $password if (defined($password)); my ($retVal) = $self->_executeCommand(command => $cmd); delete( $ENV{'PASS'} ); return undef if ($retVal eq "ERROR"); return $target; } # Given a serial number, it returns the file path sub _certFile { my ($self, $serial) = @_; my $file = CATOPDIR . "newcerts/${serial}.pem"; return $file; } # Given a common name, it returns the file path to the valid certificate # file which has as a subject a dn with this common name # Undef if the certificate does NOT exist # Path to the certificate if the certificate exists sub _findCertFile # (commonName) { my ($self, $commonName) = @_; open (my $fh, '<', INDEXFILE); my $found = '0'; my $certFile = undef; while(defined(my $line = <$fh>) and not $found) { my @fields = split ('\t', $line); if ( $fields[STATE_IDX] eq 'V') { # Extract cn from subject my $subject = EBox::CA::DN->parseDN($fields[SUBJECT_IDX]); $found = $subject->attribute("commonName") eq $commonName; if ($found) { $certFile = CERTSDIR . $fields[SERIAL_IDX] . ".pem"; return $certFile; } } } return $certFile; } # Create a request certificate # return undef if any error occurs sub _createRequest # (reqFile, genKey, privKey, keyPassword, dn, needPass?) { my ($self, %args) = @_; # To create the request the distinguished name is needed my $cmd = 'req'; $self->_commonArgs('req', \$cmd); $cmd .= '-new '; if ($args{genKey}) { $cmd .= qq{-keyout '$args{privKey}' }; if (defined($args{keyPassword})) { $cmd .= '-passout env:PASS '; } else { # If the password is undefined, the keys are created and left # unencrypted $cmd .= '-nodes '; } } else { $cmd .= qq{-key '$args{privKey}' }; if ($args{needPass}) { $cmd .= '-passin env:PASS '; } } $cmd .= qq{-out '$args{reqFile}' }; $cmd .= "-subj '" . $args{dn}->stringOpenSSLStyle() . "' "; # Not implemented till 0.9.8 # $cmd .= '-multivalue-rdn ' if ( $args{dn}->stringOpenSSLStyle() # =~ /[^\\](\\\\)*\+/); # We have to define the environment variable PASS to pass the # password $ENV{'PASS'} = $args{keyPassword} if ( defined($args{keyPassword})); # Execute the command my ($retVal, $output) = $self->_executeCommand(command => $cmd); delete( $ENV{'PASS'} ); return $output if ($retVal eq 'ERROR'); return; } # Sign a request # return the output error if any error occurr, nothing otherwise # ? Optional Parameter sub _signRequest # (userReqFile, days, userCertFile?, policy?, selfsigned?, # newSubject?, endDate?, subjAltNames?) { my ($self, %args) = @_; # For OpenSSL 0.9.7 if ($args{selfsigned}) { my $output = $self->_signSelfSignRequest( userReqFile => $args{userReqFile}, days => $args{days}, userCertFile => $args{userCertFile}, policy => $args{policy}, newSubject => $args{newSubject}, endDate => $args{endDate}, keyFile => CAPRIVKEY); if (defined $args{newSubject}) { $self->{dn} = $args{newSubject}; } # If any error has occurred return inmediately if ( $output) { return $output; } # Put in the index.txt file (update database...) $self->_putInIndex(dn => $self->caDN(), certFile => CACERT, serialNumber => $self->_obtain(CACERT,'serial')); return $output; } my $policy = "policy_anything" unless (defined($args{policy})); my $endDate = $self->_flatDate($args{endDate}) if defined($args{endDate}); # Sign the request my $cmd = "ca"; $self->_commonArgs("ca", \$cmd); # Only available since OpenSSL 0.9.8 # $cmd .= "-create_serial "if ($args{createSerial}); if (defined($self->{caKeyPassword})) { # it can be no pass for CA key $cmd .= "-passin env:PASS "; } $cmd .= "-outdir " . CERTSDIR . " "; $cmd .= "-out \'$args{userCertFile}\' " if defined($args{userCertFile}); $cmd .= '-extensions v3_ca ' if ( EXTENSIONS_V3 and $args{selfsigned}); my $tmpFileName; if ( defined($args{subjAltNames}) ) { $tmpFileName = $self->_generateExtFile($args{subjAltNames}); $cmd .= "-extfile '$tmpFileName' "; } else { $cmd .= '-extensions usr_cert ' if ( EXTENSIONS_V3 and not $args{selfsigned}); } # Only available in OpenSSL 0.9.8 $cmd .= "-selfsign " if ($args{selfsigned}); $cmd .= "-policy $policy "; $cmd .= "-keyfile " . CAPRIVKEY . " "; $cmd .= "-days $args{days} " if defined($args{days}); $cmd .= "-enddate " . $self->_flatDate($args{endDate}) . " " if defined($args{endDate}); if ( defined($args{newSubject}) ) { $cmd .= "-subj \"". $args{newSubject}->stringOpenSSLStyle() . "\" "; $cmd .= "-multivalue-rdn " if ( $args{newSubject}->stringOpenSSLStyle() =~ /[^\\](\\\\)*\+/); } $cmd .= "-in \'$args{userReqFile}\'"; $ENV{'PASS'} = $self->{caKeyPassword} if(defined($self->{caKeyPassword})); my ($retVal, $output) = $self->_executeCommand(command => $cmd); delete ( $ENV{'PASS'} ); # Remove ext file if exists # unlink( $tmpFileName ) if ( -e $tmpFileName ); return $self->_filterErrorFromOpenSSL($output) if ($retVal eq "ERROR"); return undef; } # Sign a self-signed request (compatible with OpenSSL 0.9.7 series) sub _signSelfSignRequest # (userReqFile, days?, userCertFile, # serialNumber?, newSubject?, endDate?, # keyFile) { my ($self, %args) = @_; my $endDate = $self->_flatDate($args{endDate}) if defined($args{endDate}); # Sign the request my $cmd = "req"; $self->_commonArgs("req", \$cmd); # Only available since OpenSSL 0.9.8 # Maybe the password is not necessary if ( defined($self->{caKeyPassword}) ){ $cmd .= "-passin env:PASS "; } $cmd .= "-out \'$args{userCertFile}\' " if defined($args{userCertFile}); $cmd .= "-extensions v3_ca " if ( EXTENSIONS_V3); $cmd .= "-x509 "; $cmd .= "-set_serial \"0x$args{serialNumber}\" " if defined($args{serialNumber}); $cmd .= "-days $args{days} " if defined($args{days}); $cmd .= "-enddate " . $self->_flatDate($args{endDate}) . " " if defined($args{endDate}); if ( defined($args{newSubject}) ) { $cmd .= "-subj \"". $args{newSubject}->stringOpenSSLStyle() . "\" "; $cmd .= "-multivalue-rdn " if ( $args{newSubject}->stringOpenSSLStyle() =~ /[^\\](\\\\)*\+/); } $cmd .= "-key $args{keyFile} "; $cmd .= "-in \'$args{userReqFile}\' "; $ENV{'PASS'} = $self->{caKeyPassword} if (defined($self->{caKeyPassword})); my ($retVal, $output) = $self->_executeCommand(command => $cmd); delete ( $ENV{'PASS'} ); return $output if ($retVal eq "ERROR"); return undef; } # Taken the OpenSSL command (req, x509, rsa...) # and add to the args the common arguments to all openssl commands # For now, to req and ca commands, it adds config file and batch mode # sub _commonArgs # (cmd, args) { my ($self, $cmd, $args ) = @_; if ( $cmd eq "ca" or $cmd eq "req" ) { ${$args} .= " -config " . SSLCONFFILE . " -batch "; } } # Given a certification file Obtain an attribute from the file # attribute => 'DN' return => EBox::CA::DN object # attribute => 'serial' return => String containing the serial number # attribute =>'endDate' return => Date::Calc::Object with the date # attribute => 'days' return => number of days from today to expire # attribute => 'subjAltNames' return => Array ref containing hash ref with key type of certificate # and value the value for the subject alt name # undef => if certification file does NOT exist sub _obtain # (certFile, attribute) { my ($self, $certFile ,$attribute) = @_; if (not -f $certFile) { return undef; } my $arg = ""; if ($attribute eq 'DN') { $arg = "-subject"; } elsif ($attribute eq 'serial') { $arg = "-serial"; } elsif ($attribute eq 'endDate' or $attribute eq 'days') { $arg = "-enddate"; } elsif ($attribute eq 'subjAltNames' ) { $arg = '-text -certopt -no_header,no_version,no_serial,no_signame,no_validity,no_subject'; } my $cmd = "x509 " . $arg . " -in \'$certFile\' -noout"; my ($retVal, $output) = $self->_executeCommand(command => $cmd); return undef if ($retVal ne "OK"); # Remove the attribute name part $arg =~ s/-//g; if ($arg eq "enddate") { $arg = "notAfter"; } $output =~ s/^$arg=( )*//g; chomp($output); if ($attribute eq 'DN') { return EBox::CA::DN->parseDN($output); } elsif ($attribute eq 'serial' ) { return $output; } elsif ($attribute eq 'endDate' ) { my ($monthStr, $day, $hour, $min, $sec, $yyyy) = ($output =~ /(.+) (\d+) (\d+):(\d+):(\d+) (\d+) (.+)/); $monthStr =~ s/ +//g; my $dateObj = Date::Calc->new($yyyy, Decode_Month($monthStr), $day, $hour, $min, $sec); return $dateObj; } elsif ($attribute eq 'days') { my $certDate = $self->_parseDate($output); my $diffDate = $certDate - Date::Calc->Today(); return $diffDate->day(); } elsif ($attribute eq 'subjAltNames') { my ($altNameStr) = $output =~ m/X509v3 Subject Alternative Name:\s+(.*)\s+X509v3 Au/s; return [] if not defined($altNameStr); $altNameStr =~ s/\s+//g; my @retValue = (); my @subjAltNames = split(/,/, $altNameStr); foreach my $subAlt (@subjAltNames) { my ($type, $value) = split(/:/, $subAlt); if ( $type eq 'IPAddress' ) { push(@retValue, { type => 'IP', value => $value }); } else { push(@retValue, { type => $type, value => $value }); } } return \@retValue; } } # Given the string date from index.txt file # obtain the date as a Date::Calc::Object sub _parseDate { my ($self, $str) = @_; my ($y,$mon,$mday,$h,$m,$s) = $str =~ /([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})Z$/; $y += 2000; # my $wday = Day_of_Week($y+1999,$mon,$mday); my $date = Date::Calc->new($y, $mon, $mday, $h, $m, $s); return $date; } # A private method to flat a Date::Calc::Object to a OpenSSL form like # YYMMDDHHMMSSZ sub _flatDate # (date) { my ($self, $date) = @_; return "" unless ( UNIVERSAL::isa($date, "Date::Calc")); my $dateStr =sprintf("%02d%02d%02d%02d%02d%02dZ", $date->year() - 2000, $date->month(), $date->day(), $date->hours(), $date->minutes(), $date->seconds()); return $dateStr; } # Create a serial number with 16 digits (a hex number) # Return the 16-digit string sub _createSerial { my $self = shift; srand(); my $serial = sprintf("%08X", int(rand(hex('0xFFFFFFFF')))); $serial .= sprintf("%08X", int(rand(hex('0xFFFFFFFF')))); return $serial; } # Get the maximum number of days to apply to a certificate expiration. # This is the Year 2038 Bug, explained here -> # http://www.mail-archive.com/openssl-users@openssl.org/msg45886.html sub _maxDays { my ($self) = @_; my $now = Date::Calc::Object->now(); # The final day is 2038 19th 04:14:07 :-O my $year2038 = Date::Calc::Object->new(2038, 1, 19, 4, 14, 7); my $diff = $year2038 - $now; return $diff->day(); } # Print the row for CA cert in index.txt file (to fuck them up) sub _putInIndex # (EBox::CA::DN dn, String certFile, String # serialNumber) { my ($self, %args) = @_; my $date = $self->_obtain($args{certFile}, 'endDate'); my $row = "V\t"; $row .= $self->_flatDate($date) . "\t\t"; $row .= $args{serialNumber} . "\t"; $row .= "unknown\t"; # I found that every subject has no final slash my $subject = $args{dn}->stringOpenSSLStyle(); $subject =~ s/\/$//g; $row .= $subject . "\n"; open (my $index, ">>" . INDEXFILE); print $index $row; close($index); } # Return if the given DN is the CA certificate file sub _isCACert # (EBox::CA::DN dn) { my ($self, $dn) = @_; my $caDN = $self->_obtain(CACERT, 'DN'); return $caDN->equals($dn); } # Write down the next serial to serial file, given a certificate file # Only useful under OpenSSL 0.9.7, later on fixed sub _writeDownNextSerial # (certFile) { my ($self, $certFile) = @_; my $cmd = "x509 "; $self->_commonArgs("x509", \$cmd); $cmd .= "-in \'$certFile\' "; $cmd .= "-noout "; $cmd .= "-next_serial "; $cmd .= "-out " . SERIALNOFILE; $self->_executeCommand(command => $cmd); } # Write down the serial attribute file # Only useful under OpenSSL 0.9.7, later on fixed sub _writeDownIndexAttr # (attrFile) { my ($self, $attrFile) = @_; open(my $fh, ">" . $attrFile); print $fh "unique_subject = yes" . $/; close($fh); } # Filter the given OpenSSL output from error to show normal messages # Return a normal error message # Welcome to the hell sub _filterErrorFromOpenSSL # (input) { my ($self, $input) = @_; if( $input eq "1") { $input = "1"; } elsif ( $input =~ m/unable to load CA private key/i ) { $input = __("Unable to sign. Wrong CA passphrase or has CA private key dissappeared?"); } elsif ( $input =~ m/invalid expiry date/i ) { $input = __("Database corruption"); } elsif ( $input =~ m/TXT_DB error number 2/i ) { $input = __("Identifier duplicated in Database"); } elsif ( $input =~ m/Already revoked/i ) { $input = __("Certificate already revoked"); } elsif ( $input =~ m/string too long/i ) { my ($maxSize) = ($input =~ /maxsize=(\d+)/ ); $input = __("ASN1 field length too long. Maximum Size:") . $maxSize; } else { $input = __("Unknown error. Given the OpenSSL output:") . $/ . $input; } return $input; } # Check none of other Zentyal modules uses the certificate # Return true if there's any module using it sub _certsInUse # (cn?, isCACert?) { my ($self, $cn, $isCACert) = @_; if ( not (defined($cn) and defined($isCACert)) ) { return undef; } $cn = $self->caDN()->attribute('commonName') if ($isCACert); my $global = EBox::Global->getInstance(); my @observers = @{$global->modInstancesOfType('EBox::CA::Observer')}; foreach my $obs (@observers) { if( $obs->certificateRevoked($cn, $isCACert) ){ return 1; } } return 1 if EBox::CA::Certificates->certificateRevoked($cn, $isCACert); return undef; } # Force observers to free this certificate sub _freeCert # (cn?, isCACert?) { my ($self, $cn, $isCACert) = @_; if ($isCACert) { $cn = $self->caDN()->attribute('commonName'); } my $global = EBox::Global->getInstance(); my @observers = @{$global->modInstancesOfType('EBox::CA::Observer')}; foreach my $obs (@observers) { $obs->freeCertificate($cn); } EBox::CA::Certificates->freeCertificate($cn); } # Set the CA as passwordful or passwordless sub _setPasswordRequired # (required) { my ($self, $required) = @_; $self->set_bool('pass_required', $required); } # Check if a date is later than CA certificate date # Returns the value to set the expiration date sub _isLaterThanCA # (date) { my ($self, $date) = @_; my $caExpirationDate = $self->caExpirationDate(); # gt compares date and time if ( $date gt $caExpirationDate ) { # != compares date if ( $date > $caExpirationDate ) { throw EBox::Exceptions::External( __('Expiration date later than CA certificate expiration date') ); } else { EBox::warn('Defining CA expiration date for a new certificate'); return $caExpirationDate; } } return $date; } # Check the given subject alternative names are correct sub _checkSubjAltNames { my ($self, $subjAltNames) = @_; foreach my $subjAlt (@{$subjAltNames}) { if ( $subjAlt->{type} eq 'DNS' ) { EBox::Validate::checkDomainName($subjAlt->{value}, 'DNS value'); } elsif ( $subjAlt->{type} eq 'IP' ) { EBox::Validate::checkIP($subjAlt->{value}, 'IP value'); } elsif ( $subjAlt->{type} eq 'email' ) { # copy is an special value to get the email value from CN subject if ( $subjAlt->{value} ne 'copy' ) { EBox::Validate::checkEmailAddress($subjAlt->{value}, 'email value'); } } else { throw EBox::Exceptions::InvalidData( data => 'type', value => $subjAlt->{type}, advice => __x('Only {dns}, {ip} and {email} are valid subject alternative ' . 'name types', dns => 'DNS', ip => 'IP', email => 'email'), ); } } } # Generate the v3 extension conf file to write the subj alt names # the results is the temporary file path to write onto sub _generateExtFile { my ($self, $subjAltNames) = @_; my $tmpFile = new File::Temp(DIR => EBox::Config::tmp()); $tmpFile->unlink_on_destroy(0); EBox::Module::Base::writeConfFileNoCheck($tmpFile->filename(), 'ca/v3_ext.mas', [ 'subjAltNames' => $subjAltNames ], ); return $tmpFile->filename(); } sub _audit { my ($self, $action, $arg) = @_; $self->{audit}->logAction('ca', 'Certification Authority', $action, $arg); } ##### # OpenSSL shell management ##### sub _startShell { my ($self) = @_; return if ( $self->{shell} ); my $open = '| ' . OPENSSLPATH . " 1>$output_shell" . " 2>$error_shell"; if ( not open($self->{shell}, $open) ) { throw EBox::Exceptions::Internal(__x("Cannot start OpenSSL shell. ({errval})", errval => $!)); } } sub _stopShell { my ($self) = @_; return if (not $self->{shell} ); print {$self->{shell}} "exit\n"; close($self->{shell}); undef($self->{shell}); } # Return two values into an array # (OK or ERROR, output) sub _executeCommand # (command, input, hide_output) { my ($self, %params) = @_; # Initialise the shell, launch exception if it is not possible $self->_startShell(); my $command = $params{command}; # EBox::debug("OpenSSL command: $command"); $command =~ s/\n*$//; $command .= "\n"; # Send the command if (not print {$self->{shell}} $command) { my $errVal = $!; throw EBox::Exceptions::Internal("Cannot write to the OpenSSL shell: $errVal"); } my $input; $input = $params{input} if (exists $params{input}); # Send the input if ($input and not print {$self->{shell}} $input . "\x00") { my $errVal = $!; throw EBox::Exceptions::Internal("Cannot write to the OpenSSL shell: $errVal"); } # Close the shell $self->_stopShell(); # check for errors if (-e $error_shell) { # There was an error my $ret = File::Slurp::read_file($error_shell); unlink($error_shell); if ( $ret =~ /error/i ) { unlink($output_shell); EBox::error("Error: $ret"); return ('ERROR', $ret); } } # Load the output my $ret = 1; if ( -e $output_shell ) { $ret = File::Slurp::read_file($output_shell); # $ret =~ s/^(OpenSSL>\s)*//s; $ret =~ s/^OpenSSL>\s*//gm; $ret = 1 if ($ret eq ""); } unlink($output_shell); my $msg = $ret; $msg = "" if ($params{hide_output}); return ('OK', $ret); } sub caDN { my ($self) = @_; if (not defined $self->{dn}) { $self->{dn} = $self->_obtain(CACERT, 'DN'); } return $self->{dn}; } sub caExpirationDate { my ($self) =@_; if (not defined $self->{caExpirationDate}) { $self->{caExpirationDate} = $self->_obtain(CACERT, 'endDate'); } return $self->{caExpirationDate}; } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/0000775000000000000000000000000012017147622014233 5ustar zentyal-ca-2.3.6+quantal1/src/EBox/CGI/IssueCertificate.pm0000664000000000000000000001273512017147622020034 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::IssueCertificate; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Exceptions::DataMissing; use EBox::Exceptions::External; use EBox::Exceptions::Internal; use EBox::Exceptions::InvalidData; use EBox::Gettext; use EBox::Global; use EBox::Validate; use Error qw(:try); # Constants: use constant MIN_PASS_LENGTH => 5; # Method: new # # Constructor for IssueCertificate CGI # # Returns: # # IssueCertificate - The object recently created sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); $self->{chain} = 'CA/Index'; bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return ['name', 'expiryDays', 'certificate' ]; } # Method: optionalParameters # # Overrides: # # # sub optionalParameters { return ['caNeeded', 'caPassphrase', 'reCAPassphrase', 'countryName', 'stateName', 'localityName', 'subjectAltName']; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; my $ca = EBox::Global->modInstance('ca'); my $issueCA = $self->param('caNeeded'); $issueCA = 0 unless defined($issueCA); my $name = $self->unsafeParam('name'); unless (defined($name) and ($name ne '')) { if ($issueCA) { throw EBox::Exceptions::DataMissing(data => __('Organization Name') ); } else { throw EBox::Exceptions::DataMissing(data => __('Common Name') ); } } my $days = $self->param('expiryDays'); my $subjAltName = $self->unsafeParam('subjectAltName'); my $countryName = $self->param('countryName'); my $localityName = $self->param('localityName'); my $stateName = $self->param('stateName'); my $caPass = $self->param('caPassphrase'); my $reCAPass = $self->param('reCAPassphrase'); if ( $issueCA ) { # Check passpharses if ( defined ( $caPass ) and defined ( $reCAPass )) { unless ( $caPass eq $reCAPass ) { throw EBox::Exceptions::External(__('CA passphrases do NOT match')); } # Set no pass if the pass is empty if ( $caPass eq '' ) { $caPass = undef; } # Check length if ( defined ( $caPass ) and length ( $caPass ) < MIN_PASS_LENGTH ) { throw EBox::Exceptions::External(__x('CA Passphrase should be at least {length} characters', length => MIN_PASS_LENGTH)); } } } $caPass = undef if ( (not defined($caPass)) or $caPass eq '' ); unless ( $days > 0) { throw EBox::Exceptions::External(__x('Days to expire ({days}) must be ' . 'a positive number', days => $days)); } # Only validate the following format for subjectAltName # :,:value # type = DNS, IP, email # value = DNS -> DomainName # IP -> IP address # email -> email address my @subjAltNamesParam; if ( $subjAltName ) { my @subjAltNames = split(/,/, $subjAltName); if ( @subjAltNames > 0) { foreach my $subAlt (@subjAltNames) { my ($type, $value) = split(/:/, $subAlt); push(@subjAltNamesParam, { type => $type, value => $value }); } } else { throw EBox::Exceptions::External(__('The Subject Alternative Name parameter ' . 'must follow this pattern: type:value, type:value')); } } my $retValue; if ($issueCA) { $retValue = $ca->issueCACertificate( orgName => $name, days => $days, countryName => $countryName, localityName => $localityName, stateName => $stateName, caKeyPassword => $caPass, genPair => 1); } else { $retValue = $ca->issueCertificate( commonName => $name, days => $days, caKeyPassword => $caPass, subjAltNames => \@subjAltNamesParam, ); } my $msg = __("The certificate has been issued."); $msg = __("The new CA certificate has been issued.") if ($issueCA); $self->setMsg($msg); # Delete all CGI parameters for CA/Index $self->cgi()->delete_all(); } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/RenewCertificate.pm0000664000000000000000000001103212017147622020011 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::RenewCertificate; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; # For exceptions use Error qw(:try); use EBox::Exceptions::DataInUse; # Method: new # # Constructor for RenewCertificate CGI # # Returns: # # RenewCertificate - The object recently created sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); $self->{chain} = "CA/Index"; bless($self, $class); return $self; } # Process the HTTP query sub _process { my $self = shift; my $ca = EBox::Global->modInstance('ca'); if ( $self->param('cancel') ) { $self->setRedirect( 'CA/Index' ); $self->setMsg( __("The certificate has NOT been renewed") ); $self->cgi()->delete_all(); return; } $self->_requireParam('isCACert', __('Boolean indicating Certification Authority Certificate') ); $self->_requireParam('expireDays', __('Days to expire') ); my $commonName = $self->unsafeParam('commonName'); # We have to check it manually if ( not defined($commonName) or $commonName eq '' ) { throw EBox::Exceptions::DataMissing(data => __('Common Name')); } # Only valid chars minus '/' plus '*' --> security risk unless ( $commonName =~ m{^[\w .?&+:\-\@\*]*$} ) { throw EBox::Exceptions::External(__('The input contains invalid ' . 'characters. All alphanumeric characters, ' . 'plus these non alphanumeric chars: .?&+:-@* ' . 'and spaces are allowed.')); } # Transform %40 in @ $commonName =~ s/%40/@/g; # Transform %20 in space $commonName =~ s/%20/ /g; my $isCACert = $self->param('isCACert'); my $expireDays = $self->param('expireDays'); my $caPassphrase = $self->param('caPassphrase'); $caPassphrase = undef if ( $caPassphrase eq '' ); unless ( $expireDays > 0) { throw EBox::Exceptions::External(__x('Days to expire ({days}) must be ' . 'a positive number', days => $expireDays)); } my $retValue; my $retFromCatch; if ( defined ($self->param('renewForced')) ) { if ( $isCACert ) { $retValue = $ca->renewCACertificate( days => $expireDays, caKeyPassword => $caPassphrase, force => 'true', ); } else { $retValue = $ca->renewCertificate( commonName => $commonName, days => $expireDays, caKeyPassword => $caPassphrase, force => 'true', ); } } else { try { if ( $isCACert ) { $retValue = $ca->renewCACertificate( days => $expireDays, caKeyPassword => $caPassphrase, ); } else { $retValue = $ca->renewCertificate( commonName => $commonName, caKeyPassword => $caPassphrase, days => $expireDays); } } catch EBox::Exceptions::DataInUse with { $self->{template} = '/ca/forceRenew.mas'; $self->{chain} = undef; my $cert = $ca->getCertificateMetadata( cn => $commonName ); my @array; push (@array, 'metaDataCert' => $cert); push (@array, 'expireDays' => $expireDays); push (@array, 'caPassphrase' => $caPassphrase); $self->{params} = \@array; $retFromCatch = 1; }; } if ( not $retFromCatch ) { if (not defined($retValue) ) { throw EBox::Exceptions::External(__('The certificate CANNOT be renewed')); } else { my $msg = __("The certificate has been renewed"); $msg = __("The new CA certificate has been renewed") if ($isCACert); $self->setMsg($msg); $self->cgi()->delete_all(); } } } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/CreateCA.pm0000664000000000000000000000700612017147622016203 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::CreateCA; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox; use EBox::Gettext; use EBox::Global; # Constants: use constant MIN_PASS_LENGTH => 5; # Method: new # # Constructor for CreateCA CGI # # Returns: # # CreateCA - The object recently created # sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); bless($self, $class); $self->setChain('CA/Index'); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { my ($self) = @_; return [qw(orgName expiryDays ca)] } # Method: optionalParameters # # Overrides: # # # sub optionalParameters { my ($self) = @_; return [qw(countryName stateName localityName caPassphrase reCAPassphrase)]; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; my $gl = EBox::Global->getInstance(); my $ca = $gl->modInstance('ca'); my $orgName = $self->param('orgName'); my $countryName = $self->param('countryName'); my $localityName = $self->param('localityName'); my $stateName = $self->param('stateName'); my $days = $self->param('expiryDays'); my $caPass = $self->param('caPassphrase'); my $reCAPass = $self->param('reCAPassphrase'); # Check passpharses if ( defined ( $caPass ) and defined ( $reCAPass )) { unless ( $caPass eq $reCAPass ) { throw EBox::Exceptions::External(__('CA passphrases do NOT match')); } # Set no pass if the pass is empty if ( $caPass eq '' ) { $caPass = undef; } } # Check length if ( defined ( $caPass ) and length ( $caPass ) < MIN_PASS_LENGTH ) { throw EBox::Exceptions::External(__x('CA Passphrase should be at least {length} characters', length => MIN_PASS_LENGTH)); } unless ($days > 0) { throw EBox::Exceptions::External(__x('Days to expire ({days}) must be ' . 'a positive number', days => $days)); } my $retVal = $ca->createCA( orgName => $orgName, countryName => $countryName, localityName => $localityName, stateName => $stateName, days => $days, caKeyPassword => $caPass, ); if (not defined($retVal) ) { throw EBox::Exceptions::External(__('Problems creating Certification Authority has happened')); } $self->cgi()->delete_all(); } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/ShowForm.pm0000664000000000000000000000540412017147622016340 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::ShowForm; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; use EBox; # Method: new # # Constructor for ShowForm CGI. # Show a common form (one for revokation and other for renewal) # # Returns: # # ShowForm - The object recently created sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); bless($self, $class); return $self; } # Process the HTTP query sub _process { my $self = shift; my $ca = EBox::Global->modInstance('ca'); my @array = (); my $cn = $self->unsafeParam('cn'); unless (defined($cn) and ($cn ne '')) { throw EBox::Exceptions::DataMissing(data => __('Common Name') ); } # Only valid chars minus '/' plus '*' --> security risk unless ( $cn =~ m{^[\w .?&+:\-\@\*]*$} ) { throw EBox::Exceptions::External(__('The input contains invalid ' . 'characters. All alphanumeric characters, ' . 'plus these non alphanumeric chars: .?&+:-@* ' . 'and spaces are allowed.')); } $self->_requireParam('action', __('Action')); my $action = $self->param('action'); if ($action eq "revoke") { $self->{template} = "ca/formRevoke.mas"; } elsif ($action eq "renew") { $self->{template} = "ca/formRenew.mas"; } elsif ($action eq "reissue") { $self->{template} = "ca/formReissue.mas"; } else { throw EBox::Exceptions::External(__('Only revoke, renew and reissue actions are performed')); } my $cert = $ca->getCertificateMetadata(cn => $cn); if (not defined($cert) ) { # If the common name does NOT exist sent to Index.pm $self->{errorchain} = "CA/Index"; throw EBox::Exceptions::External(__x("Common name: {cn} does NOT exist in database" , cn => $cn)); } push (@array, metaDataCert => $cert); push (@array, reasons => $ca->revokeReasons()); push (@array, passRequired => $ca->passwordRequired()); $self->{params} = \@array; } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/RevokeCertificate.pm0000664000000000000000000001060312017147622020167 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::RevokeCertificate; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; # For exceptions use Error qw(:try); use EBox::Exceptions::DataInUse; # Method: new # # Constructor for RevokeCertificate CGI # # Returns: # # RevokeCertificate - The object recently created sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); $self->{chain} = "CA/Index"; bless($self, $class); return $self; } # Process the HTTP query # Templates that come from: forceRevoke.mas and formRevoke.mas sub _process { my $self = shift; # If it comes from forceRevoke with a cancel button if ( defined($self->param('cancel')) ) { $self->setRedirect( 'CA/Index' ); $self->setMsg( __("The certificate has NOT been revoked.") ); $self->cgi()->delete_all(); return; } my $ca = EBox::Global->modInstance('ca'); $self->_requireParam('isCACert', __('Boolean indicating Certification Authority Certificate') ); $self->_requireParam('reason', __('Reason') ); my $commonName = $self->unsafeParam('commonName'); # We have to check it manually if ( not defined($commonName) or $commonName eq '' ) { throw EBox::Exceptions::DataMissing(data => __('Common Name')); } # Only valid chars minus '/' plus '*' --> security risk unless ( $commonName =~ m{^[\w .?&+:\-\@\*]*$} ) { throw EBox::Exceptions::External(__('The input contains invalid ' . 'characters. All alphanumeric characters, ' . 'plus these non alphanumeric chars: .?&+:-@* ' . 'and spaces are allowed.')); } # Transform %40 in @ $commonName =~ s/%40/@/g; # Transform %20 in space $commonName =~ s/%20/ /g; my $isCACert = $self->param('isCACert'); my $reason = $self->param('reason'); my $caPassphrase = $self->param('caPassphrase'); $caPassphrase = undef if ( $caPassphrase eq '' ); my @array = (); my $retValue; my $retFromCatch = undef; if ( defined($self->param("revokeForce")) ) { # If comes from a forceRevoke with forceRevoke button if ( $isCACert ) { $ca->revokeCACertificate(reason => $reason, caKeyPassword => $caPassphrase, force => 1); } else { $ca->revokeCertificate(commonName => $commonName, reason => $reason, caKeyPassword => $caPassphrase, force => 1); } } else { # If it comes from a formRevoke.mas try { if ( $isCACert ) { $retValue = $ca->revokeCACertificate( reason => $reason, caKeyPassword => $caPassphrase, ); } else { $retValue = $ca->revokeCertificate( commonName => $commonName, caKeyPassword => $caPassphrase, reason => $reason); } } catch EBox::Exceptions::DataInUse with { $self->{template} = '/ca/forceRevoke.mas'; $self->{chain} = undef; my $cert = $ca->getCertificateMetadata( cn => $commonName ); push (@array, 'metaDataCert' => $cert); push (@array, 'isCACert' => $isCACert); push (@array, 'reason' => $reason); push (@array, 'caPassphrase' => $caPassphrase); $self->{params} = \@array; $retFromCatch = 1; }; } if (not $retFromCatch) { my $msg = __("The certificate has been revoked"); $msg = __("The CA certificate has been revoked") if ($isCACert); $self->setMsg($msg); # No parameters to send to CA/Index $self->cgi()->delete_all(); } } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/DownloadFiles.pm0000664000000000000000000001166212017147622017331 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::DownloadFiles; # CGI to download key pair and certificates from a specific user or # to download public key and certificate from Certification Authority use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; # Method: new # # Constructor for DownloadFiles CGI # # Returns: # # DownloadFiles - The object recently created sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); # To download something, we need errorchain $self->{errorchain} = 'CA/Index'; bless($self, $class); return $self; } # Process the HTTP query sub _process { my ($self) = @_; $self->{ca} = EBox::Global->modInstance('ca'); # Check if the CA infrastructure has been created my @array = (); $self->{cn} = $self->unsafeParam('cn'); # We have to check it manually if it exists if ( not defined($self->{cn}) or ($self->{cn} eq "") ) { throw EBox::Exceptions::DataMissing(data => __('Common Name')); } # Transform %40 in @ $self->{cn} =~ s/%40/@/g; # Transform %20 in space $self->{cn} =~ s/%20/ /g; my $metaDataCert = $self->{ca}->getCertificateMetadata( cn => $self->{cn}); if (not defined($metaDataCert) ) { throw EBox::Exceptions::External(__x("Common name: {cn} does NOT exist in database" , cn => $self->{cn})); } my $files = {}; # If it is the CA certificate, only possibility to download Public Key and certificate if ($metaDataCert->{"isCACert"}) { $files->{publicKey} = $self->{ca}->CAPublicKey(); $files->{certificate} = $self->{ca}->getCACertificateMetadata()->{path}; } else { $files = $self->{ca}->getKeys($self->{cn}); $files->{certificate} = $self->{ca}->getCertificateMetadata(cn => $self->{cn})->{path}; $files->{p12} = $self->{ca}->getP12KeyStore($self->{cn}); } my $zipfile; if ( $metaDataCert->{"isCACert"} ) { $zipfile = EBox::Config->tmp() . "CA-key-and-cert.tar.gz"; } else { $zipfile = EBox::Config->tmp() . "keys-and-cert-" . $self->{cn} . ".tar.gz"; } unlink($zipfile); # We make symbolic links in order to make dir-plained tar file my ($linkPrivate, $linkPublic, $linkCert, $linkP12); if ( $metaDataCert->{"isCACert"} ) { $linkPublic = "ca-public-key.pem"; $linkCert = "ca-cert.pem"; } else { $linkPrivate = $self->{cn} . "-private-key.pem"; $linkPublic = $self->{cn} . "-public-key.pem"; $linkCert = $self->{cn} . "-cert.pem"; $linkP12 = $self->{cn} . ".p12"; } link($files->{privateKey}, EBox::Config->tmp() . $linkPrivate) if ($linkPrivate); link($files->{publicKey}, EBox::Config->tmp() . $linkPublic); link($files->{certificate}, EBox::Config->tmp() . $linkCert); link($files->{p12}, EBox::Config->tmp() . $linkP12) if ($linkP12); my $tarArgs = qq{'$zipfile' }; $tarArgs .= qq{'$linkPrivate' } if ( $linkPrivate ); $tarArgs .= qq{'$linkPublic' '$linkCert'}; $tarArgs .= qq{ '$linkP12'} if ( $linkP12 ); # -h to dump what links point to my $ret = system('tar -C ' . EBox::Config->tmp() . ' -czhf ' . $tarArgs); unlink(EBox::Config->tmp() . $linkPrivate) if ($linkPrivate); unlink(EBox::Config->tmp() . $linkPublic); unlink(EBox::Config->tmp() . $linkCert); unlink(EBox::Config->tmp() . $linkP12) if ($linkP12); if ($ret != 0) { throw EBox::Exceptions::External(__("Error creating file") . ": $!"); } # Setting the file $self->{downfile} = $zipfile; # Remove trailing slashes, only name $zipfile =~ s/^.+\///; $self->{downfilename} = $zipfile; } # Overwrite the _print method to send the file sub _print { my ($self) = @_; if ($self->{error} or not defined($self->{downfile})) { $self->SUPER::_print; return; } open( my $keyFile, $self->{downfile} ) or throw EBox::Exceptions::Internal("Could NOT open key file."); print($self->cgi()->header(-type => 'application/octet-stream', -attachment => $self->{downfilename})); while(<$keyFile>) { print $_; } close($keyFile); } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CGI/Index.pm0000664000000000000000000000407612017147622015647 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::CA::Index; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; # Method: new # # Constructor for Index CGI # # Returns: # # Index - The object recently created sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Certification Authority'), @_); bless($self, $class); return $self; } # Method: masonParameters # # Overrides: # # # sub masonParameters { my ($self) = @_; my $ca = EBox::Global->modInstance('ca'); # Check if the CA infrastructure has been created my @array = (); if ( $ca->isCreated() ) { $self->{'template'} = "ca/index.mas"; # Update CA DB prior to displaying certificates $ca->updateDB(); push( @array, 'certs' => $ca->listCertificates() ); # Check if a new CA certificate is needed (because of revokation from RevokeCertificate) my $currentState = $ca->currentCACertificateState(); if ( $currentState =~ m/[RE]/) { push( @array, 'caNeeded' => 1); } else { push( @array, 'passRequired' => $ca->passwordRequired() ); push( @array, 'caExpirationDays' => $ca->caExpirationDays() ); } } else { $self->{'template'} = "ca/createCA.mas"; } return \@array; } 1; zentyal-ca-2.3.6+quantal1/src/EBox/Model/0000775000000000000000000000000012017147622014671 5ustar zentyal-ca-2.3.6+quantal1/src/EBox/Model/Certificates.pm0000664000000000000000000002107712017147622017643 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CA::Model::Certificates; # Class: EBox::CA::Model::Certificates # # Form to set the rollover certificates for modules # use base 'EBox::Model::DataTable'; use strict; use warnings; use EBox::Gettext; use EBox::Global; use EBox::Types::Text; use EBox::Types::DomainName; use EBox::Types::Boolean; use EBox::CA; # Group: Public methods # Constructor: new # # Create the new Certificates model # # Overrides: # # # # Returns: # # - the recently created model # sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ( $self, $class ); return $self; } # Method: precondition # # Check if CA has been created. # # Overrides: # # # sub precondition { my ($self) = @_; my $ca = EBox::Global->modInstance('ca'); return $ca->isAvailable(); } # Method: preconditionFailMsg # # Returns message to be shown on precondition fail. # # Overrides: # # # sub preconditionFailMsg { my ($self) = @_; return __x('You must create a Certification Authority first. ' . 'Go to {openhref}Certification Authority{closehref} to do so', openhref => qq{}, closehref => qq{}); } # Method: syncRows # # Syncronizes installed modules certificate requests with the current model. # # Overrides: # # # sub syncRows { my ($self, $currentRows) = @_; my @srvs = @{EBox::CA::Certificates->srvsCerts()}; my %currentSrvs = map { $self->row($_)->valueByName('service') => 1 } @{$currentRows}; my @srvsToAdd = grep { not exists $currentSrvs{$_->{'service'}} } @srvs; my $modified = 0; for my $srv (@srvsToAdd) { my $cn = exists $srv->{'defaultCN'} ? $srv->{'defaultCN'} : __('Zentyal'); my $allowCustomCN = exists $srv->{allowCustomCN} ? $srv->{allowCustomCN} : 1; $self->add(module => $srv->{'module'}, service => $srv->{'service'}, cn => $cn, allowCustomCN => $allowCustomCN, enable => 0); $modified = 1; } my %srvsToAdd = map { $_->{service} => 1 } @srvs; for my $id (@{$currentRows}) { my $row = $self->row($id); my $service = $row->valueByName('service'); my $module = $row->valueByName('module'); if (not exists $srvsToAdd{$service} or not EBox::Global->modExists($module)) { $self->removeRow($id); $modified = 1; } } return $modified; } # Method: disableService # # Disables given service in the model. # sub disableService { my ($self, $service) = @_; my $row = $self->find(service => $service); if ($row) { $row->elementByName('enable')->setValue(0); $row->store(); } } # Method: setServiceRO # # Set service as read-only. # sub setServiceRO { my ($self, $service, $ro) = @_; my $row = $self->find(service => $service); if ($row) { $row->setReadOnly($ro); $row->store(); } } # Method: updateCN # # Updates the CN in the certificate for the given service. # sub updateCN { my ($self, $service, $cn) = @_; my $row = $self->find(service => $service); if ($row) { $row->elementByName('cn')->setValue($cn); $row->store(); } } # Method: certUsedByService # # Returns if a given certificate Common Name is used by any # of the services in the model. # # Returns: # # True if the certificate is used, false otherwise # sub certUsedByService { my ($self, $cn) = @_; my $row = $self->find(cn => $cn); return 1 if ($row); return 0; } # Method: cnByService # # Returns the certificate Common Name used by a service. # # Returns: # # The Common Name if exists, undef otherwise. # sub cnByService { my ($self, $service) = @_; my $row = $self->find(service => $service); return $row->valueByName('cn') if ($row); return undef; } # Method: isEnabledService # # Returns if a given service is enabled in the model. # # Returns: # # True if the service is enabled, undef otherwise # sub isEnabledService { my ($self, $service) = @_; my $row = $self->find(service => $service); return $row->valueByName('enable') if ($row); return undef; } # Group: Private methods # Method: _table # # Overrides: # # # sub _table { my @tableHeader = ( new EBox::Types::Text( fieldName => 'module', printableName => __('Module'), unique => 0, editable => 0, filter => sub { my ($self) = @_; my $modName = $self->value(); my $mod = EBox::Global->modInstance($modName); # return modname if the module was uninstalled return $modName unless defined ($mod); return $mod->title(); }, ), new EBox::Types::Text( fieldName => 'service', printableName => __('Service'), unique => 1, editable => 0, ), new EBox::Types::DomainName( fieldName => 'cn', printableName => __('Common Name'), unique => 0, editable => 1, ), new EBox::Types::Boolean( fieldName => 'enable', printableName => __('Enable'), editable => 1, help => __('Generate the certificate using CA ' . 'with the common name set above'), ), new EBox::Types::Boolean( fieldName => 'allowCustomCN', printableName => 'allowCustomCN', editable => 0, hidden => 1, defaultValue => 1, ), ); my $dataTable = { tableName => 'Certificates', printableTableName => __('Services Certificates'), printableRowName => __('certificate'), defaultActions => [ 'editField', 'changeView' ], tableDescription => \@tableHeader, class => 'dataTable', sortedBy => 'module', modelDomain => 'CA', help => __('Here you may set certificates from this CA for those ' . 'secure services managed by Zentyal'), }; return $dataTable; } sub validateTypedRow { my ($self, $action, $params_r, $actual_r) = @_; if ($action eq 'update') { if (exists $params_r->{cn}) { if (not $actual_r->{allowCustomCN}->value()) { throw EBox::Exceptions::External( __('This service does not allow to change the certificate common name') ); } } } } sub updatedRowNotify { my ($self, $row, $oldRow, $force) = @_; my $modName = $row->valueByName('module'); my $mod = EBox::Global->modInstance($modName); $mod->setAsChanged(); } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CA/0000775000000000000000000000000012017147622014114 5ustar zentyal-ca-2.3.6+quantal1/src/EBox/CA/Observer.pm0000664000000000000000000000524712017147622016251 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CA::Observer; use strict; use warnings; use EBox::Gettext; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Method: certificateRevoked # # Invoked when a certificate is gonna be revoked, this method # receives the common name which identifies the certificate and # if it is the CA certificate. Returning a true value means that # this module's configuration would become inconsistent if such # the revokation is made. In that case the CA module will # not make the change, but warn the user instead. You should # override this method if you need to. # # Parameters: # # commonName - common name which identifies the certificate # isCACert - is the CA certificate? # # Returns: # # true - if module's configuration becomes inconsistent # false - otherwise # sub certificateRevoked # (commonName, isCACert) { return undef; } # Method: certificateExpired # # Invoked when a certificate has expired or is about to do # it. You should override this method if you need to. It cannot # be prevented since time is time. # # Parameteres: # # commonName - common name which identifies the certificate # isCACert - is the CA certificate? # sub certificateExpired # (commonName, isCACert) { return undef; } # Method: certificateRenewed # # Invoked when a certificate has been renewed. # You should override this method if you need to. # # Parameteres: # # commonName - common name which identifies the certificate # isCACert - is the CA certificate? # sub certificateRenewed # (commonName, isCACert) { return undef; } # Method: freeCertificate # # Tells this module that an certificate is going to be revoked or has expired, # so that it can remove it from its configuration. # # Parameters: # # commonName - common name which identifies the certificate # sub freeCertificate # (commonName) { # default empty implementation. Subclasses should override this as # needed. } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CA/TestStub.pm0000664000000000000000000006535412017147622016244 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CA::TestStub; # Description: Test stub for CA module used by OpenVPN use strict; use warnings; use EBox::CA; use Test::MockObject; use EBox::Gettext; use EBox; # Method: fake # # Fakes the CA module sub fake { Test::MockObject->fake_module('EBox::CA', _create => \&_create, isCreated => \&isCreated, createCA => \&createCA, revokeCACertificate => \&revokeCACertificate, issueCACertificate => \&issueCACertificate, renewCACertificate => \&renewCACertificate, CAPublicKey => \&CAPublicKey, issueCertificate => \&issueCertificate, revokeCertificate => \&revokeCertificate, listCertificates => \&listCertificates, getKeys => \&getKeys, renewCertificate => \&renewCertificate, currentCACertificateState => \¤tCACertificateState, destroyCA => \&destroyCA, setInitialState => \&setInitialState, getCurrentCRL => \&getCurrentCRL, ); } # Method: unfake # # Returns real CA module to reality # sub unfake { delete $INC{'EBox/CA.pm'}; eval 'use EBox::CA'; $@ and die "Error reloading EBox::CA : $@"; } # Method: _create # # Fake constructor # # Returns: # # A mocked EBox::CA object sub _create { my $class = shift; my $self = {}; bless($self, $class); # certs is a hash with the following elements # ca -> metadata CA cert # other certs metadata indexed by serial number # Each metadata is comprised: # state -> 'V', 'R' or 'E' # dn -> EBox::CA::DN # expiryDate -> expiration date # revokeDate -> revokation date # reason -> if revoked, a reason # path -> faked path # serial -> a serial number # keys -> [publicKeyPath, privateKeyPath ] $self->{certs} = {}; $self->{created} = 0; $self->{crl} = undef; return $self; } # Method: destroyCA # # Destroy current structure from a CA # sub destroyCA { my ($self) = @_; # Destroy everything created -> not created and no certificates $self->{certs} = {}; $self->{created} = 0; $self->{crl} = undef; return 1; } # Method: isCreated # # Fake method # sub isCreated { my ($self) = @_; return $self->{created}; } # Method: createCA # # Fake method # # Parameters: # # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Aragon) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # orgName - organization name (eg, company name) # orgNameUnit - organizational unit name (eg, section name) (Optional) # commonName - common name from the CA (Optional) # caKeyPassword - passphrase for generating keys (*NOT WORKING*) # days - expire day of self signed certificate (Optional) # # Returns: # # 1 - if everything is newly created # 2 - if the CA certificate already exists # # Exceptions: # # - if any required parameter is missing sub createCA { my ($self, %args) = @_; throw EBox::Exceptions::DataMissing(data => __('Organization Name')) unless defined( $args{orgName} ); # Set CA created if ($self->{created}) { return 2; } $self->{created} = 1; $args{commonName} = "foo" unless ( $args{commonName} ); # Setting CA certificate metadata $self->{certs}->{ca} = {}; $self->{certs}->{ca}->{dn} = EBox::CA::DN->new ( countryName => $args{countryName}, stateName => $args{stateName}, localityName => $args{localityName}, organizationName => $args{orgName}, organizationNameUnit => $args{orgNameUnit}, commonName => $args{commonName}); $self->{certs}->{ca}->{state} = 'V'; my $days = $args{days}; $days = 30 unless ($days); $self->{certs}->{ca}->{expiryDate} = Date::Calc::Object->now() + [0, 0, $days, 0, 0, 0]; $self->{certs}->{ca}->{serial} = $self->_createSerial(); $self->{certs}->{ca}->{path} = "ca.cert"; # Set keys $self->{certs}->{ca}->{keys} = [ "capubkey.pem", "caprivkey.pem" ]; return 1; } # Method: revokeCACertificate # # Fake method # # Parameters: # # reason - the reason to revoke the certificate. It can be: # unspecified, keyCompromise, CACompromise, # affiliationChanged, superseeded, cessationOfOperation # or certificationHold (Optional) # caKeyPassword - the CA passphrase (*NOT WORKING*) # force - Force the revokation (*NOT WORKING*) # # Returns: # # undef if OK # sub revokeCACertificate { my ($self, %args) = @_; $self->{certs}->{ca}->{state} = 'R'; $self->{certs}->{ca}->{reason} = $args{reason}; foreach my $key (keys %{$self->{certs}}) { $self->{certs}->{$key}->{state} = 'R'; $self->{certs}->{$key}->{reason} = "cessationOfOperation"; $self->{certs}->{$key}->{revokeDate} = Date::Calc::Object->now(); } $self->{crl} = "lastest-crl.pem"; return undef; } # Method: issueCACertificate # # Fake method # # Parameters: # # commonName - the CA common name (Optional) # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Zaragoza) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # # orgName - organization name (eg, company) # # orgNameUnit - organizational unit name (eg, section) # (Optional) # days - days to hold the same certificate (Optional) # caKeyPassword - key passpharse for CA (*NOT WORKING*) # genPair - if you want to generate a new key pair (*NOT WORKING*) # # Returns: # # the new certificate file path # # Exceptions: # # DataMissing - if any required parameter is missing sub issueCACertificate { my ($self, %args) = @_; throw EBox::Exceptions::DataMissing(data => __('Organization Name')) unless defined( $args{orgName} ); if ($self->{certs}->{ca}->{state} eq 'V') { throw EBox::Exceptions::External( __('The CA certificates should be revoked or has expired before issuing a new certificate')); } # Copy revoked if exists my $oldSerial = $self->{certs}->{ca}->{serial}; if ($oldSerial) { $self->{certs}->{$oldSerial}->{serial} = $oldSerial; $self->{certs}->{$oldSerial}->{dn} = $self->{certs}->{ca}->{dn}->copy(); $self->{certs}->{$oldSerial}->{state} = $self->{certs}->{ca}->{state}; $self->{certs}->{$oldSerial}->{revokeDate} = $self->{certs}->{ca}->{revokeDate}; $self->{certs}->{$oldSerial}->{path} = $self->{certs}->{ca}->{path}; $self->{certs}->{$oldSerial}->{keys} = $self->{certs}->{ca}->{keys}; } # Define the distinguished name -> default values in configuration file $args{commonName} = "foo" unless ( $args{commonName} ); $self->{certs}->{ca}->{dn} = EBox::CA::DN->new ( countryName => $args{countryName}, stateName => $args{stateName}, localityName => $args{localityName}, organizationName => $args{orgName}, organizationNameUnit => $args{orgNameUnit}, commonName => $args{commonName}); my $days = $args{days}; $days = 30 unless ($args{days}); $self->{certs}->{ca}->{state} = 'V'; $self->{certs}->{ca}->{expiryDate} = Date::Calc::Object->now() + [0, 0, $days, 0, 0, 0];; $self->{certs}->{ca}->{serial} = $self->_createSerial(); $self->{certs}->{ca}->{path} = "ca.cert"; # Set keys $self->{certs}->{ca}->{keys} = [ "capubkey.pem", "caprivkey.pem" ]; return $self->{certs}->{ca}->{path}; } # Method: renewCACertificate # # Fake # # Parameters: # # commonName - the CA common name (Optional) # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Zaragoza) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # # orgName - organization name (eg, company) (Optional) # # orgNameUnit - organizational unit name (eg, section) # (Optional) # days - days to hold the same certificate (Optional) # caKeyPassword - key passpharse for CA (*NOT WORKING*) # # Returns: # # the new certificate file path or undef if any error happened # # Exceptions: # # DataMissing - if no caKeyPassword is given # sub renewCACertificate { my ($self, %args) = @_; $self->{certs}->{$self->{certs}->{ca}->{serial}} = {}; $self->{certs}->{$self->{certs}->{ca}->{serial}}->{state} = 'R'; $self->{certs}->{$self->{certs}->{ca}->{serial}}->{revokeDate} = Date::Calc::Object->now(); $self->{certs}->{$self->{certs}->{ca}->{serial}}->{dn} = $self->{certs}->{ca}->{dn}->copy(); $self->{certs}->{$self->{certs}->{ca}->{serial}}->{reason} = 'superseded'; $self->{certs}->{$self->{certs}->{ca}->{serial}}->{path} = $self->{certs}->{ca}->{serial} . ".cert"; $self->{certs}->{$self->{certs}->{ca}->{serial}}->{serial} = $self->{certs}->{ca}->{serial}; $self->{certs}->{$self->{certs}->{ca}->{serial}}->{keys} = [ $self->{certs}->{ca}->{serial} . "-pubkey.pem", $self->{certs}->{ca}->{serial} . "-privkey.pem" ]; $self->{certs}->{ca}->{state} = 'V'; $self->{certs}->{ca}->{dn} = EBox::CA::DN->new ( countryName => $args{countryName}, stateName => $args{stateName}, localityName => $args{localityName}, organizationName => $args{orgName}, organizationNameUnit => $args{orgNameUnit}, commonName => $self->{certs}->{ca}->{dn}->attribute('commonName')); my $days = $args{days}; $days = 30 unless ($args{days}); $self->{certs}->{ca}->{expiryDate} = Date::Calc::Object->now() + [0, 0, $days, 0, 0, 0]; $self->{certs}->{ca}->{serial} = $self->_createSerial(); foreach my $key (keys %{$self->{certs}}) { my $cert = $self->{certs}->{$key}; if ($cert->{state} eq 'V' and $cert->{expiryDate} > $self->{certs}->{ca}->{expiryDate} ) { # Renew the certificate to the CA certificate $self->renewCertificate( commonName => $cert->{dn}->attribute('commonName'), endDate => $self->{certs}->{ca}->{expiryDate} ); } } return $self->{certs}->{ca}->{path}; } # Method: CAPublicKey # # Fake method # # Parameters: # # caKeyPassword - the passphrase to access to private key # (*NOT WORKING*) # # Returns: # # Path to the file which contains the CA Public Key in # PEM format or undef if it was not possible to create # sub CAPublicKey { my ($self, $caKeyPassword) = @_; return $self->{certs}->{ca}->{keys}[0]; } # Method: issueCertificate # # Fake method # # Parameters: # # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Zaragoza) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # # orgName - organization name (eg, company) (Optional) # # orgNameUnit - organizational unit name (eg, section) # (Optional) # # commonName - common name from the organization # days - expiration days of certificate (Optional) # Only valid if endDate is not present # endDate - expiration date Date::Calc::Object (Optional) # # caKeyPassword - passphrase for CA to sign (*NOT WORKING*) # privateKeyFile - path to the private key file if there is already # a private key file in the CA (Optional) # # requestFile - path to save the new certificate request # (*NOT WORKING*) # certFile - path to store the new certificate file (*NOT WORKING*) # # Returns: # # undef if no problem has happened # # Exceptions: # # External - if the expiration date from certificate to issue is later than CA # certificate expiration date (*NOT WORKING*) # if any error happens in signing request process (*NOT WORKING*) # # DataMissing - if any required parameter is missing # sub issueCertificate { my ($self, %args) = @_; # Treat arguments throw EBox::Exceptions::DataMissing(data => __('Common Name')) unless defined( $args{commonName} ); my $serial = $self->_createSerial(); my $days; if (not defined($args{endDate})) { $days = $args{days}; $days = 30 unless ($args{days}); $self->{certs}->{ca}->{expiryDate} = Date::Calc::Object->now() + [0, 0, $days, 0, 0, 0]; } $self->{certs}->{ca}->{expiryDate} = $args{endDate} if ($args{endDate}); $self->{certs}->{$serial}->{path} = $serial . ".cert"; # Define the distinguished name # We take the default values from CA dn my $dn = $self->{certs}->{ca}->{dn}->copy(); $dn->attribute("countryName", $args{countryName}) if (defined($args{countryName})); $dn->attribute("stateName", $args{stateName}) if (defined($args{stateName})); $dn->attribute("localityName", $args{localityName}) if (defined($args{localityName})); $dn->attribute("orgName", $args{orgName}) if (defined($args{orgName})); $dn->attribute("orgNameUnit", $args{orgNameUnit}) if (defined($args{orgNameUnit})); $dn->attribute("commonName", $args{commonName}) if (defined($args{commonName})); $self->{certs}->{$serial}->{state} = 'V'; $self->{certs}->{$serial}->{dn} = $dn; $self->{certs}->{$serial}->{serial} = $serial; # Setting keys my $privKeyPath = $args{privateKeyFile}; $privKeyPath = $serial . "-privkey.pem"; $self->{certs}->{$serial}->{keys} = [ $serial . "-pubkey.pem", $privKeyPath ]; } # Method: revokeCertificate # # Fake method # # Parameters: # # commonName - the common name with the certificate to revoke # reason - the reason to revoke the certificate. It can be: # unspecified, keyCompromise, CACompromise, # affiliationChanged, superseeded, cessationOfOperation # or certificationHold (Optional) # caKeyPassword - the CA passpharse (*NOT WORKING*) # certFile - the Certificate to revoke (*NOT WORKING*) # force - Force the revokation (*NOT WORKING*) # # Returns: # # undef if OK # # Exceptions: # # External - if the certificate does NOT exist # if the reason is NOT a standard one # if any error occurred when revokation is done # if any error occurred when creating the CRL is done # DataMissing - if any required parameter is missing # sub revokeCertificate { my ($self, %args) = @_; my $commonName = $args{commonName}; my $reason = $args{reason}; throw EBox::Exceptions::DataMissing(data => __('Common Name') ) unless defined($commonName); # Find the cert my $cert; foreach my $key (keys %{$self->{certs}}) { if ($self->{certs}->{$key}->{dn}->attribute('commonName') eq $commonName) { $cert = $self->{certs}->{$key}; } } if (not $cert) { throw EBox::Exceptions::External("not certificate found"); } $cert->{state} = 'R'; $cert->{reason} = $reason; # Create the Certification Revokation List $self->{crl} = "lastest-crl.pem"; } # Method: listCertificates # # Fake method # # Parameters: # # state - 'R', 'V' or 'E' in order to show only revoked, valid # or expired certificates. All are included if not set this # attribute (Optional) # # excludeCA - boolean indicating whether the valid CA certificate # should be excluded in the response (Optional) # # Returns: # # A reference to an array containing hashes which have the following # elements # # - dn - an object # - state - 'V' from Valid, 'R' from Revoked or 'E' from Expired # - expiryDate - the expiry date in a if state valid # # - revokeDate - the revocation date in a Date hash if state is # revoked # - reason - reason to revoke if state is revoked # - isCACert - boolean indicating that it is the valid CA certificate # - path - certificate path # - serialNumber - serial number within CA # sub listCertificates { my ($self, %args) = @_; # Getting the arguments my $state = $args{'state'}; my $excludeCA = $args{'excludeCA'}; # Check parameter state is correct (R, V or E) if (defined($state) and $state !~ m/[RVE]/ ) { throw EBox::Exceptions::Internal("State should be R, V or E"); } # Convert a hash to an array my @listCerts; foreach my $key (keys %{$self->{certs}}) { my %element; my $cert = $self->{certs}->{$key}; $element{'state'} = $cert->{state}; $element{'dn'} = $cert->{dn}; $element{'serialNumber'} = $cert->{serial}; $element{'path'} = $cert->{path}; if ($element{'state'} eq 'V') { $element{'expiryDate'} = $cert->{expiryDate}; $element{'isCACert'} = $key eq 'ca'; } else { $element{'revokeDate'} = $cert->{revokeDate}; $element{'reason'} = $cert->{reason}; } push (@listCerts, \%element); } # Setting the filters if ( defined($state) ) { # Filter according to state @listCerts = grep { $_->{state} eq $state } @listCerts; } if ( $excludeCA ) { # Filter the valid CA certificate @listCerts = grep { not $_->{isCACert} } @listCerts; } # Sort the array to have CA certs first (put latest first) my @sortedOut = sort { $b->{state} cmp $a->{state} } @listCerts; return \@sortedOut; } # Method: getKeys # # Fake method # # Parameters: # # commonName - the common name to identify the key # # Returns: # # a reference to a hash containing the public and private key file # paths (*privateKey* and *publicKey*) stored in _PEM_ format # # Exceptions: # # External - if the keys do NOT exist # DataMissing - if any required parameter is missing # sub getKeys { my ($self, $commonName) = @_; throw EBox::Exceptions::DataMissing(data => __("Common Name")) unless defined ($commonName); my %keys; my ($cert) = grep { $_->{dn}->attribute('commonName') eq $commonName } values %{$self->{certs}}; if ($cert) { $keys{publicKey} = $cert->{keys}[0]; $keys{privateKey} = $cert->{keys}[1]; } else { throw EBox::Exceptions::External(__x("The user {commonName} does NOT exist", commonName => $commonName)) } return \%keys; } # Method: renewCertificate # # Fake method # # Parameters: # # commonName - the common name from the user. # # countryName - country name {2 letter code} (eg, ES) (Optional) # stateName - state or province name (eg, Zaragoza) (Optional) # localityName - locality name (eg, Zaragoza) (Optional) # # orgName - organization name (eg, company) (Optional) # # orgNameUnit - organizational unit name (eg, section) # (Optional) # days - days to hold the same certificate (Optional) # Only if enddate not appeared # endDate - the exact date when the cert expired (Optional) # Only if days not appeared.It is a Date::Calc::Object. # # caKeyPassword - key passpharse for CA (*NOT WORKING*) # certFile - the certificate file to renew (*NOT WORKING*) # reqFile - the request certificate file which to renew (*NOT WORKING*) # # privateKeyFile - the private key file (Optional) # keyPassword - the private key passpharse. Only necessary when # a new request is issued (*NOT WORKING*) # # overwrite - overwrite the current certificate file. Only if # the certFile is passed (*NOT WORKING*) # # Returns: # # the new certificate file path # # Exceptions: # # External - if the user does NOT exist, # if the CA passpharse CANNOT be located, # if the user passpharse is needed and it's NOT present, # if the expiration date for the certificate to renew is later than CA certificate expiration date # if the certificate to renew does NOT exist # if any error occurred when certificate renewal is done # Internal - if any parameter is an unexcepted type # DataMissing - if no CA passphrase is given to renew the certificate sub renewCertificate { my ($self, %args) = @_; if (not defined($args{endDate}) ) { $args{days} = 365 unless defined ($args{days}); if ( $args{days} > 11499 ) { $args{days} = 11499; # Warning -> Year 2038 Bug # http://www.mail-archive.com/openssl-users@openssl.org/msg45886.html EBox::warn(__("Days set to the maximum allowed: Year 2038 Bug")); } } my $userExpDay = $args{endDate}; $userExpDay = Date::Calc::Object->now() + [0, 0, $args{days}, 0, 0, 0] unless ($userExpDay); if ( $userExpDay gt $self->{certs}->{ca}->{expiryDate} ) { throw EBox::Exceptions::External(__("Expiration date later than CA certificate expiration date")); } # User cert # Find the cert my ($cert) = grep { $_->{dn}->attribute('commonName') eq $args{commonName} } values %{$self->{certs}}; # Check if a change in DN is needed my $userDN = $cert->{dn}->copy(); my $dnFieldHasChanged = '0'; if ( defined($args{countryName}) and $args{countryName} ne $userDN->attribute('countryName')) { $dnFieldHasChanged = "1"; $userDN->attribute('countryName', $args{countryName}); } if (defined($args{stateName}) and $args{stateName} ne $userDN->attribute('stateName')) { $dnFieldHasChanged = "1" ; $userDN->attribute('stateName', $args{stateName}); } if (defined($args{localityName}) and $args{localityName} ne $userDN->attribute('localityName')) { $dnFieldHasChanged = "1" ; $userDN->attribute('localityName', $args{localityName}); } if (defined($args{orgName}) and $args{orgName} ne $userDN->attribute('orgName')) { $dnFieldHasChanged = "1" ; $userDN->attribute('orgName', $args{orgName}); } if (defined($args{orgNameUnit}) and $args{orgNameUnit} ne $userDN->attribute('orgNameUnit')) { $dnFieldHasChanged = "1" ; $userDN->attribute('orgNameUnit', $args{orgNameUnit}); } $self->revokeCertificate(commonName => $userDN->attribute('commonName'), reason => "superseded"); $self->issueCertificate(commonName => $userDN->attribute('commonName'), endDate => $args{endDate}, privateKeyFile => $args{privateKeyFile}); } # Method: currentCACertificateState # # Fake method # # Returns: # # The current CA Certificate state # - R - Revoked # - E - Expired # - V - Valid # - ! - Inexistent # sub currentCACertificateState { my ($self) = @_; my $certRef = $self->{certs}->{ca}; if ( not defined($certRef) ) { return "!"; } else { return $certRef->{'state'}; } } # Method: setInitialState # # Set a serie of certs for a CA # # Parameters # # listCert - a list reference of hashes with cert metadata. The # hash should have the following elements: # - state -> 'V', 'R' or 'E' (Optional) # - dn -> or an String formatted as /type0=value0/type1=value1/... # - expiryDate -> expiration date (Optional) # - revokeDate -> revokation date (Optional) # - reason -> if revoked, a reason (Optional) # - isCACert -> boolean indicating if it's a valid CA certificate # -> Just ONE can have this attribute on (Optional) # - path -> string with the certificate path (Optional) # - keys -> path for keys as an array reference, first element is the public one and second one is the private one # sub setInitialState { my ($self, $listCerts) = @_; my $caCertShown = 0; $self->{certs} = {}; foreach my $argCertRef (@{$listCerts}) { my $serial = $self->_createSerial(); my $certRef; # Checking just one CA cert is given, the rest are ignored if (not $caCertShown and $argCertRef->{isCACert}) { $self->{certs}->{ca} = {}; $certRef = $self->{certs}->{ca}; $caCertShown = 1; $self->{created} = 1; } else { $self->{certs}->{$serial} = {}; $certRef = $self->{certs}->{$serial}; } # Copying all remainder data $certRef->{state} = 'V' unless ($argCertRef->{state}); $certRef->{state} = $argCertRef->{state} if ($argCertRef->{state}); if (UNIVERSAL::isa($argCertRef->{dn}, "EBox::CA::DN") ) { $certRef->{dn} = $argCertRef->{dn}; } else { # I assume an string is passed $certRef->{dn} = EBox::CA::DN->parseDN($argCertRef->{dn}); } if ($certRef->{state} eq 'V' or $certRef->{state} eq 'E') { $certRef->{expiryDate} = Date::Calc::Object->now() + [0,0,+365] unless ($argCertRef->{expiryDate}); $certRef->{expiryDate} = $argCertRef->{expiryDate} if ($argCertRef->{expiryDate}); } elsif ($certRef->{state} eq 'R') { $certRef->{revokeDate} = $argCertRef->{revokeDate} if ($argCertRef->{revokeDate}); $certRef->{revokeDate} = Date::Calc::Object->now(); $certRef->{reason} = $argCertRef->{reason}; } $certRef->{path} = $serial . ".cert" unless ($argCertRef->{path}); $certRef->{path} = $argCertRef->{path} if ($argCertRef->{path}); $certRef->{serial} = $serial; if ($argCertRef->{keys}) { $certRef->{keys} = $argCertRef->{keys}; } else { $certRef->{keys} = [ $serial . "-pubkey.pem", $serial . "-privkey.pem" ]; } # Check if the CRL should be created (any R) $self->{crl} = "lastest-crl.pem" if ( $certRef->{state} eq 'R' ); } return; } # Method: getCurrentCRL # # Fake method # # Returns: # # Path to the current CRL or undef if there is no CRL # sub getCurrentCRL { my ($self) = @_; return $self->{crl}; } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CA/Certificates.pm0000664000000000000000000001343112017147622017061 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::CA::Certificates # # package EBox::CA::Certificates; use base qw(EBox::CA::Observer); use strict; use warnings; use EBox::Gettext; use EBox::Global; use File::Temp qw(tempfile); # Group: Public methods # Constructor: new # # Create the new CA Certificates model # # Returns: # # - the recently created model # sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Method: genCerts # # Generates all the certificates requested by all the services # sub genCerts { my ($self) = @_; my @srvscerts = @{$self->srvsCerts()}; foreach my $srvcert (@srvscerts) { $self->_genCert($srvcert); } } # Method: certificateRevoked # # Overrides: # # # sub certificateRevoked { my ($self, $commonName, $isCACert) = @_; my $ca = EBox::Global->modInstance('ca'); my $model = $ca->model('Certificates'); return $model->certUsedByService($commonName); } # Method: certificateRenewed # # Overrides: # # # sub certificateRenewed { my ($self) = @_; $self->genCerts(); #FIXME only regen renewed certs } # Method: certificateExpired # # Overrides: # # # sub certificateExpired { my ($self, $commonName, $isCACert) = @_; my $ca = EBox::Global->modInstance('ca'); my $model = $ca->model('Certificates'); my @srvscerts = @{$self->srvsCerts()}; foreach my $srvcert (@srvscerts) { my $service = $srvcert->{'service'}; my $cn = $model->cnByService($service); if ($cn eq $commonName) { $model->disableService($service); } } } # Method: freeCertificate # # Overrides: # # # sub freeCertificate { my ($self, $commonName) = @_; my $ca = EBox::Global->modInstance('ca'); my $model = $ca->model('Certificates'); my @srvscerts = @{$self->srvsCerts()}; foreach my $srvcert (@srvscerts) { my $service = $srvcert->{'service'}; my $cn = $model->cnByService($service); if ($cn eq $commonName) { $model->disableService($service); } } } # Method: srvsCerts # # All services which request a certificate as provided # by EBox::Module::Service::certificates() plus the # module they are from. # # Returns: # # A ref to array with all the services information # sub srvsCerts { my ($self) = @_; my @srvscerts; my @mods = @{$self->_modsService()}; for my $mod (@mods) { my @modsrvs = @{EBox::Global->modInstance($mod)->certificates()}; next unless @modsrvs; for my $srv (@modsrvs) { $srv->{'module'} = $mod; push(@srvscerts, $srv); } } return \@srvscerts; } # Group: Public methods # Method: _genCert # # Generates the certificate for a service # sub _genCert { my ($self, $srvcert) = @_; my $ca = EBox::Global->modInstance('ca'); my $model = $ca->model('Certificates'); my $service = $srvcert->{'service'}; return undef unless ($model->isEnabledService($service)); my $cn = $model->cnByService($service); return undef unless (defined($cn)); my $certMD = $ca->getCertificateMetadata(cn => $cn); if ((not defined($certMD)) or ($certMD->{state} ne 'V')) { # Check the expiration date my $caMD = $ca->getCACertificateMetadata(); $ca->issueCertificate( commonName => $cn, endDate => $caMD->{expiryDate}, ); } my $cert = $ca->getCertificateMetadata(cn => $cn)->{'path'}; my $privkey = $ca->getKeys($cn)->{'privateKey'}; my ($tempfile_fh, $tempfile) = tempfile(EBox::Config::tmp . "/ca_certificates_XXXXXX") or throw EBox::Exceptions::Internal("Could not create temporal file."); open(CERT, $cert) or throw EBox::Exceptions::Internal('Could not open certificate file.'); my @certdata = ; close(CERT); open(KEY, $privkey) or throw EBox::Exceptions::Internal('Could not open certificate file.'); my @privkeydata = ; close(KEY); print $tempfile_fh @certdata; print $tempfile_fh @privkeydata; close($tempfile_fh); my @commands; my $user = $srvcert->{'user'}; my $group = $srvcert->{'group'}; push (@commands, "/bin/chown $user:$group $tempfile"); my $mode = $srvcert->{'mode'}; push (@commands, "/bin/chmod $mode $tempfile"); my $path = $srvcert->{'path'}; push (@commands, "mkdir -p `dirname $path`"); push (@commands, "mv -f $tempfile $path"); EBox::Sudo::root(@commands); } # Method: _modsService # # All configured service modules (EBox::Module::Service) # which could be implmenting the certificates method. # # Returns: # # A ref to array with all the Module::Service names # sub _modsService { my ($self) = @_; my @names = @{EBox::Global->modInstancesOfType('EBox::Module::Service')}; my @mods; foreach my $name (@names) { $name->configured() or next; push (@mods, $name->name()); } return \@mods; } 1; zentyal-ca-2.3.6+quantal1/src/EBox/CA/DN.pm0000664000000000000000000001160012017147622014751 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CA::DN; use strict; use warnings; use Storable qw(dclone); use EBox; # Constructor: new # # Constructor for DN class. # This class stores and manages the Distinguished Name # defined in X.500 specification. # # Parameters: # # countryName - the country name {2 letter code} (Optional) # stateName - the state name (Optional) # localityName - the locality name (Optional) # organizationName - the Organization Name # organizationNameUnit - the Organization Unit Name # commonName - the common name # # Returns: # # EBox::CA::DN - the recently created object # sub new { my ($class, %args) = @_; my $self = {}; bless($self, $class); $self->{countryName} = $args{countryName} if defined($args{countryName}); $self->{stateName} = $args{stateName} if defined($args{stateName}); $self->{localityName} = $args{localityName} if defined($args{localityName}); $self->{organizationName} = $args{organizationName}; $self->{organizationNameUnit} = $args{organizationNameUnit}; $self->{commonName} = $args{commonName}; return $self; } # Method: parseDN # # Constructor for DN class. # From a /type0=value0/... environment take # the distinguish name fields # # Parameters: # # parameters - the string containg the parameters # in /type0=value=0/ style # # Returns: # # EBox::CA::DN - the recently created object with the parameters # sub parseDN { my ($class, $parameters) = @_; my $self = {}; bless($self, $class); return undef unless defined ($parameters); chomp($parameters); my @fields = split('/', $parameters); foreach my $field (@fields) { my ($type, $value) = split('=', $field); if ($value) { $self->{countryName} = $value if ($type eq "C"); $self->{stateName} = $value if ($type eq "ST"); $self->{localityName} = $value if ($type eq "L"); $self->{organizationName} = $value if ($type eq "O"); $self->{organizationUnitName} = $value if ($type eq "OU"); $self->{commonName} = $value if ($type eq "CN"); } } return $self; } # Method: copy # # Clone the DN object # # Returns: # # EBox::CA::DN - the recently cloned object # sub copy { my $self = shift; my $copy = Storable::dclone($self); return $copy; } # Method: stringOpenSSLStyle # # the string containing the DN with an acceptable format # to pass to open ssl # # # Returns: # # string - formatted as /type0=value0/type1=value1/... # sub stringOpenSSLStyle { my $self = shift; my $outStr = ""; $outStr .= "/C=" . $self->{countryName} if ($self->{countryName}); $outStr .= "/ST=" . $self->{stateName} if ($self->{stateName}); $outStr .= "/L=" . $self->{localityName} if ($self->{localityName}); $outStr .= "/O=" . $self->{organizationName} if ($self->{organizationName}); $outStr .= "/OU=" . $self->{organizationNameUnit} if ($self->{organizationNameUnit}); $outStr .= "/CN=" . $self->{commonName} if ($self->{commonName}); $outStr .= "/"; return $outStr; } # Method: attribute # # set and get method for an attribute in a distinguished name # # Parameters: # # attributeName - the attribute name (It can be one of the # following: countryName, stateName, localityName, # OrganizationName, OrganizationNameUnit, commonName # value - the new attribute's value (Optional) # # Returns: # # string - representing the attribute value to set or get # or undef if the attribute does NOT exist # sub attribute { my ($self, $attrName, $value) = @_; if ( $self->{$attrName} ) { $self->{$attrName} = $value if defined($value); } return $self->{$attrName}; } # Method: equals # # check if two objects contain the same data # # Parameters: # # object - the object to compare with # # Returns: # # boolean - true if both object are equal or false otherwise sub equals # (object) { my ($self, $object) = @_; # If two objects are different, returns false if (ref($self) ne ref($object) ) { return 0; } foreach my $key (keys %{$self}) { if ( $self->{$key} ne $object->{$key} ) { return 0 } } return 1; } 1; zentyal-ca-2.3.6+quantal1/stubs/0000775000000000000000000000000012017147622013345 5ustar zentyal-ca-2.3.6+quantal1/stubs/v3_ext.mas0000664000000000000000000000271212017147622015261 0ustar <%doc> This stub file write down specific V3 extensions issuing a certificate. These extensions are applicable as well as default ones. Parameters: subjAltNames - Array indicating the subject alternative names. The elements are the following: type - String indicating the type of subject Alt Name value - String the value for that subject Alt Name <%args> @subjAltNames <%init> my $subjAltNameStr = join(',', map { $_->{type} . ':' . $_->{value} } @subjAltNames); my $dnsPresent = grep { $_->{type} eq 'DNS' } @subjAltNames; my $emailPresent = grep { $_->{type} eq 'email' } @subjAltNames; my $extendedUsage = ''; if ( $dnsPresent ) { $extendedUsage = 'serverAuth,clientAuth,'; } if ( $emailPresent ) { $extendedUsage .= 'emailProtection'; } # Remove trailing comma, if available $extendedUsage =~ s/,$//; % if (@subjAltNames > 0) { % if ( $extendedUsage ) { extendedKeyUsage = <% $extendedUsage %> % } subjectAltName= <% $subjAltNameStr %> % } # PKIX recommendations harmless if included in all certificates. authorityKeyIdentifier=keyid,issuer subjectKeyIdentifier = hash # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE zentyal-ca-2.3.6+quantal1/extra/0000775000000000000000000000000012017147622013330 5ustar zentyal-ca-2.3.6+quantal1/extra/openssl.cnf0000664000000000000000000002250112017147622015503 0ustar # # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. # # This definition stops the following lines choking if HOME isn't # defined. HOME = /var/lib/zentyal #RANDFILE = $ENV::HOME/.rnd RANDFILE = /var/lib/zentyal/.rnd # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) [ new_oids ] # We can add new OIDs in here for use by 'ca' and 'req'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] # FIXME set the directory dinamically) dir = /var/lib/zentyal/CA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file x509_extensions = usr_cert # The extentions to add to the cert # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. # copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. # crl_extensions = crl_ext default_days = 365 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha1 # which md to use. preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 1024 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extentions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret # output_password = secret # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. # pkix : PrintableString, BMPString. # utf8only: only UTF8Strings. # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. # WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings # so use this option with caution! string_mask = nombstr # req_extensions = v3_req # The extensions to add to a certificate request [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = ES countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Zaragoza localityName = Locality Name (eg, city) localityName_default = Zaragoza 0.organizationName = Organization Name (eg, company) 0.organizationName_default = eBox Technologies S.L. # we can do this but it is not needed normally :-) #1.organizationName = Second Organization Name (eg, company) #1.organizationName_default = World Wide Web Pty Ltd organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = commonName = Common Name (eg, YOUR name) commonName_default = user@domain.net commonName_max = 64 emailAddress = Email Address emailAddress_max = 64 # SET-ex3 = SET extension number 3 [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ usr_cert ] # These extensions are added when 'ca' signs a request. # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always # This is what PKIX recommends but some broken software chokes on critical # extensions. #basicConstraints = critical,CA:true # So we do this instead. basicConstraints = CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best # left out by default. # keyUsage = cRLSign, keyCertSign # Some might want this also # nsCertType = sslCA, emailCA # Include email address in subject alt name: another PKIX recommendation # subjectAltName=email:copy # Copy issuer details # issuerAltName=issuer:copy # DER hex encoding of an extension: beware experts only! # obj=DER:02:03 # Where 'obj' is a standard or added object # You can even override a supported extension: # basicConstraints= critical, DER:30:03:01:01:FF [ crl_ext ] # CRL extensions. # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always,issuer:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer:always # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo zentyal-ca-2.3.6+quantal1/extra/files.list0000664000000000000000000000004212017147622015323 0ustar openssl.cnf /var/lib/zentyal/conf zentyal-ca-2.3.6+quantal1/ChangeLog0000664000000000000000000001075212017147622013764 0ustar 2.3.6 + Fixed CRL generation when the link to the latest CRL is broken 2.3.5 + Adapted to new Model management framework 2.3.4 + Fixed quoting in generate p12 command 2.3.3 + Packaging fixes for precise 2.3.2 + Updated Standards-Version to 3.9.2 2.3.1 + Fixed executable permissions in src/scripts 2.3 + Fixed variable interpolation in internal exception text + Bugfix: do not try to get orgName in destroyCA after files are removed + Replaced autotools with zbuildtools 2.2.2 + Fixed typo bug in method destroyCA 2.2.1 + Do not cache CA certificates values on module creation to play along well with usercorner + Default CN for certificates is now translatable 2.1.6 + Avoid service certificates errors when a module is uninstalled 2.1.5 + Reissue with Subject Alternative Names is now direct + Reissue is only possible with expired and revoked certificates + Use own code for launching OpenSSL commands 2.1.4 + Removed /zentyal prefix from URLs + Avoid call to caExpirationDays when CA certifcate is revoked + Avoid duplicated restart during postinst 2.1.3 + Default CA expiration set to 10 years and certificates expiration set by default to the same value of the CA expiration + Log actions with AuditLogging + Removed use of obsolete LogAdmin 2.1.2 + Use proper reload icon 2.1.1 + Non-ASCII characters ban fully enforced, fixed problem in chmod mode when reinstalling package + Added more CN related option to services certificates 2.1 + Remove obsolete migrations and use new initialSetup method 2.0.3 + Services certificates are stored in backups 2.0.2 + Bugfix: Certificate generation works now when the base directory doesn't exist 2.0.1 + Certificates can be reissued after expiration or revocation 1.5.4 + Zentyal rebrand 1.5.3 + Fixed CA server error in user corner 1.5.2 + Adds setServiceRO and updateCN methods to Certificates + Allow new subjAltNames on renewCertificate, disabled there a broken external excepcion 1.5.1 + Generate PKCS12 keystore with private key and certificate using empty export password and add it to download archive + Bugfix: Only ASCII alphanumeric characters are allowed in common name to workaround the yet not fixed issue with UTF-8 and OpenSSL + Give support for subject alternative names: the available types are DNS, IP address and email address + Add revokation reason listing certificates + Fix enconding for CA HTML templates + Cancel button works again when revoking and renewing a certificate + Bugfix: DIRMODE changed to 0751 and migration script to fix existing deployments 1.3.14 + Add custom module status section 1.3.13 + Bugfix: Service certificates are issued again if they were revoked or they expired + Service certificates are issued using CA expiration date + Adding some help text to Service certificates model 1.1.30 + Do not show CA in module status + Do not show a restart button for CA 1.1.20 + Allow same expiration days for CA and new issued certificates setting to latter ones the same expiration date that CA one has 1.1.10 + Generate CRL at CA startup and correct CRL names 1.1.0 + Allow * character in common names + Updating in certificate authority DB is now working to show expired certificates in the list and call observer when this event happens + Country code, state and locality fields for CA certificate are now displayed and fill optionally by user 0.12.101 + Set SSLv3 extensions correctly to user certificates 0.12.100 + Bugfix: Expire days cannot be zero anymore 0.12.99 + New release 0.12 + New release 0.11.102 + Bugfix: Downloading files, tar is outputting nothing to stdout 0.11.101 + New release 0.11.100 + New release 0.11.99 + Check "days" are positive numbers 0.11 + New release 0.10.99 + New release 0.9.100 + New release 0.9.99 + Set a CA passphrase optionally + Fixed bug when forcing revoke CA certificate 0.9.3 + New release 0.9.2 + New release 0.9.1 + Fixed a bug related to renew or revoke currently used certificates by other modules + Added Cancel buttons where they are necessary 0.9 + Added Polish translation + Added German translation + Added Aragonese translation + Added Dutch translation + Fixed CRL permission issue related to ebox-openvpn module + Fixed bug related to back up configuration and create a bug report when the certification authority has not created yet + Fixed random error issuing certificates + Fixed names to accept all input valid parameters except for '/' + Make Organization Name visible at the GUI 0.8.99 + Initial release zentyal-ca-2.3.6+quantal1/AUTHORS0000664000000000000000000000024012017147622013251 0ustar Copyright (C) 2007-2012 eBox Technologies S.L. For an updated list of the current and past developers please visit: http://trac.zentyal.org/wiki/Contributors zentyal-ca-2.3.6+quantal1/schemas/0000775000000000000000000000000012017147622013630 5ustar zentyal-ca-2.3.6+quantal1/schemas/ca.yaml0000664000000000000000000000005612017147622015100 0ustar class: 'EBox::CA' models: - Certificates