Cannot connect to Cassandra on a cloud server using phpcassa - php

I have setup a test cloud server (Ubuntu 10.10 on Rackspace) to play with Cassandra database (0.6.8).
I can connect no problem both from within the server and from a computer external to the cloud using the cassandra-cli basic client: I have created and retrieved sample data.
I have then installed phpcassa on the cloud server and on an external computer.
I have created a very simple php program to test the connection:
<?php
$GLOBALS['THRIFT_ROOT'] = dirname(__FILE__) . '/include/thrift/';
require_once $GLOBALS['THRIFT_ROOT'].'/packages/cassandra/Cassandra.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TFramedTransport.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
include_once(dirname(__FILE__) . '/include/phpcassa.php');
include_once(dirname(__FILE__) . '/include/uuid.php');
echo 'phpcassa test01<br /><br />';
CassandraConn::add_node('184.106.97.245', 9160);
$users = new CassandraCF('Keyspace1', 'Standard2');
try {
$res = $users->get('jsmith');
print_r($res);
} catch (Exception $e) {
print CassandraConn::$last_error;
}
echo 'End.'
?>
I am using buffered transport.
I can happily connect and read data from within the server (I have also installed LAMP on it):
phpcassa test01
Array ( [age] => 42 [first] => John [last] => Smith )
End.
but not from outside because I am always getting the exception:
phpcassa test01
TException: TSocket: Could not connect to 184.106.97.245:9160 (Operation timed out [60])
End.
This is my storage-conf.xml file:
<Storage>
<ClusterName>Test Cluster</ClusterName>
<AutoBootstrap>false</AutoBootstrap>
<HintedHandoffEnabled>true</HintedHandoffEnabled>
<IndexInterval>128</IndexInterval>
<Keyspaces>
<Keyspace Name="Keyspace1">
<ColumnFamily Name="Standard1" CompareWith="BytesType"
KeysCached="1000"
RowsCached="100"
RowCacheSavePeriodInSeconds="0"
KeyCacheSavePeriodInSeconds="3600"/>
<ColumnFamily Name="Standard2"
CompareWith="UTF8Type"
KeysCached="100%"/>
<ColumnFamily Name="StandardByUUID1" CompareWith="TimeUUIDType" />
<ColumnFamily Name="Super1"
ColumnType="Super"
CompareWith="BytesType"
CompareSubcolumnsWith="BytesType" />
<ColumnFamily Name="Super2"
ColumnType="Super"
CompareWith="UTF8Type"
CompareSubcolumnsWith="UTF8Type"
RowsCached="10000"
KeysCached="50%"
Comment="A column family with supercolumns, whose column and subcolumn names are UTF8 strings"/>
<ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy>
<ReplicationFactor>1</ReplicationFactor>
<EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
</Keyspace>
</Keyspaces>
<Authenticator>org.apache.cassandra.auth.AllowAllAuthenticator</Authenticator>
<Partitioner>org.apache.cassandra.dht.RandomPartitioner</Partitioner>
<InitialToken></InitialToken>
<SavedCachesDirectory>/var/lib/cassandra/saved_caches</SavedCachesDirectory>
<CommitLogDirectory>/var/lib/cassandra/commitlog</CommitLogDirectory>
<DataFileDirectories>
<DataFileDirectory>/var/lib/cassandra/data</DataFileDirectory>
</DataFileDirectories>
<Seeds>
<Seed>184.106.97.245</Seed>
</Seeds>
<RpcTimeoutInMillis>10000</RpcTimeoutInMillis>
<CommitLogRotationThresholdInMB>128</CommitLogRotationThresholdInMB>
<ListenAddress>184.106.97.245</ListenAddress>
<StoragePort>7000</StoragePort>
<ThriftAddress>184.106.97.245</ThriftAddress>
<ThriftPort>9160</ThriftPort>
<ThriftFramedTransport>false</ThriftFramedTransport>
<DiskAccessMode>auto</DiskAccessMode>
<RowWarningThresholdInMB>64</RowWarningThresholdInMB>
<SlicedBufferSizeInKB>64</SlicedBufferSizeInKB>
<FlushDataBufferSizeInMB>32</FlushDataBufferSizeInMB>
<FlushIndexBufferSizeInMB>8</FlushIndexBufferSizeInMB>
<ColumnIndexSizeInKB>64</ColumnIndexSizeInKB>
<MemtableThroughputInMB>64</MemtableThroughputInMB>
<BinaryMemtableThroughputInMB>256</BinaryMemtableThroughputInMB>
<MemtableOperationsInMillions>0.3</MemtableOperationsInMillions>
<MemtableFlushAfterMinutes>60</MemtableFlushAfterMinutes>
<ConcurrentReads>8</ConcurrentReads>
<ConcurrentWrites>32</ConcurrentWrites>
<CommitLogSync>periodic</CommitLogSync>
<CommitLogSyncPeriodInMS>10000</CommitLogSyncPeriodInMS>
<GCGraceSeconds>864000</GCGraceSeconds>
<DoConsistencyChecksBoolean>true</DoConsistencyChecksBoolean>
</Storage>
Rackspace support suggested changing the firewall settings but I have no firewall:
root#Oahu:~# iptables --list
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Any help very much appreciated!!!
Cheers, Rujero

Access the node (first) with http://wiki.apache.org/cassandra/CassandraCli

Related

Read Windows Installer (MSI file) attributes from PHP

I have a Windows MSI file, that I need to programmatically read the version number from. The only place I can see this version is within the Subject of the file details:
If I somehow can read the entire content of Subject this would be fine but is there any way to get this from PHP? The PHP is running in an IIS web server, if this helps ;-)
stat is of no help for this.
I considered doing a checksum of the file, and can do that, but I really need the real version.
For now I am unable to find a native PHP solution for this, so I have temporarily solved this by calling a Powershell script as it seems easier to do in there.
I am now having this PHP code:
$version = exec("powershell.exe -file GetMsiVersion.ps1 MyFile.msi);
With my above picture then $version will contain 1.5.9, so I will not even require to interpret the data from Subject.
The GetMsiVersion.ps1 Powershell script has this code:
function Get-Property ($Object, $PropertyName, [object[]]$ArgumentList) {
return $Object.GetType().InvokeMember($PropertyName, 'Public, Instance, GetProperty', $null, $Object, $ArgumentList)
}
function Invoke-Method ($Object, $MethodName, $ArgumentList) {
return $Object.GetType().InvokeMember($MethodName, 'Public, Instance, InvokeMethod', $null, $Object, $ArgumentList)
}
$Path = $args[0]
$msiOpenDatabaseModeReadOnly = 0
$Installer = New-Object -ComObject WindowsInstaller.Installer
$Database = Invoke-Method $Installer OpenDatabase #($Path, $msiOpenDatabaseModeReadOnly)
$View = Invoke-Method $Database OpenView #("SELECT Value FROM Property WHERE Property='ProductVersion'")
Invoke-Method $View Execute
$Record = Invoke-Method $View Fetch
if ($Record) {
Write-Output (Get-Property $Record StringData 1)
}
Invoke-Method $View Close #()
I will accept this as the best solution here-and-now but, I hope this can be archieved natively from PHP as I see this as a better and more clean solution - and by then I will accept that as the best answer (or at least unaccept my own temporary solution).
Doing an exec is a little evil ;-)
Two things first:
I have never accessed COM from PHP, but below are some VBScript samples of getting information from MSI files by using MSI API - COM automation. There are also Win32 functions.
That field you refer to is not a version field, but a text field from the MSI's "Summary Stream" - a special part of the MSI file with "meta information" of various kinds. Summary Information Stream (the full name).
Here is how you can get the REAL version of an MSI file. This is stored in the property "ProductVersion" in the MSI file. There are at least two different ways to retrieve it - by opening the MSI file as a session or just access the property table via SQL query:
Access version via Session object:
Const msiUILevelNone = 2
Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")
installer.UILevel = msiUILevelNone
Set s = installer.OpenPackage("C:\MySetup.msi",1)
MsgBox CStr(s.ProductProperty("ProductVersion"))
Accessing version via SQL agains MSI database (property table):
Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")
' Open MSI database in read-only mode (0)
Set db = installer.OpenDatabase("C:\MySetup.msi", 0)
Set view = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'")
view.Execute
Set record = view.Fetch
MsgBox CStr(record.StringData(1))
Then there is the issue of accessing the SummaryStream - which is what you are really asking by the looks of it - here is a simple smoke test with some hints as to what properties you can retrieve - be careful with the summary stream - corruption is possible in various ways (I don't recall the details, but it should be safe to access read-only):
Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")
' Open MSI database in read-only mode (0)
Set db = installer.OpenDatabase("C:\MySetup.msi", 0)
MsgBox CStr(db.SummaryInformation.Property(3))
' 1 = "Codepage"
' 2 = "Title"
' 3 = "Subject"
' 4 = "Author"
' 5 = "Keywords"
' 6 = "Comments"
' 7 = "Template"
' 8 = "LastAuthor"
' 9 = "Revision"
' 11 = "Printed"
' 12 = "Created"
' 13 = "Saved"
' 14 = "Pages"
' 15 = "Words"
' 16 = "Characters"
' 18 = "Application"
' 19 = "Security"
Links:
Modify an MSI using MSI SQL VBScript WiRunSQL.vbs
List tables in MSI file using VBScript

How to use phpseclib to verify that a certificate is signed by a public CA?

I have a need to ensure that an SMTP server certificate is signed by a public certificate authority. I would like to use phpseclib or some other trusted library. I believe I can use the root certificates extracted from Firefox.
There are some home-brew approaches here to check cert dates and other metadata, but it does not look like that does any signature checking as such (other than ensuring that OpenSSL does it). In any case, I want to use a library - I want to write as little certificate-handling code as possible, since I am not a cryptographer.
That said, the answers on the above link were still very useful, as it helped me get some code to fetch a certificate from a TLS conversation:
$url = "tcp://{$domain}:{$port}";
$connection_context_option = [
'ssl' => [
'capture_peer_cert' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
]
];
$connection_context = stream_context_create($connection_context_option);
$connection_client = stream_socket_client($url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $connection_context);
stream_set_timeout($connection_client, 2);
fread($connection_client, 10240);
fwrite($connection_client,"HELO alice\r\n");
fread($connection_client, 10240);
fwrite($connection_client, "STARTTLS\r\n");
fread($connection_client, 10240);
$ok = stream_socket_enable_crypto($connection_client, TRUE, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
if ($ok === false)
{
return false;
}
$connection_info = stream_context_get_params($connection_client);
openssl_x509_export($info["options"]["ssl"]["peer_certificate"], $pem_encoded);
(Note that I have turned off certificate validation here deliberately. This is because I have no control over what hosts this runs on, and their certificates may be old or misconfigured. Therefore, I wish to fetch the certificate regardless of the verification on the connection I am using, and then verify it myself using a cacert.pem that I will supply.)
That will give me a certificate like this. This one is for Microsoft's Live.com email server at smtp.live.com:587:
-----BEGIN CERTIFICATE-----
MIIG3TCCBcWgAwIBAgIQAtB7LVsRCmgbyWiiw7Sf5jANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTcwOTEzMDAwMDAwWhcN
MTkwOTEzMTIwMDAwWjBqMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
aW9uMRQwEgYDVQQDEwtvdXRsb29rLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAIz2tovvgBmK4sOHgpyzCdtXrI0XOujctf6LHMj16wzUnMEatioS
tH0Pz0dKkCr/0yd9qtXbGhD1o6WhFsd7k651K9MZ98+uQ29SzTIAl6y1gkaBbp4h
MFXcE5EpRNHHmK8t2OR7hzmrvvNr6OTYv7BhVCw9pSrQqEFNno0K2TQRhAD9uzrL
OY+rBBVedCXWXH7uhZoZ6joUU7CEA5pPMzKPL1ro+Eorc8vt5FYOC+oAT587+b1M
z+jbZVQlq0qaMkBKRtUIII78MYY0n8DopGqHyzwqWoGySHJNC8256q+MwsZQvvQ3
vmy/rf61h2sg1tU0s7O88Yufxp0LSaMMzZcCAwEAAaOCA5owggOWMB8GA1UdIwQY
MBaAFA+AYRyCMWHVLyjnjUY4tCzhxtniMB0GA1UdDgQWBBT7hLoZ/03rqwcslIc2
0k0z2R+vNTCCAdwGA1UdEQSCAdMwggHPggtvdXRsb29rLmNvbYIWKi5jbG8uZm9v
dHByaW50ZG5zLmNvbYIWKi5ucmIuZm9vdHByaW50ZG5zLmNvbYIgYXR0YWNobWVu
dC5vdXRsb29rLm9mZmljZXBwZS5uZXSCG2F0dGFjaG1lbnQub3V0bG9vay5saXZl
Lm5ldIIdYXR0YWNobWVudC5vdXRsb29rLm9mZmljZS5uZXSCHWNjcy5sb2dpbi5t
aWNyb3NvZnRvbmxpbmUuY29tgiFjY3Mtc2RmLmxvZ2luLm1pY3Jvc29mdG9ubGlu
ZS5jb22CC2hvdG1haWwuY29tgg0qLmhvdG1haWwuY29tggoqLmxpdmUuY29tghZt
YWlsLnNlcnZpY2VzLmxpdmUuY29tgg1vZmZpY2UzNjUuY29tgg8qLm9mZmljZTM2
NS5jb22CFyoub3V0bG9vay5vZmZpY2UzNjUuY29tgg0qLm91dGxvb2suY29tghYq
LmludGVybmFsLm91dGxvb2suY29tggwqLm9mZmljZS5jb22CEm91dGxvb2sub2Zm
aWNlLmNvbYIUc3Vic3RyYXRlLm9mZmljZS5jb22CGHN1YnN0cmF0ZS1zZGYub2Zm
aWNlLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMGsGA1UdHwRkMGIwL6AtoCuGKWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
bS9zc2NhLXNoYTItZzEuY3JsMC+gLaArhilodHRwOi8vY3JsNC5kaWdpY2VydC5j
b20vc3NjYS1zaGEyLWcxLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwBATAqMCgG
CCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEC
AjB8BggrBgEFBQcBAQRwMG4wJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
ZXJ0LmNvbTBGBggrBgEFBQcwAoY6aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
L0RpZ2lDZXJ0U0hBMlNlY3VyZVNlcnZlckNBLmNydDAMBgNVHRMBAf8EAjAAMA0G
CSqGSIb3DQEBCwUAA4IBAQA3zjN7I6jTeL+08nhG5eAY0q4pLY40bCQHqONBLSI3
uRmQFUfrQOPYBqLC1QU+J2Z2HcX7YiqE3WAR3ODS9g2BAVXkKOQKNBnr2hKwueOz
qPwyvTyzcIQYUw+SrTX+bfJwYMTmZvtP9S7/pB1jPhrV7YGsD55AI9bGa9cmH7VQ
OiL1p5Qovg5KRsldoZeC04OF/UQIR1fv47VGptsHHGypvSo1JinJFQMXylqLIrUW
lV66p3Ui7pFABGc/Lv7nOyANXfLugBO8MyzydGA4NRGiS2MbGpswPCg154pWausU
M0qaEPsM2o3CSTfxSJQQIyEe+izV3UQqYSyWkNqCCFPN
-----END CERTIFICATE-----
OK, great. So I want to validate this against any public CA. I believe this is a valid certificate, and the chain is correctly verified using this checking service:
Array
(
[name] => /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=outlook.com
[subject] => Array
(
[C] => US
[ST] => Washington
[L] => Redmond
[O] => Microsoft Corporation
[CN] => outlook.com
)
[hash] => a3c08ece
[issuer] => Array
(
[C] => US
[O] => DigiCert Inc
[CN] => DigiCert SHA2 Secure Server CA
)
[version] => 2
[serialNumber] => 3740952067977374966703603448215281638
[serialNumberHex] => 02D07B2D5B110A681BC968A2C3B49FE6
[validFrom] => 170913000000Z
[validTo] => 190913120000Z
[validFrom_time_t] => 1505260800
[validTo_time_t] => 1568376000
[signatureTypeSN] => RSA-SHA256
[signatureTypeLN] => sha256WithRSAEncryption
[signatureTypeNID] => 668
[purposes] => Array
(
[1] => Array
(
[0] => 1
[1] =>
[2] => sslclient
)
[2] => Array
(
[0] => 1
[1] =>
[2] => sslserver
)
[3] => Array
(
[0] => 1
[1] =>
[2] => nssslserver
)
[4] => Array
(
[0] =>
[1] =>
[2] => smimesign
)
[5] => Array
(
[0] =>
[1] =>
[2] => smimeencrypt
)
[6] => Array
(
[0] =>
[1] =>
[2] => crlsign
)
[7] => Array
(
[0] => 1
[1] => 1
[2] => any
)
[8] => Array
(
[0] => 1
[1] =>
[2] => ocsphelper
)
[9] => Array
(
[0] =>
[1] =>
[2] => timestampsign
)
)
[extensions] => Array
(
[authorityKeyIdentifier] => keyid:0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2
[subjectKeyIdentifier] => FB:84:BA:19:FF:4D:EB:AB:07:2C:94:87:36:D2:4D:33:D9:1F:AF:35
[subjectAltName] => DNS:outlook.com, DNS:*.clo.footprintdns.com, DNS:*.nrb.footprintdns.com, DNS:attachment.outlook.officeppe.net, DNS:attachment.outlook.live.net, DNS:attachment.outlook.office.net, DNS:ccs.login.microsoftonline.com, DNS:ccs-sdf.login.microsoftonline.com, DNS:hotmail.com, DNS:*.hotmail.com, DNS:*.live.com, DNS:mail.services.live.com, DNS:office365.com, DNS:*.office365.com, DNS:*.outlook.office365.com, DNS:*.outlook.com, DNS:*.internal.outlook.com, DNS:*.office.com, DNS:outlook.office.com, DNS:substrate.office.com, DNS:substrate-sdf.office.com
[keyUsage] => Digital Signature, Key Encipherment
[extendedKeyUsage] => TLS Web Server Authentication, TLS Web Client Authentication
[crlDistributionPoints] =>
Full Name:
URI:http://crl3.digicert.com/ssca-sha2-g1.crl
Full Name:
URI:http://crl4.digicert.com/ssca-sha2-g1.crl
[certificatePolicies] => Policy: 2.16.840.1.114412.1.1
CPS: https://www.digicert.com/CPS
Policy: 2.23.140.1.2.2
[authorityInfoAccess] => OCSP - URI:http://ocsp.digicert.com
CA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt
[basicConstraints] => CA:FALSE
)
)
Here is how I am trying to validate the sig in phpseclib:
$x509 = new \phpseclib\File\X509();
// From the Mozilla bundle (getPublicCaCerts splits them with a regex)
$splitCerts = getPublicCaCerts(file_get_contents('cacert.pem'));
// Load the certs separately
$caStatus = true;
foreach ($splitCerts as $caCert)
{
$caStatus = $caStatus && $x509->loadCA($caCert);
}
// $caStatus is now true, so all good here
$certData = $x509->loadX509($pem_encoded); // From the TLS server
$valid = $x509->validateSignature();
// $valid is now false
This returns false, which is not what I expect. I wonder if I have got the input formats correct? The loading of the CAs and the cert under test seem to return good values. Unfortunately, the phpseclib docs are a bit light on examples, and I've not found much elsewhere on the web.
Aside: I have a vague suspicion that this library could help me, assuming it has the feature to verify a certificate. However, I think it is trying to do to much for my case - I want my software to run on shared hosting, and auto-downloading feels like another moving part that might fail. I would rather deploy my own package, supply the public CA information as a (large) parameter, and run the validation test in situ. phpseclib is probably perfect for that, as long as I can figure out the input formats!
Possible reason: phpseclib can't find a matching cert to test
I have narrowed the problem down to a search loop in phpseclib's validator. On L2156, we have this code:
case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']:
The constant is indeed undefined, so the test really is whether a CA can match on the right cert particulars. The cert has this meta-data:
id-at-countryName = US
id-at-organizationName = DigiCert Inc
id-at-organizationalUnitName = www.digicert.com
id-at-commonName = DigiCert SHA2 High Assurance Server CA
And for all the current certs that would otherwise match, I have only these values in the latest cert bundle (i.e. all of the below would match if it was not for the common name DigiCert SHA2 High Assurance Server CA not being found):
id-at-commonName = DigiCert Assured ID Root CA
id-at-commonName = DigiCert High Assurance EV Root CA
id-at-commonName = DigiCert Assured ID Root G2
id-at-commonName = DigiCert Assured ID Root G3
id-at-commonName = DigiCert Global Root G2
id-at-commonName = DigiCert Global Root G3
id-at-commonName = DigiCert Trusted Root G4
Thus, the system does not even get at far as a digital signature check, since it cannot find the CA corresponding to this cert. What am I missing? This simple task should be a lot easier than this!
Possible reason: the Mozilla bundle is web server certs only
I have speculated that mail server certificates are not in the Mozilla bundle because a web browser would have no need for them. I would assume though that the certs on my GNU/Linux Mint install would be up-to-date and suitable for the purpose, since an operating system should be able to verify certs used in mail servers.
I therefore tried this code, which loads all the system certs into phpseclib:
$certLocations = openssl_get_cert_locations();
$dir = $certLocations['default_cert_dir'];
$glob = $dir . '/*';
echo "Finding certs: " . $dir . "\n";
$x509 = new \phpseclib\File\X509();
foreach (glob($glob) as $certPath)
{
// Change this so it is recursive?
if (is_file($certPath))
{
$ok = $x509->loadCA(file_get_contents($certPath));
if (!$ok)
{
echo sprintf("CA cert `%s` is invalid\n", $certPath);
}
}
}
// The 'getCertToTest' func just gets the live.com cert as a string
$data = $x509->loadX509(getCertToTest());
if (!$data)
{
echo "Cert is invalid\n";
exit();
}
$valid = $x509->validateSignature();
echo sprintf("Validation: %s\n", $valid ? 'Yes' : 'No');
Unfortunately this fails as well.
Confirm that my system certs are OK using openssl
I have issued this command on my system, and the remote TLS cert is verified OK. I don't know the phpseclib code well, but it doesn't look like it is doing any chaining, which is evidently necessary.
openssl s_client -connect smtp.live.com:25 -starttls smtp
CONNECTED(00000003)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert Cloud Services CA-1
verify return:1
depth=0 C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, CN = outlook.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=outlook.com
i:/C=US/O=DigiCert Inc/CN=DigiCert Cloud Services CA-1
1 s:/C=US/O=DigiCert Inc/CN=DigiCert Cloud Services CA-1
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIG/jCCBeagAwIBAgIQDs2Q7J6KkeHe1d6ecU8P9DANBgkqhkiG9w0BAQsFADBL
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSUwIwYDVQQDExxE
aWdpQ2VydCBDbG91ZCBTZXJ2aWNlcyBDQS0xMB4XDTE3MDkxMzAwMDAwMFoXDTE4
MDkxMzEyMDAwMFowajELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
(snipped, see other code block)
nGhseM2tJfwa2HMwUpuuo5029u4Dd40qvD0cMz33cOvBLRGkTPbXCFw24ZBdQrkt
SC5TAWzHFyT2tLC17LeSb7d0g+fuj41L6y4a9och8cPiv9IAP4sftzYupO99h4qg
7UXP7o3AOOGqrPS3INhO4068Z63indstanIHYM0IUHa3A2xrcz7ZbEuw1HiGH/Ba
HMz/gTSd2c0BXNiPeM7gdOK3
-----END CERTIFICATE-----
subject=/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=outlook.com
issuer=/C=US/O=DigiCert Inc/CN=DigiCert Cloud Services CA-1
---
No client certificate CA names sent
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
Requested Signature Algorithms: RSA+SHA256:RSA+SHA384:RSA+SHA1:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA1:DSA+SHA1:RSA+SHA512:ECDSA+SHA512
Shared Requested Signature Algorithms: RSA+SHA256:RSA+SHA384:RSA+SHA1:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA1:DSA+SHA1:RSA+SHA512:ECDSA+SHA512
Peer signing digest: SHA1
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3831 bytes and written 478 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: C11A0000050CD144CB5C49DD873D2C911F7CDDECFE18001F70FE0427C88B52F7
Session-ID-ctx:
Master-Key: 5F4EC0B1198CF0A16D19F758E6A0961ED227FCEBD7EF96D4D6A7470E3F9B0453A2A06AC0C1691C31A1CA4B73209B38DE
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1519322480
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
250 SMTPUTF8
I may drop phpseclib in favour of the binary command, but I would be relying on system/exec etc, which may not be available. Still, working sometimes is better than not working always!
Summary
Despite extensive work, I have reached a dead end on this. I will summarise here what I am wanting to do.
I want to use PHP to verify mail server SSL certificates against known public CAs. I don't know if the Mozilla certificates are appropriate to use for this, or whether I need to obtain them from elsewhere. I have found that my Linux Mint development machine has certificates that will verify the example mail server above.
The trivial strategy here is to use PHP 5.6+ and ensure all verification options are enabled in the stream context (though ideally, I wish to support 5.5 also). However, I want to do the proof myself, either using openssl_ functions or a library such as phpseclib, so I can see why a given cert is valid (or not). The openssl binary does this (as shown above) and it does so presumably using something very similar to PHP's openssl calls, but I don't know how it does so. For example, does the openssl binary use cert chain information to do this?
Another approach would be to read some information from a valid SSL session, but I cannot find anything in the manual to do that either.
The certificate is signed by an intermediate, which in this case is DigiCert SHA2 Secure Server CA. Intermediate certificates are not present in a root certificate list. Whatever library you're using, I believe you have to explicitly provide valid intermediate certificates for the validation process.
Here's an example using sop/x509 library.
// certificate from smtp.live.com
$cert = Certificate::fromPEM(PEM::fromString($certdata));
// list of trust anchors from https://curl.haxx.se/ca/cacert.pem
$trusted = CertificateBundle::fromPEMBundle(PEMBundle::fromFile('cacert.pem'));
// intermediate certificate from
// https://www.digicert.com/CACerts/DigiCertSHA2SecureServerCA.crt
$intermediates = new CertificateBundle(
Certificate::fromDER(file_get_contents('DigiCertSHA2SecureServerCA.crt')));
// build certification path
$path_builder = new CertificationPathBuilder($trusted);
$certification_path = $path_builder->shortestPathToTarget($cert, $intermediates);
// validate certification path
$result = $certification_path->validate(PathValidationConfig::defaultConfig());
// failure would throw an exception
echo "Validation successful\n";
This does signature validation and some basic checks per RFC 5280. It does not verify that CN or SANs match the destination domain.
Disclaimer! I'm the author of said library. It's not battle-proven and thus I'm afraid it won't fall into your "some other trusted library" category. Feel free to experiment with it however :).
I was able to get it to verify thusly:
<?php
include('File/X509.php');
$certs = file_get_contents('cacert.pem');
$certs = preg_split('#==(?:=)+#', $certs);
foreach ($certs as &$cert) {
$cert = trim(preg_replace('#-----END CERTIFICATE-----.+#s', '-----END CERTIFICATE-----', $cert));
}
unset($cert);
array_shift($certs);
$x509 = new File_X509();
foreach ($certs as $i => $cert) {
$x509->loadCA($cert);
}
$test = file_get_contents('test.cer');
$x509->loadX509($test);
$opts = $x509->getExtension('id-pe-authorityInfoAccess');
foreach ($opts as $opt) {
if ($opt['accessMethod'] == 'id-ad-caIssuers') {
$url = $opt['accessLocation']['uniformResourceIdentifier'];
break;
}
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$intermediate = curl_exec($ch);
$x509->loadX509($intermediate);
if (!$x509->validateSignature()) {
exit('validation failed');
}
$x509->loadCA($intermediate);
$x509->loadX509($test);
echo $x509->validateSignature() ?
'good' :
'bad';
Note the $test = file_get_contents('test.cer'); bit. That's where I loaded your cert. If I commented out $x509->loadCA($intermediate); the cert didn't validate. If I leave it in it does validate.
edit:
This branch does this automatically:
https://github.com/terrafrost/phpseclib/tree/authority-info-access-1.0
Unit tests still need to be added however it's not in the 2.0 or master branches yet either. I'll try to do work on that this weekend.
Example of how to use:
<?php
include('File/X509.php');
$certs = file_get_contents('cacert.pem');
$certs = preg_split('#==(?:=)+#', $certs);
foreach ($certs as &$cert) {
$cert = trim(preg_replace('#-----END CERTIFICATE-----.+#s', '-----END CERTIFICATE-----', $cert));
}
unset($cert);
array_shift($certs);
$x509 = new File_X509();
foreach ($certs as $i => $cert) {
$x509->loadCA($cert);
}
$test = file_get_contents('test.cer');
$x509->loadX509($test);
//$x509->setRecurLimit(0);
echo $x509->validateSignature() ?
'good' :
'bad';
It turns out I can fetch the whole of the certificate chain from the remote server - I have had to go through various false leads and dodgy assumptions to get to this point! Credit to Joe who pointed out, in the comments, that the context option capture_peer_cert only gets the certificate cert without any chain certificates that would complete the validation path to a public CA; to do that, one needs capture_peer_cert_chain.
Here is some code to do that:
$url = "tcp://{$domain}:{$port}";
$connection_context_option = [
'ssl' => [
'capture_peer_cert_chain' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
]
];
$connection_context = stream_context_create($connection_context_option);
$connection_client = stream_socket_client($url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $connection_context);
// timeout fread after 2s
stream_set_timeout($connection_client, 2);
fread($connection_client, 10240);
fwrite($connection_client,"HELO alice\r\n");
// let the server introduce it self before sending command
fread($connection_client, 10240);
// send STARTTLS command
fwrite($connection_client, "STARTTLS\r\n");
// wait for server to say its ready, before switching
fread($connection_client, 10240);
// Switching to SSL/TLS
$ok = stream_socket_enable_crypto($connection_client, TRUE, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);
if ($ok === false)
{
return false;
}
$chainInfo = stream_context_get_params($connection_client);
Then we can extract all the certificates using OpenSSL:
if (isset($chainInfo["options"]["ssl"]["peer_certificate_chain"]) && is_array($chainInfo["options"]["ssl"]["peer_certificate_chain"]))
{
$verboseChainCerts = [];
foreach ($chainInfo["options"]["ssl"]["peer_certificate_chain"] as $ord => $intermediate)
{
$chainCertOk = openssl_x509_export($intermediate, $verboseChainCerts[$ord]);
if (!$chainCertOk)
{
$verboseChainCerts[$ord] = 'Cannot read chain info';
}
}
$chainValid = checkChainAutomatically($x509Chain, $verboseChainCerts);
}
Finally, the function to do the check is here. You should assume that a good set of public certificates are loaded already, as per the question:
function checkChainAutomatically(X509 $x509, array $encodedCerts)
{
// Set this to true as long as the loop will run
$verified = (bool) $encodedCerts;
// The certs should be tested in reverse order
foreach (array_reverse($encodedCerts) as $certText)
{
$cert = $x509->loadX509($certText);
$ok = $x509->validateSignature();
if ($ok)
{
$x509->loadCA($cert);
}
$verified = $verified && $ok;
}
return $verified;
}
I tried verifying them in forward order, but the first one failed. I thus reversed the order, and they all succeeded. I have no idea whether certs are provided in chain order, so a very solid approach would be to loop through with two nested loops, adding any valid certs as a CA, and then continuing on the outer loop. This can be done until all certs in the list are confirmed as having a validated signature.

php - ssh2_exec failed allocating pty

Using PHP, I need to periodically execute a remote command on a Draytek 2925 router using SSH.
I can already connect to the router using the standard "ssh" command on Linux, but after authenticating I am getting "PTY allocation request failed on channel 0". However despite that message, I can still send commands.
When I try doing the same with PHP, I am getting 1 of 2 results and neither of which is returning the results I need.
My code is as follows:
<?
if (!($connection = ssh2_connect('my.router.local', 22)))
die('CONN');
else if (!ssh2_auth_password($connection, 'admin', 'password'))
die('AUTH');
else
{
$stream = ssh2_exec($connection, 'sys version');
stream_set_blocking($stream, true);
$stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
echo stream_get_contents($stream_out);
exit();
}
?>
The PHP fails at ssh2_exec(). If I specify the 3rd argument (pty), I get this result:
Warning: ssh2_exec(): Failed allocating 1 pty at 80x25 characters
However, not specifying the 3rd argument results in:
Warning: ssh2_exec(): Unable to request command execution on remote host
Even if I type #ssh2_exec(), I don't get a warning, but I don't get a result either.
If PHP is a no-go, I tried using sshpass in the same way, but that results in the following:
# sshpass -f sshpwd -v ssh admin#my.router.local 'sys version'
SSHPASS searching for password prompt using match "assword"
SSHPASS read: admin#my.router.local's password:
SSHPASS detected prompt. Sending password.
SSHPASS read:
exec request failed on channel 0
#
I ultimately just need to authenticate, send a command and return the result. Is there anything I am doing wrong or is there a simpler way?
I have resolved my query by using TELNET instead. Yes it's not secure like SSH, but since it's being used internally over the LAN, it doesn't matter.
For anyone interested, I used Net_Telnet and the following code:
<?php
require_once "Net/Telnet.php";
$router = 'my.router.local';
$password = 'password';
try
{
$t = new Net_Telnet($router);
$t->connect();
$t->login([
'login_prompt' => 'Account:',
'login_success' => ' User login successful, expired time is "Unlimited".',
'login_fail' => '% Access denied',
'login' => 'admin',
'password' => $password,
'prompt' => "{$router}>",
]
);
echo $t->cmd('sys version');
$t->disconnect();
// catch any buffered data
echo $t->get_data();
echo "\n";
} catch (Exception $e)
{
echo "Caught Exception ('{$e->getMessage()}')\n{$e}\n";
}
exit();
?>
Which results in:
# php -f telnet.php
sys version
Router Model: Vigor2925n Version: 3.8.4 English
Profile version: 3.0.0 Status: 1 (0x48245e4c)
Router IP: 0.0.0.0 Netmask: 255.255.255.0
Firmware Build Date/Time: Dec 15 2016 17:26:01
Router Name: my.router.local
Revision: 61327 V384_2925
>
#

How to get domain expires date using whois response data on php?

How to get domain expires date using whois response data on php ?
Normally i use whois response for check domain availability.
eg: check DRGDRGDRGRGDRG.COM if whois response data have word No match for it's mean this domain is available.
No match for domain "DRGDRGDRGRGDRG.COM".
But now i want to check domain expires date using whois response data like this
Domain Name: GOOGLE.COM
Registrar: MARKMONITOR INC.
Sponsoring Registrar IANA ID: 292
Whois Server: whois.markmonitor.com
Referral URL: http://www.markmonitor.com
Name Server: NS1.GOOGLE.COM
Name Server: NS2.GOOGLE.COM
Name Server: NS3.GOOGLE.COM
Name Server: NS4.GOOGLE.COM
Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited
Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited
Status: serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited
Status: serverTransferProhibited https://icann.org/epp#serverTransferProhibited
Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Updated Date: 20-jul-2011
Creation Date: 15-sep-1997
Expiration Date: 14-sep-2020
How can i do that ? thank you
This is not a simple question of parsing the response string as it might sound at the first moment because domain name registrars provide information in different format.
I guess we have two options here:
Use some library and when it fails parse the string
Do everything yourself, do not use any library and parse the output.
I suggest to start with some library but I really do not know the 'perfect' one. I'm going to try phpWhois. If it fails it gives raw data to try and parse it on our own.
First you need to install the library. I do this using Composer. Below is my composer.json file
{
"require": {
"phpwhois/phpwhois":"dev-master",
"mso/idna-convert": "0.9.1"
}
}
Note that latest phpWhois version does not work with latest idna-convert version, this is why I have to specify it in my requirements.
Execute composer install to download the libraries.
And finally PHP script to query the domain:
<?php
require(__DIR__ . '/vendor/autoload.php');
use phpWhois\Whois;
$whois = new Whois();
$whois->deepWhois = true;
$query = isset($argv[1]) ? $argv[1] : 'google.com';
$result = $whois->lookup($query);
$registered = isset($result['regrinfo']['registered']) && $result['regrinfo']['registered'] == 'yes';
if (!$registered) {
echo 'Domain: '.$query.' not registered.'.PHP_EOL;
} else {
if (isset($result['regrinfo']['domain']['expires'])) {
echo 'Domain: '.$query.PHP_EOL;
echo 'Expired: '.$result['regrinfo']['domain']['expires'].PHP_EOL;
} else {
echo 'Domain: '.$query.PHP_EOL;
echo 'Trying to find expires date...'.PHP_EOL;
foreach ($result['rawdata'] as $raw) {
if (strpos($raw, 'Expiry Date:') !== false) {
echo 'Expired: '.trim(explode(':', $raw)[1]).PHP_EOL;
}
}
}
}
It takes domain name as the first script argument $argv[1].
If the library does not parse the results from registrar we try to parse it manually. I've added a simple check
if (strpos($raw, 'Expiry Date:') !== false) {
echo 'Expired: '.trim(explode(':', $raw)[1]).PHP_EOL;
}
You might search for 'Expiration Date' in the raw response data or do some better logic based on your experience with parsing data if you will. Perfectly the library should do this but sometimes it fails.

WSO2 WS Security Password only without certificate possible?

we want to create a PHP WSO2 Webservice Client which uses WS Security, but without signature nor encryption. Instead we want to use a simple Password. Problem is: we always get an certificate error (see below). Do we really have to install a certificate, and if so: where ? Java Keystore ?
Environment: PHP 5.3.10, WSO2 PHP 2.10, Apache 2.2.x
wfs_client_log:
[error] key_mgr.c(295) [rampart][rampart_signature] Public key certificate file is not specified.
[error] rampart_signature.c(856) [rampart][rampart_signature] Cannot get certificate
[error] rampart_sec_header_builder.c(131) [rampart][shb] Signing failed. ERROR
[error] rampart_sec_header_builder.c(601) [rampart][shb] Asymmetric Binding failed
[error] rampart_out_handler.c(130) [rampart]Security header building failed.
[error] phase.c(224) Handler RampartOutHandler invoke failed within phase Security
[error] engine.c(657) Invoking phase Security failed
PHP Code is:
<?php
// Endpoint WebService
$endPoint = 'http://xxx.xxxx.xxx:7000/orabpel/selfservice/passwortAendernMBE/1.0';
// Security-Payload
$user = 'mustermann123';
$passwortAlt = 'foo';
$passwortNeu = 'bar';
// create Security-Token
$secToken = new WSSecurityToken(array(
"user" => $user,
"password" => $passwortAlt,
"passwordType" => "PlainText"));
// create SecurityPolicy
$policy = new WSPolicy(array(
"security" => array(
"useUsernameToken" => TRUE)));
// create WS-Client
$client = new WSClient( array(
"to" => $endPoint,
"useSOAP" => "1.1",
"action" => "process",
"policy" => $policy,
"securityToken" => $secToken));
// create SOAP-Payload
$soapPayload = '
<ns1:passwortAendern_processElement xmlns:ns1="http://xxxx.xxxx.xxxxxe/Integration/prozesse/xxxxxxSchema"
xmlns:ns2="http://xxxx.xxxx.xxx/types/xx.xxx.xxxx.selfService.prozesse.xxx.xxxxMessage">
<ns1:passwortAendernMessage>
<ns2:benutzerkennung>' . $user . '</ns2:benutzerkennung>
<ns2:passwortAlt>' . $passwortAlt . '</ns2:passwortAlt>
<ns2:passwortNeu>' . $passwortNeu . '</ns2:passwortNeu>
</ns1:passwortAendernMessage>
</ns1:passwortAendern_processElement>';
// Request
$soapResponse = null;
try {
// soap Request
$soapResponse = $client->request( $soapPayload );
// print out Response
echo '<pre>';
print_r(htmlspecialchars( str_replace('>','>'.PHP_EOL,$soapResponse->str ) ));
echo '</pre>';
} catch(Exception $e) {
echo '<h1>Error:</h1>' . PHP_EOL;
var_dump($e);
}
// dump Soap-Parameters
echo '<h1>Soap-Parameter</h1>' . PHP_EOL;
var_dump($soapPayload);
// dump Soap-Response
echo '<h1>Soap-Response</h1>' . PHP_EOL;
var_dump($soapResponse);
Finally successful! Calling the Webservice (with above mentioned vector/intent) now works.
Many attempts and another example by Nandika later we've found out that for us (Matthias and I) changing the creation of the WS-SecurityPolicy -object did the trick.
Instead of using above array as initialisation-parameter:
// create SecurityPolicy
$policy = new WSPolicy(array(
"security" => array(
"useUsernameToken" => TRUE)));
...we now use a xml-policy-file like so:
// load Policy (xml) file...
$policy_file = file_get_contents("policy.xml");
// ...and create SecurityPolicy
$policy = new WSPolicy($policy_file);
Content of "policy.xml":
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:ExactlyOne>
<wsp:All>
<sp:TransportBinding>
<wsp:Policy>
</wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens>
<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10 />
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
Beginning to use WSO2 with WS-Security WSF/PHP 2.1 feels rather touchy. Therefore I'll try to list some thoughts that knowing (would have) helped me save time:
most common error reflected to me by wsf/php is (an exception-object with the message): "Error , NO Response Received". This now happens almost whenever anything goes wrong, for example:
my request-xml (-structure) is not valid
a parameter has an incorrect type
the webservice throws an Exception (any exception really: be it because of a wrong WS-Security user/password, missing/unknown namespace, even some exceptions of type 'WS-Fault')
anything going wrong on the service-side really
a misconfiguration / configuration-change on php/wsf -side that disallowes anything relevant
network problems? (...not confirmed)
no request is sent by wso2 (for example when having trouble with TransportBinding -confinguration)
sometimes I do get a WS-Fault -ojbect (in a resopnse envelope) which I can check for my client-code [$e typeof WSFault]
always have a tool with proxy-capabilities nearby to route through + inspect your request and response. At the moment I use Oracles JDeveloper 10 and 11 which both have a neat little "HTTP Analyzer" inside (but there sure are smaller and/or better tools out there for this purpose).
Playing around with the settings in policy.xml a comrad and I found out that:
having a instead of the needed node (in your security_policy.xml) you'll get a WS-Fault: "policy requires authentication token"
having an empty node results in connection terminated in browser and wsf/php crashing (without signifficant error message - as far as i can see).
Thanks everyone (especially Nandika) for your help!
I've encountered the same issue once, but it was concerning WSServer, not WSClient. Since version 2.1 (or even earlier) WSF consideres WS-Policy signed by default, you need a WSPolicy file which declared signatureless behaviour. I've posted an article on this topic, but it's in Russian. Use Google Translate.
http://habrahabr.ru/post/132353/
The default generated policy is failing because, wsf/php 2.1.0 version is expecting signed messages for default generated policy for
$policy = new WSPolicy(array( "security" => array("useUsernameToken" => TRUE)));
Try with the following policy file.
https://svn.wso2.org/repos/wso2/trunk/wsf/php/samples/security/username_token/call_back/policy.xml
You can load the policy as
$policy_file = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_file);

Categories