Updates to SSL and other general items

This commit is contained in:
Deon George 2014-10-09 23:23:02 +11:00
parent c952738750
commit 8d71460121
25 changed files with 813 additions and 391 deletions

View File

@ -39,7 +39,7 @@ class Config extends Kohana_Config {
} }
public static function Copywrite($html=FALSE) { public static function Copywrite($html=FALSE) {
return ($html ? '©' : '(c)').' 2014 Deon George'; return ($html ? '©' : '(c)').' 2014 IBM';
} }
public static function version() { public static function version() {

View File

@ -17,6 +17,12 @@ class Controller_User_Welcome extends Controller_Welcome {
); );
public function action_index() { public function action_index() {
Block::factory()
->title('Quick Shortcuts')
->title_icon('icon-bookmark')
->span(3)
->body(View::factory('welcome/user/shortcuts'));
$n = ORM::factory('ADMIN')->where('EMAIL_ADDRESS','=',$this->ao->email)->find_all(); $n = ORM::factory('ADMIN')->where('EMAIL_ADDRESS','=',$this->ao->email)->find_all();
if (! $n->count()) if (! $n->count())
$output = 'You have no currently registered ADMINs, would you like to '.HTML::anchor(URL::link('user','admin/add'),'Register').' one?'; $output = 'You have no currently registered ADMINs, would you like to '.HTML::anchor(URL::link('user','admin/add'),'Register').' one?';
@ -84,14 +90,6 @@ class Controller_User_Welcome extends Controller_Welcome {
->title_icon('icon-info-sign') ->title_icon('icon-info-sign')
->span(9) ->span(9)
->body($output); ->body($output);
/*
Block::factory()
->title('Quick Shortcuts')
->title_icon('icon-bookmark')
->span(3)
->body(View::factory('welcome/user/shortcuts'));
*/
} }
} }
?> ?>

View File

@ -0,0 +1,72 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class overrides Kohana's Core
*
* @package Membership Database
* @category Modifications
* @author Deon George
* @copyright (c) 2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class Kohana extends Kohana_Core {
/**
* Work out our Class Name as per Kohana's standards
*/
public static function classname($name) {
return str_replace(' ','_',ucwords(strtolower(str_replace('_',' ',$name))));
}
/**
* Find files using a multi-site enabled application
*
* In order of precedence, we'll return:
* 1) site-theme file, ie: site/X/THEME/${file}
* 2) site file, ie: site/X/${file}
* 3) theme file, ie: THEME/${file}
* 4) normal search, ie: ${file}
*/
public static function find_file($dir,$file,$ext=NULL,$array=FALSE) {
// Limit our scope to the following dirs
// @note, we cannot have classes checked, since Config() doesnt exist yet
$dirs = array('views','media');
if (! in_array($dir,$dirs) OR PHP_SAPI === 'cli')
return parent::find_file($dir,$file,$ext,$array);
$prefixes = array('');
// Our search order.
array_unshift($prefixes,Site::Theme().'/');
array_unshift($prefixes,sprintf('site/%s/',Site::ID()));
array_unshift($prefixes,sprintf('site/%s/%s/',Site::ID(),Site::Theme()));
foreach ($prefixes as $p)
if ($x = parent::find_file($dir,$p.$file,$ext,$array))
break;
// We found a path.
return $x;
}
/**
* Override Kohana's shutdown_handler()
*
* When set up for multi-site, we need to disable Kohana's caching
* unless each site has exactly the same modules enabled.
* This is because Kohana::$file is cached with the enabled modules
* and is not OSB multi-site aware.
*/
public static function shutdown_handler() {
// If caching isnt enabled, we can skip this anyway
if (! Kohana::$caching)
return parent::shutdown_handler();
Kohana::$caching = FALSE;
$result = parent::shutdown_handler();
Kohana::$caching = TRUE;
return $result;
}
}
?>

View File

@ -20,5 +20,9 @@ class Model_Account extends lnApp_Model_Account {
return strlen($this->prefix) > 1 ? $this->prefix : sprintf('%s%06d',$this->prefix,$this->id); return strlen($this->prefix) > 1 ? $this->prefix : sprintf('%s%06d',$this->prefix,$this->id);
} }
public function ssl_dn() {
return sprintf('O=IBM,CN=%s',$this->id());
}
} }
?> ?>

View File

@ -0,0 +1,25 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* SSL Model
*
* @package TSM Access Management
* @category Models
* @author Deon George
* @copyright (c) 2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Model_SSL extends lnApp_Model_SSL {
public function rules() {
return Arr::merge(parent::rules(),array(
'csr'=>array(
array(array($this,'isValidDN')),
),
));
}
public function isValidDN() {
return ! $this->isCA() AND ($this->account->ssl_dn() == self::_dn(openssl_csr_get_subject($this->csr)));
}
}
?>

View File

@ -3,6 +3,6 @@
return array return array
( (
'appname' => 'TSM Access Request', 'appname' => 'TSM Access Request',
'theme' => 'bootstrap', 'theme' => 'focusbusiness',
'theme_admin' => 'baseadmin', 'theme_admin' => 'baseadmin',
); );

View File

@ -0,0 +1,40 @@
#header {
padding: 25px 0;
}
#header h1 {
width: 300px;
height: 80px;
background-size: 40% 80%;
line-height: 120px;
padding-left: 8px;
font-size: 12px;
}
#header h1 a {
color: #000;
}
#header h1 a:hover {
text-decoration: none;
}
#header h1 a sup {
color: #F90;
padding: 5px;
font-size: 10px;
}
#footer #footer-logo {
height: 65px;
}
.modal-dialog {
height: 400px;
}
#wrapper {
width: 980px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,18 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* SSL Certificate validation messages.
*
* @package SSL
* @category Validation
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
return array(
'csr'=>array(
'isValidDN'=>'This Certificate Sign Request does not contact your valid DN',
),
);
?>

View File

@ -1,11 +1,71 @@
<!-- @todo Move this content into the DB --> <!-- @todo Move this content into the DB -->
<div id="container"> <div id="container">
<div class="row">
<div id="welcome" class="grid-12">
<h1>TSM Server Access Request.</h1>
</div>
</div> <!-- /row -->
<hr class="row-divider" /> <hr class="row-divider" />
<div class="col-md-6"> <div class="row divider service-container">
<h3><i class="fa fa-edit"></i>Request Access</h3> <div class="grid-3">
<p>To be able to use this server, you need to request access. Once your access is activated, you will be able to use this TSM server with your TSM client.</p> <h2><span class="slash">//</span> This Service</h2>
<p><a href="<?php echo URL::site('login'); ?>" class="">Login »</a></p> </div>
</div>
<div class="grid-3">
<div class="service-item">
<h3><i class="fa fa-recycle"></i>Node Access</h3>
<p>This TSM server is available for you to connect with any TSM client and backup data. You do not need to worry about the setup of a TSM server (we've done already)!</p>
<p>When connecting to this server, you can try out a TSM client and see how you can protect your application or data, and how you can recover it.</p>
<p>Request as many NODE IDs as you need, it's easy, first just register on the site, and then drop an email to dgeorge AT au DOT ibm DOT com, and tell us what you need. We'll reply with the details.</p>
<p><a href="<?php echo URL::site('login'); ?>" class="">Login »</a></p>
</div> <!-- /service -->
</div>
<div class="grid-3">
<div class="service-item">
<h3><i class="fa fa-shield"></i>Admin Access</h3>
<p>If you would like to have ADMINISTRATOR access to this server, you can request that as well. With ADMIN access, you can experience running some commands on our TSM server, as well as use the TSM server HELP.</p>
<p>Admin access is via our Operations Center by default, however, if you want to try out admin access with an admin client, you'll need to request an SSL certificate.</p>
<p><a href="<?php echo URL::link('user','ssl/add'); ?>" class="">Request SSL »</a></p>
</div> <!-- /service -->
</div>
<div class="grid-3">
<div class="service-item">
<h3><i class="fa fa-bolt"></i>Want some guidance?</h3>
<p>If you would like help with any of our clients, then please contact us and we'll be happy to help.</p>
<p>Send an email to dgeorge AT au DOT ibm DOT com, and we'll find somebody in your time zone to help you.</p>
<p><a href="<?php echo URL::site('login'); ?>" class="">Login »</a></p>
</div> <!-- /service -->
</div>
</div> <!-- /row -->
<!--
<hr class="row-divider" />
<div class="row divider about-container">
<div class="grid-3">
<h2><span class="slash">//</span> Our Story</h2>
</div>
<div class="grid-4">
<div class="about-item">
<h3>About Us</h3>
<p>!</p>
</div> <!-- /about --/
</div> <!-- /grid-4 --/
<div class="grid-5">
<h3>Why Choose Us</h3>
<div class="choose-item">
<h3><i class="glyphicon glyphicon-star"></i> Some info</h3>
<p>Some info.</p>
</div> <!-- /choose-item --/
</div> <!-- /grid-5 --/
</div> <!-- /row --/
-->
</div> <!-- /container --> </div> <!-- /container -->

View File

@ -16,10 +16,11 @@
<code>gsk8capicmd_64 -keydb -create -db dsmcert.kdb -type kdb -stash</code><br/><br/> <code>gsk8capicmd_64 -keydb -create -db dsmcert.kdb -type kdb -stash</code><br/><br/>
</li> </li>
<li>Create a Certificate Signing Request using the following command:<br/> <li>Create a Certificate Signing Request using the following command:<br/>
<code>gsk8capicmd_64 -certreq -create -db dsmcert.kdb -stashed -label 'TSM-SL01' -dn 'O=IBM,cn=<?php echo $o->id(); ?>' -size 2048 -file <?php echo $o->id(); ?>.CSR</code><br/><br/> <code>gsk8capicmd_64 -certreq -create -db dsmcert.kdb -stashed -label 'TSM-SL01' -dn '<?php echo $o->ssl_dn(); ?>' -size 2048 -file <?php echo $o->id(); ?>.CSR</code><br/><br/>
</li> </li>
<li>Paste the contents of your CSR file here:<br/> <li>Upload your CSR file here <?php echo Form::file('csr_file',array('class'=>'col-md-3','label'=>'CSR File')); ?>
OR, paste the contents of your CSR file here:<br/>
<?php echo Form::textarea('csr','',array('class'=>'col-md-6','label'=>'CSR','placeholder'=>'Certificate Sign Request','style'=>'font-family: monospace;','cols'=>61,'rows'=>15)); ?> <?php echo Form::textarea('csr','',array('class'=>'col-md-6','label'=>'CSR','placeholder'=>'Certificate Sign Request','style'=>'font-family: monospace;','cols'=>61,'rows'=>15)); ?>
</li> </li>
@ -31,5 +32,5 @@
<div class="col-md-offset-1"> <div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Save changes</button> <button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-default">Cancel</button> <button type="button" class="btn btn-default">Cancel</button>
</div> </div>
</div> </div>

View File

@ -0,0 +1,110 @@
<fieldset class="col-md-6">
<legend>SSL Details</legend>
<div class="dl-horizontal">
<dt>Subject</dt>
<dd><?php echo $o->dn(); ?></dd>
<?php if ($o->cert) : ?>
<dt>Subject Key ID</dt>
<dd><?php echo $o->ski(); ?></dd>
<dt>Serial Number</dt>
<dd><?php echo $o->serial(); ?></dd>
<dt>Issuer</dt>
<dd>
<?php if ($o->validCA()) : ?>
<?php echo HTML::anchor(URL::link('admin','ssl/edit/').$o->ca->id,$o->issuer()); ?>
<?php else : ?>
<?php echo $o->issuer(); ?>
<?php endif ?>
</dd>
<dt>Issuer Key ID</dt>
<dd><?php echo $o->aki_keyid(); ?></dd>
<dt>Issuer Serial</dt>
<dd><?php echo $o->aki_serial(); ?></dd>
<dt>Valid From</dt>
<dd><?php echo $o->valid_from(TRUE); ?></dd>
<dt>Valid To</dt>
<dd><?php echo $o->valid_to(TRUE); ?></dd>
<dt>Hash</dt>
<dd><?php echo $o->hash(); ?></dd>
<dt>Version</dt>
<dd><?php echo $o->version(); ?></dd>
<dt>Algorithm</dt>
<dd><?php echo $o->algorithm(); ?></dd>
<?php else : ?>
<dt>Status</dt>
<dd>Waiting to be signed.</dd>
<?php endif ?>
</div> <!-- dl-horizontal -->
<?php if ($o->cert) : ?>
<br/>
<legend>Certificate Chain</legend>
<?php echo Table::factory()
->data($o->list_ca())
->columns(array(
'id'=>'ID',
'subject_cn()'=>'Cert',
'valid_to(TRUE)'=>'Expires',
'issuer_cn()'=>'Issuer',
))
->prepend(array(
'id'=>array('url'=>URL::link('','ssl/download/')),
)); ?>
<?php endif ?>
</fieldset>
<fieldset class="col-md-6">
<legend>Certificate</legend>
<pre><?php echo $o->cert ? $o->cert : $o->csr; ?></pre>
<?php
echo $o->download_button();
if ($ao=Auth::instance()->get_user() AND ($ao->isAdmin()) AND $o->service->status AND ($o->valid_to()-(Kohana::$config->load('ssl.min_renew_days')*86400) <= time()) AND $o->service->paid_to() > time()) :
echo Form::open(URL::link('admin','ssl/renew/'.$o->service->id));
echo Form::button('submit','Renew',array('class'=>'btn btn-primary'));
endif
?>
</fieldset>
<fieldset class="col-md-12">
<legend>TSM Configuration</legend>
<p>To use this certificate with a Tivoli Storage Manager client, please do the following:<p>
<p>(If this certificate has just been renewed, you only need to jump to the last step.)<p>
<ol>
<li>Download this signed certificate and the CA certificates above.</li>
<li>Open up a command prompt, and depending on your operating system, change to your BA Client <strong>BIN</strong> directory. For example:<br/>
<dl class="dl-horizontal">
<dt>Linux</dt>
<dd>cd /opt/tivoli/tsm/client/ba/bin</dd>
<dt>Windows</dt>
<dd>cd "C:\Program Files\Tivoli\TSM\baclient"</dd>
</dl>
</li>
<li>Import the ROOT SSL certificate above with the following command:<br/>
<code>gsk8capicmd_64 -cert -add -db dsmcert.kdb -stashed -file [DOWNLOAD.CRT] -label [NAME OF ROOT CERTIFICATE]</code><br/><br/>
</li>
<li>Import any additional CA certificates above with the following command:<br/>
<code>gsk8capicmd_64 -cert -add -db dsmcert.kdb -stashed -file [DOWNLOAD.CRT] -label [NAME OF CERTIFICATE]</code><br/><br/>
</li>
<li>Import this signed certificate with the following command:<br/>
<code>gsk8capicmd_64 -cert -receive -db dsmcert.kdb -stashed -default_cert enabled -file [DOWNLOAD.CRT]</code><br/><br/>
</li>
</ol>
</fieldset>

View File

@ -0,0 +1,5 @@
<div class="shortcuts">
<a href="<?php echo URL::link('user','node/add',TRUE); ?>" class="shortcut"><i class="shortcut-icon fa fa-recycle"></i><span class="shortcut-label">Create Node</span></a>
<a href="<?php echo URL::link('user','ssl/add',TRUE); ?>" class="shortcut"><i class="shortcut-icon fa fa-certificate"></i><span class="shortcut-label">Create SSL</span></a>
<a href="<?php echo URL::link('user','admin/add',TRUE); ?>" class="shortcut"><i class="shortcut-icon fa fa-graduation-cap"></i><span class="shortcut-label">Create Admin</span></a>
</div>

@ -1 +1 @@
Subproject commit a7ed6672e18773ff07eb1c01fcf8e38b4828de11 Subproject commit 9302b51ebb18a638a8ed1f87643d1d04e1e27649

View File

@ -9,7 +9,7 @@
* @copyright (c) 2009-2014 Deon George * @copyright (c) 2009-2014 Deon George
* @license http://dev.leenooks.net/license.html * @license http://dev.leenooks.net/license.html
*/ */
class Controller_Admin_SSL extends Controller_SSL { class Controller_Admin_Ssl extends Controller_Ssl {
protected $auth_required = TRUE; protected $auth_required = TRUE;
protected $secure_actions = array( protected $secure_actions = array(

View File

@ -1,14 +0,0 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides SSL management
*
* @package SSL
* @category Controllers
* @author Deon George
* @copyright (c) 2009-2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_SSL extends Controller_TemplateDefault {
}
?>

View File

@ -0,0 +1,27 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides SSL management
*
* @package SSL
* @category Controllers
* @author Deon George
* @copyright (c) 2009-2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_Ssl extends Controller_TemplateDefault {
protected $auth_required = FALSE;
public function action_download() {
$so = ORM::factory('SSL_CA')->where('id','=',$this->request->param('id'))->or_where('name','=',$this->request->param('id'))->find();
if (! $so->loaded() OR ! $so->cert)
throw HTTP_Exception::factory(404,'SSL either doesnt exist');
$this->auto_render = FALSE;
$this->response->headers('Content-Type','plain/text');
$this->response->headers('Content-Disposition','attachment; filename="'.$so->name.'.crt"');
$this->response->body($so->cert);
}
}
?>

View File

@ -9,7 +9,7 @@
* @copyright (c) 2009-2013 Deon George * @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html * @license http://dev.leenooks.net/license.html
*/ */
class Controller_User_SSL extends Controller_SSL { class Controller_User_Ssl extends Controller_Ssl {
protected $auth_required = TRUE; protected $auth_required = TRUE;
protected $secure_actions = array( protected $secure_actions = array(
@ -19,31 +19,58 @@ class Controller_User_SSL extends Controller_SSL {
); );
public function action_add() { public function action_add() {
if ($this->request->post()) { if ($this->request->post() OR $_FILES) {
$so = ORM::factory('SSL'); if ($_FILES AND $this->request->post('csr'))
SystemMessage::add(array(
'title'=>_('Validation failed'),
'type'=>'info',
'body'=>_('Only supply a CSR file OR the CSR text, not both!'),
));
$so->account_id = (string)Auth::instance()->get_user(); else {
// Set our values, so that our filters have data $so = ORM::factory('SSL');
$so->values($this->request->post()); $so->account_id = (string)Auth::instance()->get_user();
$this->save($so); // Set our values, so that our filters have data
$so->values($this->request->post());
if ($so->saved()) if ($_FILES) {
HTTP::redirect(URL::link('user','ssl/view/'.$so->id)); // Process upload
$files = Validation::factory($_FILES)
->rule('csr_file','Upload::valid')
->rule('csr_file','Upload::not_empty')
->rule('csr_file','Upload::type',array(':value',array('csr')))
->rule('csr_file','Upload::size',array(':value','512K'));
if ($files->check())
foreach ($files->data() as $file) {
$so->csr = file_get_contents($file['tmp_name']);
break;
}
if (! $so->csr)
throw HTTP_Exception::factory(501,'No CSR data :csr_file?',$files->errors('user/ssl/add'));
}
$this->save($so);
if ($so->saved())
HTTP::redirect(URL::link('user','ssl/view/'.$so->id));
}
} }
Block::factory() Block::factory()
->type('form-horizontal') ->type('form-horizontal')
->title('Add/Edit Record') ->title('SSL Certificate')
->title_icon('fa-wrench') ->title_icon('fa-certificate')
->body(View::factory('ssl/user/add')->set('o',$this->ao)); ->body(View::factory('ssl/user/add')->set('o',$this->ao));
} }
public function action_download() { public function action_download() {
$passwd_len = Kohana::$config->load('ssl')->minpass_length; $passwd_len = Kohana::$config->load('ssl')->minpass_length;
$so = ORM::factory('SSL',$this->request->post('sid')); $so = ORM::factory('SSL',$this->request->param('id'));
if (! $so->loaded() OR ! Auth::instance()->authorised($so->account)) if (! $so->loaded() OR ! Auth::instance()->authorised($so->account))
throw HTTP_Exception::factory(403,'SSL either doesnt exist, or you are not authorised to see it'); throw HTTP_Exception::factory(403,'SSL either doesnt exist, or you are not authorised to see it');

View File

@ -1,339 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.'); <?php defined('SYSPATH') or die('No direct access allowed.');
/** class Model_SSL extends lnApp_Model_SSL {}
* This class supports SSL Certificates
*
* @package SSL
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Model_SSL extends ORM {
protected $_cert_details = NULL;
// Relationships
protected $_belongs_to = array(
'account'=>array(),
);
protected $_has_one = array(
'ca'=>array('model'=>'SSL_CA','far_key'=>'ssl_ca_id','foreign_key'=>'id'),
);
protected $_display_filters = array(
'csr'=>array(
array('Model_SSL::subject_csr',array(':value')),
),
'cert'=>array(
array('Model_SSL::subject_cert',array(':value')),
),
);
protected $_save_message = TRUE;
/**
* Parse our AuthorityKeyIndentifier Extension to extract information
* @param $key Return just that index
*/
private function _aki($key=NULL) {
$result = array();
$aki = $this->_extensions('authorityKeyIdentifier');
if (! $aki)
return '';
foreach (explode("\n",preg_replace("/\n$/",'',$aki)) as $x) {
if (! $x)
continue;
if (strstr($x,':')) {
list($a,$b) = explode(':',$x,2);
$result[strtolower($a)] = $b;
}
}
return is_null($key) ? $result : Arr::get($result,$key);
}
/**
* This function will convert a large decimal number into hex
* @param $number Large decimal number
*/
private static function _dec_to_hex($number) {
$hex = array();
if ($number == 0)
return '00';
while ($number > 0) {
if ($number == 0) {
array_push($hex, '0');
} else {
$x = (int) ($number/16);
array_push($hex,strtoupper(dechex((int)($number-($x*16)))));
$number = $x;
}
}
return preg_replace('/^:/','',preg_replace('/(..)/',":$1",implode(array_reverse($hex))));
}
/**
* Parse our Sign Certifcate to extract information
* @param $key Return just that index
*/
private function _details($key=NULL) {
if (! $this->cert)
return array();
return is_null($key) ? $this->_cert_details : Arr::get($this->_cert_details,$key,array());
}
private static function _dn(array $array) {
$result = '';
$i = 0;
foreach ($array as $k=>$v) {
if ($i++)
$result .= ',';
$result .= sprintf('%s=%s',$k,$v);
}
return $result;
}
/**
* Parse our Sign Certifcate Extensions to extract information
* @param $key Return just that index
*/
protected function _extensions($key=NULL) {
$result = Arr::get($this->_cert_details,'extensions');
return is_null($key) ? $result : Arr::get($result,$key);
}
// We want to inject the SSL object into this Model
protected function _load_values(array $values) {
parent::_load_values($values);
$this->_cert_details = openssl_x509_parse($this->cert);
return $this;
}
public function aki_dirname() {
return $this->_aki('dirname');
}
public function aki_keyid() {
return $this->_aki('keyid');
}
public function aki_serial() {
return $this->_aki('serial');
}
public function algorithm() {
if (! $this->cert)
return NULL;
$e = '';
openssl_x509_export(openssl_x509_read($this->cert),$e,FALSE);
// @todo There must be a nice way to get this?
return (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match)) ? $match[1] : _('Unknown');
}
public function download_button() {
if ($this->valid_to() < time())
return '';
$passwd_len = Kohana::$config->load('ssl')->minpass_length;
$output = Form::open(URL::link('user','ssl/download'),array('class'=>'form-inline','data-toggle'=>'validator','role'=>'form'));
$output .= Form::hidden('sid',$this->id);
if ($passwd_len) {
$output .= '<div class="form-group">';
$output .= '<div class="input-group">';
$output .= Form::password('passwd','',array('class'=>'form-control','placeholder'=>_('Choose a password'),'nocg'=>TRUE,'pattern'=>'.{'.$passwd_len.',}','data-error'=>"Minimum ${passwd_len} characters",'required'));
$output .= '<span class="input-group-btn">';
}
$output .= Form::button('download','Download',array('class'=>'btn btn-default','nocg'=>TRUE));
if ($passwd_len) {
$output .= '</span>';
$output .= '</div>';
$output .= '<div class="help-block with-errors"></div>';
$output .= '</div>';
}
$output .= Form::close();
return $output;
}
public function dn() {
return $this->display($this->signed() ? 'cert' : 'csr');
}
public function hash() {
return Arr::get($this->_cert_details,'hash');
}
public function issuer() {
return self::_dn(Arr::get($this->_cert_details,'issuer',array()));
}
public function serial() {
return $this->_dec_to_hex(Arr::get($this->_cert_details,'serialNumber'));
}
/**
* (Re)Sign an SSL Certificate
*/
public function sign($force=FALSE) {
$ssl_config = Kohana::$config->load('ssl');
$ssl_conf = Kohana::find_file('config',$ssl_config->config,'');
$ssl_conf = array_pop($ssl_conf);
// If our certificate is not old enough skip
if ($this->valid_to() > time()+$ssl_config->min_renew_days*86400 AND ! $force)
return FALSE;
$today = mktime(0,0,0,date('n'),date('j'),date('Y'));
$days = (int)$this->account->renew;
if (! $this->ca->pk)
throw HTTP_Exception::factory(400,'Unable to sign, missing Private Key for CA :ca',array(':ca'=>$this->ca->subject_cn()));
$res = openssl_csr_sign($this->csr,$this->ca->cert,$this->ca->pk,$days,array(
'config'=>$ssl_conf,
'x509_extensions'=>'client',
'digest_alg'=>'sha1',
),time());
if ($res AND openssl_x509_export($res,$cert)) {
$this->cert = $cert;
$this->save();
return TRUE;
} else {
/*
echo Debug::vars(array(
'csr'=>$this->csr,
'ca'=>$this->ca->cert,
'caid'=>$this->ca->id,
'days'=>$days,
'ssl'=>$ssl_conf,
'x509e'=>'client',
// 'command'=>sprintf('openssl ca -days %s -cert /tmp/ssl_ca.crt -keyfile /tmp/ssl_ca.key -out /tmp/ssl.crt -in /tmp/ssl.csr -extensions %s -config %s',$days,'client',$ssl_conf),
));
file_put_contents('/tmp/ssl.csr',$this->csr);
file_put_contents('/tmp/ssl_ca.key',$this->ca->pk);
file_put_contents('/tmp/ssl_ca.crt',$this->ca->cert);
*/
throw HTTP_Exception::factory(501,'Error Creating SSL Certificate :error',array(':error'=>openssl_error_string()));
}
}
private function signed() {
return $this->_cert_details ? TRUE : FALSE;
}
public function ski() {
return $this->_extensions('subjectKeyIdentifier');
}
public function issuer_cn() {
return Arr::get($this->cert ? Arr::get($this->_cert_details,'issuer') : array(),'CN');
}
public function subject_cn() {
return $this->cert ? Arr::get(Arr::get($this->_cert_details,'subject'),'CN') : self::subject_csr($this->csr);
}
public static function subject_cert($cert) {
try {
return self::_dn(Arr::get(openssl_x509_parse($cert),'subject'));
} catch (exception $e) {
return 'Invalid Cert';
}
}
public static function subject_csr($csr) {
try {
return self::_dn(openssl_csr_get_subject($csr));
} catch (exception $e) {
return 'Invalid CSR';
}
}
public function validCA($format=FALSE) {
return StaticList_YesNo::get(($this->_cert_details AND $this->ssl_ca_id AND $this->aki_keyid()==$this->ca->ski()) ? $this->ca->validParent() : FALSE,$format);
}
public function valid_from($format=FALSE) {
$k = Arr::get($this->_cert_details,'validFrom_time_t');
if (! $k)
return NULL;
return $format ? Site::Date($k) : $k;
}
public function valid_to($format=FALSE) {
$k = Arr::get($this->_cert_details,'validTo_time_t');
if (! $k)
return NULL;
return $format ? Site::Date($k) : $k;
}
// If we change the SSL certificate, we need to reload our SSL object
public function values(array $values, array $expected = NULL) {
parent::values($values,$expected);
if (array_key_exists('cert',$this->_changed))
$this->_cert_details = openssl_x509_parse($this->cert);
return $this;
}
public function version() {
return Arr::get($this->_cert_details,'version');
}
public function list_ca() {
$result = array();
if (! $this->validCA())
return $result;
$x = $this;
while (! is_null($x->ssl_ca_id) AND $x->id != $x->ssl_ca_id AND $x=$x->ca)
array_push($result,$x);
return $result;
}
/**
* Return all our CA Certs for this certificate
*/
public function list_ca_crts() {
$result = array();
foreach ($this->list_ca() as $so)
array_push($result,$so->cert);
return $result;
}
}
?> ?>

View File

@ -0,0 +1,350 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports SSL Certificates
*
* @package SSL
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class lnApp_Model_SSL extends ORM {
protected $_cert_details = NULL;
// Relationships
protected $_belongs_to = array(
'account'=>array(),
);
protected $_has_one = array(
'ca'=>array('model'=>'SSL_CA','far_key'=>'ssl_ca_id','foreign_key'=>'id'),
);
protected $_display_filters = array(
'csr'=>array(
array('Model_SSL::subject_csr',array(':value')),
),
'cert'=>array(
array('Model_SSL::subject_cert',array(':value')),
),
);
protected $_save_message = TRUE;
public function rules() {
return Arr::merge(parent::rules(),array(
'csr'=>array(
array(array($this,'isCSR')),
),
));
}
/**
* Parse our AuthorityKeyIndentifier Extension to extract information
* @param $key Return just that index
*/
protected function _aki($key=NULL) {
$result = array();
$aki = $this->_extensions('authorityKeyIdentifier');
if (! $aki)
return '';
foreach (explode("\n",preg_replace("/\n$/",'',$aki)) as $x) {
if (! $x)
continue;
if (strstr($x,':')) {
list($a,$b) = explode(':',$x,2);
$result[strtolower($a)] = $b;
}
}
return is_null($key) ? $result : Arr::get($result,$key);
}
/**
* This function will convert a large decimal number into hex
* @param $number Large decimal number
*/
protected static function _dec_to_hex($number) {
$hex = array();
if ($number == 0)
return '00';
while ($number > 0) {
if ($number == 0) {
array_push($hex, '0');
} else {
$x = (int) ($number/16);
array_push($hex,strtoupper(dechex((int)($number-($x*16)))));
$number = $x;
}
}
return preg_replace('/^:/','',preg_replace('/(..)/',":$1",implode(array_reverse($hex))));
}
/**
* Parse our Sign Certifcate to extract information
* @param $key Return just that index
*/
protected function _details($key=NULL) {
if (! $this->cert)
return array();
return is_null($key) ? $this->_cert_details : Arr::get($this->_cert_details,$key,array());
}
protected static function _dn(array $array) {
$result = '';
$i = 0;
foreach ($array as $k=>$v) {
if ($i++)
$result .= ',';
$result .= sprintf('%s=%s',$k,$v);
}
return $result;
}
/**
* Parse our Sign Certifcate Extensions to extract information
* @param $key Return just that index
*/
protected function _extensions($key=NULL) {
$result = Arr::get($this->_cert_details,'extensions');
return is_null($key) ? $result : Arr::get($result,$key);
}
// We want to inject the SSL object into this Model
protected function _load_values(array $values) {
parent::_load_values($values);
$this->_cert_details = openssl_x509_parse($this->cert);
return $this;
}
public function aki_dirname() {
return $this->_aki('dirname');
}
public function aki_keyid() {
return $this->_aki('keyid');
}
public function aki_serial() {
return $this->_aki('serial');
}
public function algorithm() {
if (! $this->cert)
return NULL;
$e = '';
openssl_x509_export(openssl_x509_read($this->cert),$e,FALSE);
// @todo There must be a nice way to get this?
return (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m',$e,$match)) ? $match[1] : _('Unknown');
}
public function download_button() {
if ($this->valid_to() < time())
return '';
$passwd_len = Kohana::$config->load('ssl')->minpass_length;
$output = Form::open(URL::link('user','ssl/download/'.$this->id),array('class'=>'form-inline','data-toggle'=>'validator','role'=>'form'));
if ($passwd_len) {
$output .= '<div class="form-group">';
$output .= '<div class="input-group">';
$output .= Form::password('passwd','',array('class'=>'form-control','placeholder'=>_('Choose a password'),'nocg'=>TRUE,'pattern'=>'.{'.$passwd_len.',}','data-error'=>"Minimum ${passwd_len} characters",'required'));
$output .= '<span class="input-group-btn">';
}
$output .= Form::button('download','Download',array('class'=>'btn btn-default','nocg'=>TRUE));
if ($passwd_len) {
$output .= '</span>';
$output .= '</div>';
$output .= '<div class="help-block with-errors"></div>';
$output .= '</div>';
}
$output .= Form::close();
return $output;
}
public function dn() {
return $this->display($this->signed() ? 'cert' : 'csr');
}
public function hash() {
return Arr::get($this->_cert_details,'hash');
}
public function isCSR() {
return openssl_csr_get_subject($this->csr);
}
public function issuer() {
return self::_dn(Arr::get($this->_cert_details,'issuer',array()));
}
public function serial() {
return $this->_dec_to_hex(Arr::get($this->_cert_details,'serialNumber'));
}
/**
* (Re)Sign an SSL Certificate
*/
public function sign($force=FALSE) {
$ssl_config = Kohana::$config->load('ssl');
$ssl_conf = Kohana::find_file('config',$ssl_config->config,'');
$ssl_conf = array_pop($ssl_conf);
// If our certificate is not old enough skip
if ($this->valid_to() > time()+$ssl_config->min_renew_days*86400 AND ! $force)
return FALSE;
$today = mktime(0,0,0,date('n'),date('j'),date('Y'));
$days = (int)$this->account->renew;
if (! $this->ca->pk)
throw HTTP_Exception::factory(400,'Unable to sign, missing Private Key for CA :ca',array(':ca'=>$this->ca->subject_cn()));
$res = openssl_csr_sign($this->csr,$this->ca->cert,$this->ca->pk,$days,array(
'config'=>$ssl_conf,
'x509_extensions'=>'client',
'digest_alg'=>'sha1',
),time());
if ($res AND openssl_x509_export($res,$cert)) {
$this->cert = $cert;
$this->save();
return TRUE;
} else {
/*
echo Debug::vars(array(
'csr'=>$this->csr,
'ca'=>$this->ca->cert,
'caid'=>$this->ca->id,
'days'=>$days,
'ssl'=>$ssl_conf,
'x509e'=>'client',
// 'command'=>sprintf('openssl ca -days %s -cert /tmp/ssl_ca.crt -keyfile /tmp/ssl_ca.key -out /tmp/ssl.crt -in /tmp/ssl.csr -extensions %s -config %s',$days,'client',$ssl_conf),
));
file_put_contents('/tmp/ssl.csr',$this->csr);
file_put_contents('/tmp/ssl_ca.key',$this->ca->pk);
file_put_contents('/tmp/ssl_ca.crt',$this->ca->cert);
*/
throw HTTP_Exception::factory(501,'Error Creating SSL Certificate :error',array(':error'=>openssl_error_string()));
}
}
protected function signed() {
return $this->_cert_details ? TRUE : FALSE;
}
public function ski() {
return $this->_extensions('subjectKeyIdentifier');
}
public function issuer_cn() {
return Arr::get($this->cert ? Arr::get($this->_cert_details,'issuer') : array(),'CN');
}
public function subject_cn() {
return Arr::get($this->cert ? Arr::get($this->_cert_details,'subject') : openssl_csr_get_subject($this->csr),'CN');
}
public static function subject_cert($cert) {
try {
return self::_dn(Arr::get(openssl_x509_parse($cert),'subject'));
} catch (exception $e) {
return 'Invalid Cert';
}
}
public static function subject_csr($csr) {
try {
return self::_dn(openssl_csr_get_subject($csr));
} catch (exception $e) {
return 'Invalid CSR';
}
}
public function validCA($format=FALSE) {
return StaticList_YesNo::get(($this->_cert_details AND $this->ssl_ca_id AND $this->aki_keyid()==$this->ca->ski()) ? $this->ca->validParent() : FALSE,$format);
}
public function valid_from($format=FALSE) {
$k = Arr::get($this->_cert_details,'validFrom_time_t');
if (! $k)
return NULL;
return $format ? Site::Date($k) : $k;
}
public function valid_to($format=FALSE) {
$k = Arr::get($this->_cert_details,'validTo_time_t');
if (! $k)
return NULL;
return $format ? Site::Date($k) : $k;
}
// If we change the SSL certificate, we need to reload our SSL object
public function values(array $values, array $expected = NULL) {
parent::values($values,$expected);
if (array_key_exists('cert',$this->_changed))
$this->_cert_details = openssl_x509_parse($this->cert);
return $this;
}
public function version() {
return Arr::get($this->_cert_details,'version');
}
public function list_ca() {
$result = array();
if (! $this->validCA())
return $result;
$x = $this;
while (! is_null($x->ssl_ca_id) AND $x->id != $x->ssl_ca_id AND $x=$x->ca)
array_push($result,$x);
return $result;
}
/**
* Return all our CA Certs for this certificate
*/
public function list_ca_crts() {
$result = array();
foreach ($this->list_ca() as $so)
array_push($result,$so->cert);
return $result;
}
}
?>

View File

@ -0,0 +1,18 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* SSL Certificate validation messages.
*
* @package SSL
* @category Validation
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
return array(
'csr'=>array(
'isCSR'=>'This is not a valid Certificate Sign Request',
),
);
?>

View File

@ -0,0 +1,18 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* CSR validation messages.
*
* @package SSL
* @category Validation
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
return array(
'csr_file'=>array(
'Upload::type'=>'Incorrect upload type, it must be CSR',
),
);
?>

View File

@ -69,6 +69,8 @@
<legend>Private Key Data</legend> <legend>Private Key Data</legend>
<?php echo Form::textarea('pk',$o->pk,array('divclass'=>'col-md-12','placeholder'=>'Private Key','style'=>'font-family: monospace; font-size: 95%;','rows'=>Form::textarea_rows($o->pk))); ?> <?php echo Form::textarea('pk',$o->pk,array('divclass'=>'col-md-12','placeholder'=>'Private Key','style'=>'font-family: monospace; font-size: 95%;','rows'=>Form::textarea_rows($o->pk))); ?>
<?php echo Form::input('name',$o->name,array('divclass'=>'col-md-6','label'=>'Name','placeholder'=>'Label')); ?>
</fieldset> </fieldset>
<fieldset class="col-md-6"> <fieldset class="col-md-6">

View File

@ -4,7 +4,8 @@
<p>To use SSL with this application, you need to receive a certificate from this service.<p> <p>To use SSL with this application, you need to receive a certificate from this service.<p>
<p>Please do the following:<p> <p>Please do the following:<p>
<ol> <ol>
<li>Paste the contents of your CSR file here:<br/> <li>Upload your CSR file here <?php echo Form::file('csr_file',array('class'=>'col-md-3','label'=>'CSR File')); ?>
OR, paste the contents of your CSR file here:<br/>
<?php echo Form::textarea('csr','',array('class'=>'col-md-6','label'=>'CSR','placeholder'=>'Certificate Sign Request','style'=>'font-family: monospace;','cols'=>61,'rows'=>15)); ?> <?php echo Form::textarea('csr','',array('class'=>'col-md-6','label'=>'CSR','placeholder'=>'Certificate Sign Request','style'=>'font-family: monospace;','cols'=>61,'rows'=>15)); ?>
</li> </li>
@ -16,5 +17,5 @@
<div class="col-md-offset-1"> <div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Save changes</button> <button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-default">Cancel</button> <button type="button" class="btn btn-default">Cancel</button>
</div> </div>
</div> </div>

View File

@ -61,7 +61,7 @@
'issuer_cn()'=>'Issuer', 'issuer_cn()'=>'Issuer',
)) ))
->prepend(array( ->prepend(array(
'id'=>array('url'=>URL::link('admin','ssl/edit/')), 'id'=>array('url'=>URL::link('','ssl/download/')),
)); ?> )); ?>
<?php endif ?> <?php endif ?>
</fieldset> </fieldset>
@ -79,9 +79,4 @@
echo Form::button('submit','Renew',array('class'=>'btn btn-primary')); echo Form::button('submit','Renew',array('class'=>'btn btn-primary'));
endif endif
?> ?>
<!--
<?php #echo Form::textarea('cert','',array('class'=>'col-md-6','label'=>'CSR','placeholder'=>'Certificate Sign Request','style'=>'font-family: monospace;','cols'=>61,'rows'=>15)); ?>
<button type="submit" class="btn btn-primary">Save changes</button>
-->
</fieldset> </fieldset>