Added SSL

This commit is contained in:
Deon George 2014-10-08 23:17:00 +11:00
parent 62992c1a0e
commit c952738750
17 changed files with 1411 additions and 11 deletions

View File

@ -119,6 +119,7 @@ Kohana::$config->attach(new Config_File);
* Enable modules. Modules are referenced by a relative or absolute path. * Enable modules. Modules are referenced by a relative or absolute path.
*/ */
Kohana::modules(array( Kohana::modules(array(
'ssl' => MODPATH.'ssl', // SSL Management Module
'tsm' => MODPATH.'tsm', // TSM Module 'tsm' => MODPATH.'tsm', // TSM Module
// 'lnauth' => MODPATH.'lnauth', // lnAuth Base Authentication Tools // 'lnauth' => MODPATH.'lnauth', // lnAuth Base Authentication Tools
'lnapp' => MODPATH.'lnapp', // lnApp Base Application Tools 'lnapp' => MODPATH.'lnapp', // lnApp Base Application Tools

View File

@ -12,6 +12,10 @@
class Controller_User_Welcome extends Controller_Welcome { class Controller_User_Welcome extends Controller_Welcome {
protected $auth_required = TRUE; protected $auth_required = TRUE;
protected $secure_actions = array(
'index'=>0,
);
public function action_index() { public function action_index() {
$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())
@ -31,7 +35,29 @@ class Controller_User_Welcome extends Controller_Welcome {
)); ));
Block::factory() Block::factory()
->title(sprintf('Your ADMINs in TSMs : %s',$this->ao->name())) ->title(sprintf('Your ADMINs in TSM : %s',$this->ao->name()))
->title_icon('icon-info-sign')
->span(9)
->body($output);
$n = $this->ao->ssl->find_all();
if (! $n->count())
$output = 'You have no currently SSL Certificates, would you like to '.HTML::anchor(URL::link('user','ssl/add'),'add').' one?';
else
$output = Table::factory()
->data($n)
->columns(array(
'id'=>'ID',
'dn()'=>'Cert',
'valid_to(TRUE)'=>'Expires',
'issuer_cn()'=>'Issuer',
))
->prepend(array(
'id'=>array('url'=>URL::link('user','ssl/view/')),
));
Block::factory()
->title(sprintf('Your SSL Certificates for TSM : %s',$this->ao->name()))
->title_icon('icon-info-sign') ->title_icon('icon-info-sign')
->span(9) ->span(9)
->body($output); ->body($output);
@ -54,7 +80,7 @@ class Controller_User_Welcome extends Controller_Welcome {
)); ));
Block::factory() Block::factory()
->title(sprintf('Your NODES in TSMs : %s',$this->ao->name())) ->title(sprintf('Your NODES in TSM : %s',$this->ao->name()))
->title_icon('icon-info-sign') ->title_icon('icon-info-sign')
->span(9) ->span(9)
->body($output); ->body($output);

View File

@ -0,0 +1,24 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Acconunt Model
*
* @package TSM Access Management
* @category Models
* @author Deon George
* @copyright (c) 2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Model_Account extends lnApp_Model_Account {
protected $_has_many = array(
'ssl'=>array('model'=>'SSL','far_key'=>'id','foreign_key'=>'account_id'),
);
public function id() {
if (! $this->prefix)
throw HTTP_Exception::factory(501,'Your prefix is missing, please contact an admin');
return strlen($this->prefix) > 1 ? $this->prefix : sprintf('%s%06d',$this->prefix,$this->id);
}
}
?>

View File

@ -0,0 +1,34 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class overrides Kohana's ORM
*
* This file contains enhancements for Kohana, that should be considered upstream and maybe havent been yet.
* It also contains some functionality for OSB, which cannot be covered in ORM_OSB.
*
* @package TSM Access Management
* @category Modifications
* @author Deon George
* @copyright (c) 2009-2013 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
abstract class ORM extends lnApp_ORM {
/**
* Function help to find records that are active
*/
protected function _where_active() {
return $this->where('status','=',TRUE);
}
/**
* Function help to find records that are active
*/
public function list_active() {
return $this->_where_active()->find_all();
}
public function where_active() {
return $this->_where_active();
}
}
?>

View File

@ -1,9 +1,9 @@
<fieldset> <fieldset>
<legend>Account Details</legend> <legend>Account Details</legend>
<?php echo Form::input('email',$o->display('email'),array('label'=>'Email','class'=>'col-md-3','placeholder'=>'Email Address','type'=>'email','required','data-error'=>'Invalid EMAIL address')); ?> <?php echo Form::input('email',$o->display('email'),array('label'=>'Email','divclass'=>'col-md-3','placeholder'=>'Email Address','type'=>'email','required','data-error'=>'Invalid EMAIL address')); ?>
<?php echo Form::input('company',$o->display('company'),array('label'=>'Company','class'=>'col-md-3','placeholder'=>'Company Name','required')); ?> <?php echo Form::input('company',$o->display('company'),array('label'=>'Company','divclass'=>'col-md-3','placeholder'=>'Company Name','required')); ?>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="Title">Name</label> <label class="col-md-2 control-label" for="Title">Name</label>
@ -12,33 +12,33 @@
<?php echo Form::select('title',StaticList_Title::table(),$o->display('title'),array('class'=>'form-control','required','nocg'=>TRUE)); ?> <?php echo Form::select('title',StaticList_Title::table(),$o->display('title'),array('class'=>'form-control','required','nocg'=>TRUE)); ?>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<?php echo Form::input('first_name',$o->display('first_name'),array('class'=>'form-control col-md-2','placeholder'=>'First Name','required','nocg'=>TRUE)); ?> <?php echo Form::input('first_name',$o->display('first_name'),array('class'=>'form-control','placeholder'=>'First Name','required','nocg'=>TRUE)); ?>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<?php echo Form::input('last_name',$o->display('last_name'),array('class'=>'form-control col-md-2','placeholder'=>'Last Name','required','nocg'=>TRUE)); ?> <?php echo Form::input('last_name',$o->display('last_name'),array('class'=>'form-control','placeholder'=>'Last Name','required','nocg'=>TRUE)); ?>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="address1">Address</label> <label class="col-md-2 control-label" for="address1">Address</label>
<?php echo Form::input('address1',$o->display('address1'),array('class'=>'col-md-6','placeholder'=>'Address Line 1','required')); ?> <?php echo Form::input('address1',$o->display('address1'),array('divclass'=>'col-md-6','placeholder'=>'Address Line 1','required')); ?>
<label class="col-md-2 control-label" for="address2"></label> <label class="col-md-2 control-label" for="address2"></label>
<?php echo Form::input('address2',$o->display('address2'),array('class'=>'col-md-6','placeholder'=>'Address Line 2')); ?> <?php echo Form::input('address2',$o->display('address2'),array('divclass'=>'col-md-6','placeholder'=>'Address Line 2')); ?>
<label class="col-md-2 control-label" for="city"></label> <label class="col-md-2 control-label" for="city"></label>
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<?php echo Form::input('city',$o->display('city'),array('label'=>'City','placeholder'=>'City','required','nocg'=>TRUE)); ?> <?php echo Form::input('city',$o->display('city'),array('class'=>'form-control','label'=>'City','placeholder'=>'City','required','nocg'=>TRUE)); ?>
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<?php echo Form::input('state',$o->display('state'),array('label'=>'','class'=>'input-mini','placeholder'=>'State','required','nocg'=>TRUE)); ?> <?php echo Form::input('state',$o->display('state'),array('class'=>'form-control','label'=>'','class'=>'input-mini','placeholder'=>'State','required','nocg'=>TRUE)); ?>
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<?php echo Form::input('zip',$o->display('zip'),array('label'=>'','class'=>'input-mini','placeholder'=>'Post Code','required','nocg'=>TRUE)); ?> <?php echo Form::input('zip',$o->display('zip'),array('class'=>'form-control','label'=>'','class'=>'input-mini','placeholder'=>'Post Code','required','nocg'=>TRUE)); ?>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,35 @@
<fieldset>
<legend>SSL Certificate Details</legend>
<p>To use SSL with a Tivoli Storage Manager Client, you need to receive a certificate from this service.<p>
<p>Please do the following:<p>
<ol>
<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>Create your SSL Certificate Store <strong>dsmsert.kdb</strong> with the following command:<br/>
<code>gsk8capicmd_64 -keydb -create -db dsmcert.kdb -type kdb -stash</code><br/><br/>
</li>
<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/>
</li>
<li>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)); ?>
</li>
<li>Submit your CSR to be signed, you'll receive a certificate once approved</li>
</ol>
</fieldset>
<div class="row">
<div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-default">Cancel</button>
</div>
</div>

View File

@ -0,0 +1,149 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides Reseller SSL functions
*
* @package SSL
* @category Controllers/Admin
* @author Deon George
* @copyright (c) 2009-2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_Admin_SSL extends Controller_SSL {
protected $auth_required = TRUE;
protected $secure_actions = array(
'add'=>3,
'edit'=>3,
'list'=>3,
'listchildca'=>3,
'listchildcrt'=>3,
'renew'=>3,
);
public function action_add() {
Block::factory()
->type('form-horizontal')
->title('Add/View SSL CA')
->title_icon('icon-wrench')
->body($this->add_edit());
}
public function action_edit() {
list($id,$output) = Table::page(__METHOD__);
Block::factory()
->type('form-horizontal')
->title(sprintf('%s: %s',_('Add/View SSL CA'),$id))
->title_icon('icon-wrench')
->body($this->add_edit($id,$output));
}
public function action_list() {
Block::factory()
->title('SSL CA Certificates')
->title_icon('icon-th-list')
->body(Table::factory()
->data(ORM::factory('SSL_CA')->find_all())
->columns(array(
'id'=>'ID',
'subject_cn()'=>'Cert',
'valid_to(TRUE)'=>'Expires',
'validParent(TRUE)'=>'Valid',
'count_ca_child(FALSE)'=>'cCA',
'count_ssl_child(FALSE)'=>'Crts',
'issuer()'=>'Issuer',
))
->prepend(array(
'id'=>array('url'=>URL::link('admin','ssl/edit/')),
))
);
}
public function action_listchildca() {
list($id,$output) = Table::page(__METHOD__);
$sco = ORM::factory('SSL_CA',$id);
if ($sco->list_ca_child())
Block::factory()
->title(sprintf('SSL CA Certificates for CA: %s',$sco->dn()))
->title_icon('icon-th-list')
->body(Table::factory()
->data($sco->where_active()->list_ca_child())
->columns(array(
'id'=>'ID',
'subject_cn()'=>'Cert',
'ski()'=>'Identifier',
'valid_to(TRUE)'=>'Expires',
'validParent(TRUE)'=>'Valid',
'count_ca_child(FALSE)'=>'cCA',
'count_ssl_child(FALSE)'=>'Crts',
))
->prepend(array(
'id'=>array('url'=>URL::link('admin','ssl/edit/')),
))
);
if ($sco->list_ssl_child())
$this->action_listchildcrt();
}
public function action_listchildcrt() {
list($id,$output) = Table::page(__METHOD__);
$sco = ORM::factory('SSL_CA',$id);
Block::factory()
->title(sprintf('SSL Certificates for CA: %s',$sco->dn()))
->title_icon('icon-th-list')
->body(Table::factory()
->jssort('crt')
->data($sco->list_ssl_child())
->columns(array(
'id'=>'ID',
'subject_cn()'=>'Cert',
'ski()'=>'Identifier',
'valid_to(TRUE)'=>'Expires',
'validCA(TRUE)'=>'Valid',
))
->prepend(array(
'id'=>array('url'=>URL::link('user','ssl/view/')),
))
);
}
public function action_renew() {
$so = ORM::factory('SSL',$this->request->param('id'));
if (! $so->loaded() OR ! Auth::instance()->authorised($so->account))
throw HTTP_Exception::factory(403,'Service either doesnt exist, or you are not authorised to see it');
$so->sign();
HTTP::redirect(URL::link('user','ssl/view/'.$so->id));
}
private function add_edit($id=NULL,$output='') {
$sco = ORM::factory('SSL_CA',$id);
if ($this->request->post()) {
if (! $sco->account_id)
$sco->account_id = (string)Auth::instance()->get_user();
// Set our values, so that our filters have data
$sco->values($this->request->post());
// To trigger our filter to get the correct parent
$sco->ssl_ca_id = -1;
if ($sco->changed() AND ! $this->save($sco))
$sco->reload();
if ($sco->saved())
HTTP::redirect(URL::link('admin','ssl/edit/'.$sco->id));
}
return View::factory('ssl/admin/add_edit')
->set('o',$sco)
->set('mode',$this->request->action());
}
}
?>

View File

@ -0,0 +1,14 @@
<?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,96 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides User SSL functions
*
* @package SSL
* @category Controllers/User
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_User_SSL extends Controller_SSL {
protected $auth_required = TRUE;
protected $secure_actions = array(
'add'=>0,
'download'=>0,
'view'=>0,
);
public function action_add() {
if ($this->request->post()) {
$so = ORM::factory('SSL');
$so->account_id = (string)Auth::instance()->get_user();
// Set our values, so that our filters have data
$so->values($this->request->post());
$this->save($so);
if ($so->saved())
HTTP::redirect(URL::link('user','ssl/view/'.$so->id));
}
Block::factory()
->type('form-horizontal')
->title('Add/Edit Record')
->title_icon('fa-wrench')
->body(View::factory('ssl/user/add')->set('o',$this->ao));
}
public function action_download() {
$passwd_len = Kohana::$config->load('ssl')->minpass_length;
$so = ORM::factory('SSL',$this->request->post('sid'));
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');
if ($passwd_len) {
$passwd = $this->request->post('passwd');
if (strlen($passwd) < $passwd_len) {
SystemMessage::add(array(
'title'=>_('Validation failed'),
'type'=>'danger',
'body'=>_('Your requested password is too short.'),
));
HTTP::redirect(URL::link('user','ssl/view/'.$so->id));
}
}
$this->auto_render = FALSE;
$this->response->headers('Content-Type','plain/text');
$this->response->headers('Content-Disposition','attachment; filename="'.$this->ao->id().'.crt"');
$this->response->body($so->cert);
}
public function action_view() {
$so = ORM::factory('SSL',$this->request->param('id'));
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');
if ($this->request->post()) {
$so->account_id = (string)Auth::instance()->get_user();
// Set our values, so that our filters have data
$so->values($this->request->post());
if ($so->changed() AND ! $this->save($so))
$so->reload();
if ($so->saved())
HTTP::redirect(URL::link('user','ssl/view/'.$so->id));
}
Block::factory()
->title('SSL Certificate')
->title_icon('fa-certificate')
->body(View::factory('ssl/user/view')->set('o',$so));
}
}
?>

View File

@ -0,0 +1,339 @@
<?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
*/
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,162 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class supports SSL
*
* @package SSL
* @category Models
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Model_SSL_CA extends Model_SSL {
// Relationships
protected $_belongs_to = array(
'parent'=>array('model'=>'ssl_ca','foreign_key'=>'ssl_ca_id'),
);
protected $_has_many = array(
'child'=>array('model'=>'ssl_ca','far_key'=>'id','foreign_key'=>'ssl_ca_id'),
'ssl'=>array('model'=>'ssl','far_key'=>'id','foreign_key'=>'ssl_ca_id'),
);
protected $_display_filters = array(
'cert'=>array(
array('Model_SSL::subject_cert',array(':value')),
),
);
public function filters() {
return array(
'ssl_ca_id'=>array(
array(array($this,'filter_getParent')),
)
);
}
public function rules() {
return Arr::merge(parent::rules(),array(
'cert'=>array(
array('not_empty'),
array(array($this,'isCert')),
array(array($this,'isCA')),
),
'ssl_ca_id'=>array(
array(array($this,'rule_parentExist')),
),
));
}
private function _bc() {
return $this->_extensions('basicConstraints');
}
public function ca_path_len() {
$m = array();
$x = preg_match('/.*pathlen:\s*([0-9]+).*$/',$this->_bc(),$m);
return isset($m[1]) ? (int)$m[1] : 0;
}
/**
* Filter to find the parent SSL_CA
*
* @notes This filter only runs when the value passed is -1
*/
public function filter_getParent() {
// This cannot be an array
if (count(func_get_args()) != 1)
return NULL;
$x = func_get_args();
$x = array_pop($x);
// This filter only runs when our value is -1
if ($x != -1)
return $x;
foreach (ORM::factory($this->_object_name)->find_all() as $so)
if ($so->ski() == $this->aki_keyid())
return $so->id;
// If we got here, we couldnt find it
return $this->isRoot() ? NULL : $x;
}
public function isCA() {
return preg_match('/CA:TRUE/',$this->_bc()) ? TRUE : FALSE;
}
public function isCert() {
return $this->_cert_details ? TRUE : FALSE;
}
public function isRoot() {
return $this->aki_keyid() == $this->ski();
}
public function rule_parentExist() {
// Our ssl_ca_id should have been populated by filter_GetParent().
return ($this->ssl_ca_id > 0) OR $this->isRoot();
}
/**
* Make sure we have our parent in the DB too
*/
public function validParent($format=FALSE) {
$result = NULL;
// If we are a root cert, we are valid
if (is_null($this->ssl_ca_id) AND $this->isRoot())
return StaticList_YesNo::get(TRUE,$format);
return StaticList_YesNo::get($this->aki_keyid() == $this->parent->ski(),$format);
}
/**
* List the child CA certs
*/
public function count_ca_child($children=FALSE) {
$result = 0;
if ($children)
foreach ($this->list_ca_child() as $cao)
$result += $cao->count_ca_child($children);
return $result+count($this->list_ca_child());
}
public function count_ssl_child($children=FALSE) {
$result = 0;
if ($children)
foreach ($this->list_ssl_child() as $cao)
$result += $cao->ca->count_ssl_child($children);
return $result+count($this->list_ssl_child());
}
public function list_ca_child() {
$result = array();
foreach ($this->child->find_all() as $co) {
// Ignore me if this is the root certificate
if ($co->id == $this->id)
continue;
array_push($result,$co);
}
return $result;
}
public function list_ssl_child() {
$result = array();
foreach ($this->ssl->find_all() as $co)
if ($co->validCA())
array_push($result,$co);
return $result;
}
}
?>

View File

@ -0,0 +1,284 @@
# OpenSSL example configuration file.
# 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 ]
dir = . # Where everything is kept
certs = $dir # Where the issued certs are kept
crl_dir = $dir # Where the issued crl are kept
database = $dir/index.txt # database index file.
new_certs_dir = $dir # default place for new certs.
certificate = $dir/ca.crt # The CA certificate
serial = $dir/serial # The current serial number
crl = $dir/crl.pem # The current CRL
private_key = $dir/ca.key # The private key
RANDFILE = $dir/.rand # private random number file
x509_extensions = usr_cert # The extentions to add to the cert
# 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.
crl_extensions = crl_ext
default_days = 375 # 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 = optional
stateOrProvinceName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_o ]
countryName = match
stateOrProvinceName = supplied
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_ou ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = supplied
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 = 2048
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 = AU
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = VIC
localityName = Locality Name (eg, city)
localityName_default = Melbourne
0.organizationName = Organization Name (eg, company)
0.organizationName_default = IBM Australia
#0.organizationName_default = $ENV::KEY_ORG
# 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 = CSI Data Software
#organizationalUnitName_default = $ENV::KEY_OU
#organizationalUnitName_default =
commonName = Common Name (eg, your name or your server\'s hostname)
#commonName_default = $ENV::KEY_CN
commonName_max = 64
#emailAddress = Email Address
#emailAddress_default = $ENV::KEY_EMAIL
#emailAddress_max = 40
# SET-ex3 = SET extension number 3
#subjectAltName = Alternative Names
[ 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
# 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
# Copy subject details
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
## Should add --extensions client to client certs
[ client ]
nsCertType = client,email,objsign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
## Should add --extensions server to server certs
[ server ]
nsCertType = server
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = cRLSign, keyCertSign
nsCertType = sslCA, emailCA
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
[ v3_ca_ou ]
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = cRLSign, keyCertSign
nsCertType = sslCA, emailCA
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
[ v3_ca_o ]
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = cRLSign, keyCertSign
nsCertType = sslCA, emailCA
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
[ v3_ca_ca ]
basicConstraints = critical,CA:true,pathlen:1
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = cRLSign, keyCertSign
nsCertType = sslCA, emailCA
issuerAltName = issuer:copy,URI:https://www.csidata.co
authorityInfoAccess = OCSP;URI:https://www.csidata.co/ssl/ocsp,caIssuers;URI:https://www.csidata.co/ssl/ca
crlDistributionPoints = URI:https://www.csidata.co/ssl/crl
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier = keyid:always,issuer:always

View File

@ -0,0 +1,22 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* SSL Config Items
*
* @package SSL
* @category Configuration
* @author Deon George
* @copyright (c) 2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
return array(
// Days before certificates can be renewed.
'min_renew_days' => 7,
// Min password length for exports
'minpass_length' => 0,
// Location to openssl config
'config' => 'openssl.cnf',
);
?>

View File

@ -0,0 +1,22 @@
<?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(
'cert'=>array(
'isCert'=>'This is not a valid certificate',
'isCA'=>'This is certificate is not a Certificate Authority certificate',
),
'ssl_ca_id'=>array(
'rule_parentExist'=>'The parent certificate doesnt exist, please define it first',
),
);
?>

View File

@ -0,0 +1,85 @@
<?php if ($mode == 'edit') : ?>
<div class="row">
<fieldset class="col-md-12">
<legend>SSL CA Certificate Edit/Update</legend>
<div class="dl-horizontal">
<dt>Subject</dt>
<dd><?php echo $o->dn(); ?></dd>
<dt>Subject Key ID</dt>
<dd><?php echo $o->ski(); ?></dd>
<dt>Serial Number</dt>
<dd><?php echo $o->serial(); ?></dd>
<?php if (! is_null($o->isRoot()) AND ! $o->isRoot()) : ?>
<dt>Issuer</dt>
<dd>
<?php if ($o->validParent()) : ?>
<?php echo HTML::anchor(URL::link('admin','ssl/edit/').$o->ssl_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>Issuer Valid</dt>
<dd><?php echo $o->validParent(TRUE); ?></dd>
<?php else : ?>
<dt>Status</dt>
<dd>Root Ceritificate</dd>
<?php endif ?>
<dt>Path Length</dt>
<dd><?php echo $o->ca_path_len(); ?></dd>
<dt>Hash</dt>
<dd><?php echo $o->hash(); ?></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>Key Algorithm<dt>
<dd><?php echo $o->algorithm(); ?></dd>
</div> <!-- /dl-horizontal -->
</fieldset>
</div>
<?php endif ?>
<div class="row">
<fieldset class="col-md-6">
<legend>CSR Data</legend>
<?php echo Form::textarea('csr',$o->csr,array('divclass'=>'col-md-12','placeholder'=>'Certificate Sign Request','style'=>'font-family: monospace; font-size: 95%;','rows'=>Form::textarea_rows($o->csr))); ?>
<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))); ?>
</fieldset>
<fieldset class="col-md-6">
<legend>Certificate Data</legend>
<?php echo Form::textarea('cert',$o->cert,array('divclass'=>'col-md-12','placeholder'=>'Public Certificate','style'=>'font-family: monospace; font-size: 95%;','rows'=>Form::textarea_rows($o->cert))); ?>
</fieldset>
</div>
<div class="row">
<div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-default">Cancel</button>
</div>
</div>

View File

@ -0,0 +1,20 @@
<fieldset>
<legend>SSL Certificate Details</legend>
<p>To use SSL with this application, you need to receive a certificate from this service.<p>
<p>Please do the following:<p>
<ol>
<li>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)); ?>
</li>
<li>Submit your CSR to be signed, you'll receive a certificate once approved</li>
</ol>
</fieldset>
<div class="row">
<div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-default">Cancel</button>
</div>
</div>

View File

@ -0,0 +1,87 @@
<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('admin','ssl/edit/')),
)); ?>
<?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
?>
<!--
<?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>