our
(
$STDERR
,
$STDOUT
);
$STDOUT
= \
*STDOUT
;
$STDERR
= \
*STDERR
;
our
(
$errno
,
$errval
);
our
$AUTOLOAD
;
(
$OpenCA::Token::nCipher::VERSION
=
'$Revision: 1.1.1.1 $'
)=~ s/(?:^.*: (\d+))|(?:\s+\$$)/
defined
$1?
"0\.9"
:
""
/eg;
sub
new {
my
$that
=
shift
;
my
$class
=
ref
(
$that
) ||
$that
;
my
$self
= {
DEBUG
=> 0,
debug_fd
=>
$STDERR
,
ONLINECHECKGRACEPERIOD
=> 60,
CHECKCMDTIMEOUT
=> 15,
TMPDIR
=>
"/tmp"
,
};
bless
$self
,
$class
;
my
$keys
= {
@_
};
$self
->{CRYPTO} =
$keys
->{OPENCA_CRYPTO};
$self
->{gettext} =
$keys
->{GETTEXT};
$self
->{NAME} =
$keys
->{OPENCA_TOKEN};
$self
->{MODE} =
$keys
->{TOKEN_MODE};
$self
->{DEBUG} =
$keys
->{DEBUG};
$self
->{KEY} =
$keys
->{KEY};
foreach
(
qw(CHECKCMDTIMEOUT ONLINECHECKGRACEPERIOD TMPDIR)
) {
if
(
exists
$keys
->{
$_
} and (
$keys
->{
$_
} ne
""
)) {
$self
->{
$_
} =
$keys
->{
$_
};
}
}
return
$self
->setError (7151013,
$self
->{gettext} (
"NFAST_HOME not defined."
))
if
(not
$keys
->{NFAST_HOME});
return
$self
->setError (7151014,
$self
->{gettext} (
"NFAST_HOME not accessible."
))
if
(! (-d
$keys
->{NFAST_HOME} and -x
$keys
->{NFAST_HOME}));
$self
->{NFAST_HOME} =
$keys
->{NFAST_HOME};
$keys
->{WRAPPER} =
$keys
->{NFAST_HOME} .
"/bin/with-nfast -M"
unless
(
exists
$keys
->{WRAPPER} and (
$keys
->{WRAPPER} ne
""
));
$self
->{WRAPPER} =
$keys
->{WRAPPER};
return
$self
->setError (7151010,
$self
->{gettext} (
"Crypto layer is not defined."
))
if
(not
$self
->{CRYPTO});
return
$self
->setError (7151012,
$self
->{gettext} (
"The name of the token is not defined."
))
if
(not
$self
->{NAME});
$keys
->{ENGINE} =
"chil"
;
$keys
->{CERT} =
$keys
->{PEM_CERT};
if
(
exists
$keys
->{PRE_ENGINE}) {
$keys
->{DYNAMIC_ENGINE} = 1;
$keys
->{STDOUT_CALLBACK} = \
&_stdout_callback
;
$keys
->{CALLBACK_HANDLER} =
$self
;
my
$pre_ref
;
if
(
ref
$keys
->{PRE_ENGINE}) {
$pre_ref
=
$keys
->{PRE_ENGINE};
}
else
{
push
(@{
$pre_ref
},
$keys
->{PRE_ENGINE});
}
$keys
->{PRE_ENGINE} =
$pre_ref
;
return
$self
->setError (7151016,
$self
->{gettext} (
"7151016 PRE_ENGINE: SO_PATH not defined (configuration problem)"
))
unless
(
grep
(/SO_PATH/, @{
$keys
->{PRE_ENGINE}}));
unshift
@{
$keys
->{PRE_ENGINE}},
"LIST_ADD:1"
unless
(
grep
(/^LIST_ADD:/, @{
$keys
->{PRE_ENGINE}}));
unshift
@{
$keys
->{PRE_ENGINE}},
"ID:chil"
unless
(
grep
(/^ID:/, @{
$keys
->{PRE_ENGINE}}));
push
@{
$keys
->{PRE_ENGINE}},
"LOAD"
unless
(
grep
(/^LOAD/, @{
$keys
->{PRE_ENGINE}}));
push
@{
$keys
->{PRE_ENGINE}},
"THREAD_LOCKING:1"
unless
(
grep
(/^THREAD_LOCKING:/, @{
$keys
->{PRE_ENGINE}}));
}
$self
->{OPENSSL} = OpenCA::OpenSSL->new ( %{
$keys
} );
return
$self
->setError (
$OpenCA::OpenSSL::errno
,
$OpenCA::OpenSSL::errval
)
if
(not
$self
->{OPENSSL});
$self
->debug(
"new: KEY: "
.
$self
->{KEY});
$self
->debug(
"new: NFAST_HOME: "
.
$self
->{NFAST_HOME});
$self
->debug(
"new: WRAPPER: "
.
$self
->{WRAPPER});
$self
->debug(
"new: TMPDIR: "
.
$self
->{TMPDIR});
return
$self
;
}
sub
cachestatus {
my
$self
=
shift
;
my
$keys
= {
@_
};
return
undef
unless
defined
$keys
->{ID};
my
$filename
=
$self
->{TMPDIR} .
"/openca_cache_nCipher_"
.
$keys
->{ID};
if
(
defined
$keys
->{RESET}) {
unlink
$filename
;
return
1;
}
elsif
(
defined
$keys
->{TIMEOUT}) {
return
undef
unless
(-e
$filename
);
my
$age
=
time
- (
stat
(
$filename
))[9];
$self
->debug(
"cachestatus: state file $filename age: $age"
);
return
undef
if
(
$age
< 0);
$age
= 1
if
(
$age
== 0);
if
(
$age
<
$keys
->{TIMEOUT}) {
$self
->debug(
"cachestatus: status was updated within configured timeout of "
.
$keys
->{TIMEOUT} .
" seconds ($age seconds ago)"
);
$self
->cachestatus(
ID
=>
$keys
->{ID})
if
(
exists
$keys
->{RETRIGGER} and
$keys
->{RETRIGGER});
return
$age
;
}
$self
->debug(
"cachestatus: timeout expired for "
.
$keys
->{ID});
unlink
$filename
;
return
0;
}
else
{
$self
->debug(
"cachestatus: caching online status in $filename"
);
local
*HANDLE
;
open
HANDLE,
">$filename"
;
close
HANDLE;
}
}
sub
getKeyHash {
my
$self
=
shift
;
return
$self
->{KEYINFO}->{
$self
->{KEY}}->{OCS}->{HASH}
if
(
exists
$self
->{KEYINFO}->{
$self
->{KEY}});
$self
->debug(
"getKeyHash: Getting object hash for key "
.
$self
->{KEY});
my
@cmd
= (
qq("$self->{NFAST_HOME}/bin/nfkmverify")
,
"hwcrhk"
,
qq("$self->{KEY}")
);
my
$keyid
=
""
;
my
$keyinfo
;
my
@keys
;
eval
{
local
$SIG
{ALRM} =
sub
{
die
"alarm\n"
};
alarm
$self
->{CHECKCMDTIMEOUT};
$self
->debug(
"getKeyHash: exec: "
.
join
(
' '
,
@cmd
));
if
(!
open
HANDLE,
join
(
' '
,
@cmd
) .
"|"
) {
$self
->debug(
"getKeyHash: nCipher nfkmverify: could not run command '"
.
join
('
', @cmd) . "'
");
$self
->setError (7153055,
$self
->{gettext} (
"Could not execute nCipher nfkmverify command"
));
alarm
0;
return
undef
;
}
while
(<HANDLE>) {
chomp
;
if
(/^\*\* \[Application key hwcrhk (.*)\]/) {
$keyid
= $1;
push
(
@keys
,
$keyid
);
}
else
{
if
(
$keyid
ne
""
) {
if
(/^\s
*Cardset
protected:\s+(\d+)\/(\d+)\s*(.*?)\s*\[(\d+)s\s+`(.*?)'\]/) {
$keyinfo
->{
$keyid
}->{OCS}->{QUORUM}->{K} = $1;
$keyinfo
->{
$keyid
}->{OCS}->{QUORUM}->{N} = $2;
$keyinfo
->{
$keyid
}->{OCS}->{TYPE} = $3;
$keyinfo
->{
$keyid
}->{OCS}->{TIMEOUT} = $4;
$keyinfo
->{
$keyid
}->{OCS}->{NAME} = $5;
}
if
(/^\s
*Cardset
hash\s+(.*)/) {
$keyinfo
->{
$keyid
}->{OCS}->{HASH} = $1;
}
if
(/^\s
*Type
\s+(.*)\s+(\d+)/) {
$keyinfo
->{
$keyid
}->{TYPE} = $1;
$keyinfo
->{
$keyid
}->{BIT} = $2;
}
}
}
}
close
HANDLE;
alarm
0;
};
if
($@) {
if
($@ ne
"alarm\n"
) {
$self
->setError(7151015,
$self
->{gettext} (
"Unexpected exception during program execution"
));
return
undef
;
}
$self
->debug(
"getKeyHash: nCipher nfkmverify did not terminate within timeout and was interrupted administratively"
);
$self
->setError (7153059,
$self
->{gettext} (
"External program call timed out"
));
return
undef
;
}
if
($? != 0) {
$self
->debug(
"getKeyHash: nCipher nfkmverify returned error code $?"
);
$self
->setError (7153055,
$self
->{gettext} (
"Could not execute nCipher nfkmverify command"
));
return
undef
;
}
$self
->debug(
"getKeyHash: Key information summary"
);
foreach
(
@keys
) {
my
$k
= \
$keyinfo
->{
$_
};
$self
->debug(
"getKeyHash: Key $_:"
);
$self
->debug(
"getKeyHash: Type: $$k->{TYPE} ($$k->{BIT} bit)"
);
$self
->debug(
"getKeyHash: OCS name: $$k->{OCS}->{NAME}"
);
$self
->debug(
"getKeyHash: OCS hash: $$k->{OCS}->{HASH}"
);
$self
->debug(
"getKeyHash: OCS type: $$k->{OCS}->{TYPE}"
);
$self
->debug(
"getKeyHash: OCS quorum: $$k->{OCS}->{QUORUM}->{K}/$$k->{OCS}->{QUORUM}->{N}"
);
$self
->debug(
"getKeyHash: OCS timeout: $$k->{OCS}->{TIMEOUT}"
);
}
$self
->{KEYINFO} =
$keyinfo
;
return
$keyinfo
->{
$keyid
}->{OCS}->{HASH};
}
sub
setError {
my
$self
=
shift
;
if
(
scalar
(
@_
) == 4) {
my
$keys
= {
@_
};
$errval
=
$keys
->{ERRVAL};
$errno
=
$keys
->{ERRNO};
}
else
{
$errno
=
$_
[0];
$errval
=
$_
[1];
}
return
undef
if
(not
$errno
);
print
$STDERR
"PKI Master Alert: OpenCA::Token::nCipher error\n"
;
print
$STDERR
"PKI Master Alert: Aborting all operations\n"
;
print
$STDERR
"PKI Master Alert: Error: $errno\n"
;
print
$STDERR
"PKI Master Alert: Message: $errval\n"
;
print
$STDERR
"PKI Master Alert: debugging messages of empty token follow\n"
;
$self
->{debug_fd} =
$STDERR
;
$self
->debug ();
$self
->{debug_fd} =
$STDOUT
;
$self
->cachestatus(
ID
=>
'key_'
.
$self
->{KEY},
RESET
=> 1);
return
undef
;
}
sub
errno {
my
$self
=
shift
;
return
$self
->{errno};
}
sub
errval {
my
$self
=
shift
;
return
$self
->{errval};
}
sub
debug {
my
$self
=
shift
;
if
(
$_
[0]) {
$self
->{debug_msg}[
scalar
@{
$self
->{debug_msg}}] =
$_
[0];
$self
->debug ()
if
(
$self
->{DEBUG});
}
else
{
my
$oldfh
;
if
(
$self
->{errno})
{
$oldfh
=
select
$self
->{debug_fd};
print
"PKI Debugging: OpenCA::Token::nCipher error\n"
;
print
"PKI Debugging: Aborting all operations\n"
;
print
"PKI Debugging: Error: "
.
$self
->{errno}.
"\n"
;
print
"PKI Debugging: Message: "
.
$self
->{errval}.
"\n"
;
print
"PKI Debugging: debugging messages of OpenSSL token follow\n"
;
select
$oldfh
;
}
my
$msg
;
foreach
$msg
(@{
$self
->{debug_msg}}) {
$msg
=~ s/ /
 
;/g
if
(
$self
->{debug_fd} eq
$STDOUT
);
my
$oldfh
=
select
$self
->{debug_fd};
print
"OpenCA::Token::nCipher->$msg<br>\n"
;
select
$oldfh
;
}
$self
->{debug_msg} = ();
}
}
sub
login {
my
$self
=
shift
;
if
(
$self
->online() and
$self
->keyOnline()) {
$self
->{ONLINE} = 1;
return
1;
}
return
undef
;
}
sub
logout {
my
$self
=
shift
;
$self
->{ONLINE} = 0;
return
1;
}
sub
online {
my
$self
=
shift
;
$self
->debug(
"online: nCipher HSM online check"
);
if
(
$self
->cachestatus(
ID
=>
'ncipher_module'
,
TIMEOUT
=>
$self
->{ONLINECHECKGRACEPERIOD},
RETRIGGER
=> 1)) {
$self
->debug(
"online: Last HSM online check was performed less than "
.
$self
->{ONLINECHECKGRACEPERIOD} .
" seconds ago. Returning cached result."
);
return
1;
}
$self
->debug(
"online: Checking nCipher infrastructure"
);
my
$section
=
""
;
my
$enquiry
;
my
@modules
;
eval
{
local
$SIG
{ALRM} =
sub
{
die
"alarm\n"
};
alarm
$self
->{CHECKCMDTIMEOUT};
my
@cmd
= (
qq("$self->{NFAST_HOME}/bin/enquiry")
);
$self
->debug(
"online: exec: "
.
join
(
' '
,
@cmd
));
if
(!
open
HANDLE,
join
(
' '
,
@cmd
) .
"|"
) {
$self
->debug(
"online: nCipher enquiry: could not run command '"
.
join
('
', @cmd) . "'
");
$self
->setError (7153053,
$self
->{gettext} (
"Could not execute nCipher enquiry command"
));
alarm
0;
return
undef
;
}
while
(<HANDLE>) {
chomp
;
if
(/^\S/) {
s/[: \
$section
=
lc
(
$_
);
push
(
@modules
,
$section
)
if
(
$section
=~ /^module/);
}
else
{
if
((
$section
ne
""
) and
(/^\s+(mode|version)\s\s+(\S+)/)) {
$enquiry
->{
$section
}->{
lc
($1)} = $2;
}
}
}
close
HANDLE;
alarm
0;
};
if
($@) {
if
($@ ne
"alarm\n"
) {
$self
->setError(7151015,
$self
->{gettext} (
"Unexpected exception during program execution"
));
return
undef
;
}
$self
->debug(
"online: nCipher enquiry did not terminate within timeout and was interrupted administratively"
);
$self
->setError (7153059,
$self
->{gettext} (
"External program call timed out"
));
return
undef
;
}
if
($? != 0) {
$self
->debug(
"online: nCipher enquiry: hardserver is not running (error code $?)"
);
$self
->setError (7153051,
$self
->{gettext} (
"nCipher hardserver process is not running"
));
return
undef
;
}
$self
->debug(
"online: nCipher hardserver information"
);
my
$operational_modules
= 0;
foreach
((
'server'
,
@modules
)) {
$self
->debug(
"online: '$_' (version: $enquiry->{$_}->{version}) is $enquiry->{server}->{mode}"
);
$operational_modules
++
if
((
$_
ne
"server"
) and (
$enquiry
->{
$_
}->{mode} eq
"operational"
));
}
if
(
$enquiry
->{server}->{mode} ne
"operational"
) {
$self
->setError (7153052,
$self
->{gettext} (
"nCipher hardserver process is not operational"
));
return
undef
;
}
if
(
$operational_modules
< 1) {
$self
->debug(
"online: No operational nCipher modules are online."
);
$self
->setError (7153056,
$self
->{gettext} (
"No operational nCipher modules online"
));
return
undef
;
}
$self
->cachestatus(
ID
=>
'ncipher_module'
);
return
1;
}
sub
keyOnline {
my
$self
=
shift
;
$self
->debug(
"keyOnline: nCipher key online check"
);
if
(
$self
->cachestatus(
ID
=>
'key_'
.
$self
->{KEY},
TIMEOUT
=>
$self
->{ONLINECHECKGRACEPERIOD},
RETRIGGER
=> 1)) {
$self
->debug(
"keyOnline: Last key online check was performed less than "
.
$self
->{ONLINECHECKGRACEPERIOD} .
" seconds ago. Returning cached result."
);
return
1;
}
my
@cmd
= (
$self
->{WRAPPER},
"$self->{NFAST_HOME}/bin/nfkminfo"
);
my
$section
=
""
;
my
$worldinfo
;
eval
{
local
$SIG
{ALRM} =
sub
{
die
"alarm\n"
};
alarm
$self
->{CHECKCMDTIMEOUT};
$self
->debug(
"keyOnline: exec: "
.
join
(
' '
,
@cmd
));
if
(!
open
HANDLE,
join
(
' '
,
@cmd
) .
"|"
) {
$self
->debug(
"keyOnline: nCipher nfkminfo: could not run command '"
.
join
('
', @cmd) . "'
");
$self
->setError (7153054,
$self
->{gettext} (
"Could not execute nCipher nfkminfo command"
));
alarm
0;
return
undef
;
}
while
(<HANDLE>) {
chomp
;
if
(/^\S/) {
s/[: \
$section
=
lc
(
$_
);
}
else
{
if
((
$section
ne
""
) and
(/^\s+(state)\s\s+(.*)/)) {
$worldinfo
->{
$section
}->{
lc
($1)} = $2;
}
if
(
$section
=~ /preloadedobjects/) {
/\s+([0-9A-Fa-f]+)/;
$worldinfo
->{
$section
}->{preloaded}->{$1}++;
}
}
}
close
HANDLE;
alarm
0;
};
if
($@) {
if
($@ ne
"alarm\n"
) {
$self
->setError(7151015,
$self
->{gettext} (
"Unexpected exception during program execution"
));
return
undef
;
}
$self
->debug(
"keyOnline: nCipher nfkminfo did not terminate within timeout and was interrupted administratively"
);
$self
->setError (7153059,
$self
->{gettext} (
"External program call timed out"
));
return
undef
;
}
if
($? != 0) {
$self
->debug(
"keyOnline: nCipher nfkminfo returned error code $?"
);
$self
->setError (7153054,
$self
->{gettext} (
"Could not execute nCipher nfkminfo command"
));
return
undef
;
}
$self
->debug(
"keyOnline: nCipher security world information"
);
$self
->debug(
"keyOnline: state:"
.
$worldinfo
->{world}->{state});
my
$initialized
= 0;
my
$usable
= 0;
foreach
(
split
(/\s+/,
$worldinfo
->{world}->{state})) {
$initialized
++
if
(
$_
eq
"Initialised"
);
$usable
++
if
(
$_
eq
"Usable"
);
}
if
(!
$initialized
) {
$self
->debug(
"keyOnline: Security world is not initialized."
);
$self
->setError (7153057,
$self
->{gettext} (
"Security world not initialized/usable"
));
return
undef
;
}
if
(!
$usable
) {
$self
->debug(
"keyOnline: Security world is not usable."
);
$self
->setError (7153057,
$self
->{gettext} (
"Security world not initialized/usable"
));
return
undef
;
}
if
(!
exists
$worldinfo
->{
$section
}->{preloaded}) {
$self
->debug(
"keyOnline: No preloaded objects found"
);
$self
->setError (7153058,
$self
->{gettext} (
"No preloaded objects found"
));
return
undef
;
}
$self
->debug(
"keyOnline: Preloaded objects:"
);
foreach
(
keys
%{
$worldinfo
->{
$section
}->{preloaded}}) {
$self
->debug(
"keyOnline: $_"
);
}
my
$ocshash
=
$self
->getKeyHash();
$self
->debug(
"keyOnline: Verify if key ocs object hash $ocshash is preloaded"
);
if
(!
exists
$worldinfo
->{
$section
}->{preloaded}->{
$ocshash
}) {
$self
->debug(
"keyOnline: Object is not preloaded, key is not usable"
);
$self
->setError (7153050,
$self
->{gettext} (
"Required object is not preloaded, key is not usable"
));
return
undef
;
}
$self
->debug(
"keyOnline: Key seems to be usable"
);
$self
->cachestatus(
ID
=>
'key_'
.
$self
->{KEY});
return
1;
}
sub
getMode {
return
"daemon"
;
}
sub
AUTOLOAD {
my
$self
=
shift
;
if
(
$AUTOLOAD
=~ /OpenCA::OpenSSL/)
{
print
STDERR
"PKI Master Alert: OpenCA::OpenSSL is missing a function\n"
;
print
STDERR
"PKI Master Alert: $AUTOLOAD\n"
;
$self
->setError (666,
$self
->{gettext} ("OpenCA::OpenSSL is missing a function. __FUNCTION
__",
"__FUNCTION__"
,
$AUTOLOAD
));
return
undef
;
}
$self
->debug (
"OpenCA::Token::nCipher: AUTOLOAD => $AUTOLOAD"
);
return
1
if
(
$AUTOLOAD
eq
'OpenCA::Token::nCipher::DESTROY'
);
my
$function
=
$AUTOLOAD
;
$function
=~ s/.*:://g;
$self
->debug (
"nCipher AUTOLOAD function $function"
);
my
$ret
;
eval
{
local
$SIG
{ALRM} =
sub
{
die
"alarm\n"
};
alarm
$self
->{CHECKCMDTIMEOUT};
$ret
=
$self
->{OPENSSL}->
$function
(
@_
);
alarm
0;
};
if
($@) {
if
($@ ne
"alarm\n"
) {
$self
->setError(7151015,
$self
->{gettext} (
"Unexpected exception during program execution"
));
return
undef
;
}
$self
->debug(
"AUTOLOAD: OpenCA::OpenSSL::$function did not terminate within timeout and was interrupted administratively"
);
$self
->setError (7153059,
$self
->{gettext} (
"External program call timed out"
));
return
undef
;
}
$self
->setError (
$OpenCA::OpenSSL::errno
,
$OpenCA::OpenSSL::errval
);
return
$ret
;
}
sub
genKey {
my
$self
=
shift
;
my
$keys
= {
@_
};
return
$self
->setError (7154001,
$self
->{gettext} (
"Key generation not supported."
));
}
sub
_stdout_callback
{
my
$self
=
shift
;
my
$result
=
shift
;
$result
=~ s/^OpenSSL>\s//;
$result
=
substr
$result
,
index
$result
,
"OpenSSL> "
;
return
$result
}
1;