I've been trying to figure out a way to validate DNS records in PHP (oa1) but have come up short.
I can validate a whole domain with this library, but not the individual records: https://github.com/metaregistrar/php-dnssec-validator
In addition, that library only allows for a very small set of TLDs to be validated.
Is there another library out there that can handle this for me, or perhaps something else I should look into?
I've also found this: http://www.phpclasses.org/package/9031-PHP-Validate-DNSSEC-keys-and-calculate-the-DS-record.html
But I have no idea how to get the keys to use in their validating function.
Help please!
UPDATE
So, I ended up using this...
exec('host -t RRSIG ' . $domain, $output);
Returns the RRSIG, or lack thereof, with minimal hassle.
The PHP engine has a fixed set of DNS record types it supports, all defined by the type parameter to dns_get_record. You can double check this list by looking in the engine code that implements DNS queries.
Unfortunately, none of the DNSSEC records are in that pre-defined list. So, you need to rely on a library or an external tool.
I'd use Net_DNS2, as it supports many DNSSEC RR. Example:
$google = new \Net_DNS2_Resolver(['nameservers' => ['8.8.8.8', '8.8.4.4']]);
$google->dnssec = true;
try {
$result = $google->query('kyhwana.org', 'SSHFP');
} catch(\Net_DNS2_Exception $ex) {
die($ex->getMessage());
}
foreach ($result->answer as $answer) {
if ($answer instanceof \Net_DNS2_RR_SSHFP) {
printf(
'%s %d %s %s %d %d %s' . PHP_EOL,
$answer->name,
$answer->ttl,
$answer->class,
$answer->type,
$answer->algorithm,
$answer->fp_type,
$answer->fingerprint
);
} else if ($answer instanceof \Net_DNS2_RR_RRSIG) {
printf('Signed by %s: %s' . PHP_EOL, $answer->signname, $answer->signature);
}
}
Aside: if your domain uses ECDSA algorithm or the SHA-256 fingerprint (like the example above), then you need the latest Net_DNS2 code which fixes Issue #39.
I haven't implemented the code yet but it looks to me like using php to verify the signatures themselves is a mistake. Use a local recursive resolver like unbound on the local server that enforces DNSSEC.
Then in php with the PEAR module Net_DNS2 look for an RRSIG record for the domain of interest. The result should tell you the zone responsible for that record.
Look for a DNSKEY record in that zone. If present, then look for a DS record.
The DS record will come from the parent zone (e.g. .email for deviant.email) and if present indicates DNSSEC is present for the zone.
If DNSSEC is present for the zone, then all the results for the zone are valid if your local recursive nameserver enforces DNSSEC.
I believe with Net_DNS2 that using
r = new Net_DNS2_Resolver(array('nameservers' => array('127.0.0,1')));
r->dnssec = true;
will also result in Net_DNS2 also validating the results if DNSSEC is active - which is indicated by the DS record that comes from the parent zone. But using a recursive resolver (preferably on the webserver) that enforces DNSSEC is probably safest.
Make sure the recursive resolver isn't listening on public interfaces so it doesn't get used in reflective amplification attacks.
Having the same goal (validate DNSSEC in PHP), i ended up with:
exec("drill -k root.keys -TDQ $domain SOA | grep -v '^;;'", $output);
This is doing a complete topdown DNSSEC validation.
You need root keys you can retrieve with:
unbound-anchor # NB: on FreeBSD, use local-unbound-anchor for "base" version
Or "manually":
dig . dnskey > root.keys
To confirm that the record is fully DNSSEC validated, check the last line of exec output:
[S] self sig OK This is not satisfying, probably zone key has not been populated in parent zone
[B] bogus
[T] trusted This means that everything is OK!
Related
I'm currently developing a snippet of code to create a libvirt KVM domain. Currently, the script is in working condition, but I'm unsatisfied with the way that I'm having to do things. As it stands, I'm having to load the domain XML using the PHP bindings, manually change it, then undefine and redefine the domain, all to change the domain type, and VNC address. I would like to know if there are any better alternatives to the way that I'm doing this below (The code that I'm not happy with is on lines 5 & 6). I feel like the PHP binding documentation is extremely lacking, which is why I haven't found a solution to this problem yet. I know that I should be able to do what I'm wanting to do because virt-manager can do it.
$vnc = "PGdyYXBoaWNzIHR5cGU9J3ZuYycgcG9ydD0nLTEnIGF1dG9wb3J0PSd5ZXMnIGxpc3Rlbj0nMC4wLjAuMCcgcGFzc3dkPSdfX1BBU1NfXyc+CiAgICAgIDxsaXN0ZW4gdHlwZT0nYWRkcmVzcycgYWRkcmVzcz0nMC4wLjAuMCcvPgogICAgPC9ncmFwaGljcz4="; // VNC config (base64)
$dom = libvirt_domain_lookup_by_name($c, $command['parameters']['name']); // Get domain resource
setBridge($dom, $command['parameters']['networks'][0]['mac']); // Replace network configuration with an interface bridge
setISO($dom, "/var/lib/libvirt/ISOs/Win7Pro.iso"); // Set the CD-ROM ISO
$xml = libvirt_domain_get_xml_desc($dom, null); // Get XML
domain_change_xml($c, $dom, str_ireplace("<domain type='qemu'>", "<domain type='kvm'>", str_ireplace("<graphics type='vnc' port='-1' autoport='yes'/>", str_ireplace("__PASS__", rand(10000000, 99999999), base64_decode($vnc)), $xml))); // Replace domain type and graphics block
libvirt_domain_create($dom); // Start the domain
function setBridge($dom, $mac) {
libvirt_domain_update_device($dom, "<interface type='bridge'><mac address=".escapeshellarg($mac)."/><source bridge='br0'/><model type='e1000'/></interface>", VIR_DOMAIN_DEVICE_MODIFY_CONFIG);
}
function setISO($dom, $iso) {
libvirt_domain_update_device($dom, "<disk type='file' device='cdrom'><driver name='qemu' type='raw'/><source file=".escapeshellarg($iso)."/><target dev='hdc' bus='ide'/><readonly/></disk>", VIR_DOMAIN_DEVICE_MODIFY_CONFIG);
}
Anyone know if/where there is documentation for valid ObjectList filter arrays?
The project's entry on github has a tiny blurb on it directing me to the API documentation, but that also fails to have a comprehensive list, and a search on 'filters' talks about containers only, not the object themselves.
I have a list of videos, each in four different formats named the same thing (sans filetype). Using the php-opencloud API, I want to GET only one of those video formats (to grab the unique filename rather than all its different formats).
I figured using a filter is the way to go, but I can't find any solid documentation.
Someone has got to have done this before. Help a noob out?
Most of the links on this page are dead now. Here's a current link to the php-opencloud documentation, which includes an example of using a prefix to filter the objectList results:
http://docs.php-opencloud.com/en/latest/services/object-store/objects.html#list-objects-in-a-container
I didn't find documentation of this, but apparently when the Rackspace Cloud Files documentation mentions arguments in a query string, those translate to arguments in an objectList method call like this:
GET /v1/MossoCloudFS_0672d7fa-9f85-4a81-a3ab-adb66a880123/AppleType?limit=2&marker=grannysmith
equals
$container->objectList(array('limit'=>'2', 'marker'=>'grannysmith'));
As Glen's pointed out, there isn't support (at the moment) for the service to apply filters on objects. The only thing which you might be interested in is supplying a prefix, which allows you to refine the objects returned based on how the filenames start. So if you sent 'bobcatscuddling' as the prefix, you'd get back all associated video formats for that one recording.
Your only option, it seems, is to get back all objects and iterate over the collection:
use OpenCloud\Rackspace;
$connection = new Rackspace(RACKSPACE_US, array(
'username' => 'foo',
'apiKey' => 'bar'
));
$service = $connection->objectStore('cloudFiles', 'DFW', 'publicURL');
$container = $service->container('CONTAINER_NAME');
$processedObjects = array();
$marker = '';
while ($marker !== null) {
$objects = $container->objectList('marker' => $marker);
$total = $objects->count();
$count = 0;
while ($object = $objects->next()) {
// Extract the filename
$filename = pathinfo($object->name, PATHINFO_FILENAME);
// Make sure you only deal with the filename once (i.e. to ignore different extensions)
if (!in_array($processedObjects, $filename)) {
// You can do your DB check here...
// Stock the array
$processedObjects[] = $filename;
}
$count++;
$marker = ($count == $total) ? $object->name : null;
}
}
What you'll notice is that you're incrementing the marker and making a new request for each 10,000 objects. I haven't tested this, but it'll probably lead you in the right direction.
Unfortunately, the underlying API doesn't support filtering for objects in Swift/Cloud Files containers (cf. http://docs.rackspace.com/files/api/v1/cf-devguide/content/List_Objects-d1e1284.html). The $filter parameter is supported as part of the shared code, but it doesn't actually do anything with Cloud Files here.
I'll see if I can get the docs updated to reflect that.
Our policy for people who are terminated/separated or who go on leave of absence involves a handful of changes to their AD account for record keeping purposes and security. One of these changes is renaming the account (login name, display name and dn) to a value that includes the original name with the help desk ticket number appended.
I have been able to use ldap_rename() to change the active directory "name" attribute, thus changing the DN. I can change the displayName attribute using either ldap_modify() or ldap_mod_replace(). What I cannot seem to do is change the samAccountName using any of these. Below is the core of the code I'm using. The errors I get are dependent upon which function I use, and are listed below.
I know there are some nuances to using PHP LDAP with Active Directory, but I find it hard to believe that I have been able to do everything up to and including changing passwords and I can't change the samAccountName... help?
<?php
$connection=ldap_connect(domain.local,389);
ldap_set_option($connection,LDAP_OPT_PROTOCOL_VERSION,3);
ldap_set_option($connection,LDAP_OPT_REFERRALS,0);
ldap_start_tls($connection);
ldap_bind($connection,$username,$password);
$accountName=$_POST["accountName"];
$ticketNumber=$_POST["ticketNumber"];
$baseDn="dc=domain,dc=local";
$attribs=array("samaccountname","dn","name","displayname","description","info","memberof");
$search=ldap_search($connection,$baseDn,"(samaccountname=".$accountName.")",$attribs);
$result=ldap_get_entries($connection,$search);
// ldap_modify returns error 80: Internal (implementation specific) error.
foreach ($result as $account) {
$newValues=array("samaccountname"=>$account["samaccountname"][0]."-".$ticketNumber)
ldap_modify($connection,$account["dn"],$newValues);
}
// ldap_mod_replace returns error 80: Internal (implementation specific) error.)
foreach ($result as $account) {
$newValues=array("samaccountname"=>$account["samaccountname"][0]."-".$ticketNumber)
ldap_mod_replace($connection,$account["dn"],$newValues);
}
?>
So yeah, what is it I'm supposed to be doing to make this happen?
The "implementation specific" error message you're receiving means that your sAMAccountName is invalid because it doesn't meet specific AD restrictions on it. The sAMAccountName attribute cannot be more than 20 characters and cannot contain any of the following: " [ ] : ; | = + * ? < > / \ ,. It might be helpful to see an example username with the ticket number appended.
In Subversion 1.6, there was an .svn directory in every working copy directory. I could use the following code to quickly retrive the current revision number without the need for shell access/execution.
public function getSubversionRevision() {
if(file_exists("../.svn/entries")) {
$svn = File("../.svn/entries");
return (int)$svn[3];
}
return false;
}
Subversion 1.7 breaks this code. There is now only one .svn directory per local repository. There is an entries file in this directory but it no longer has anything useful for me. It looks like everything I need is now in a SQLite database. Specifically wc.db. I suppose I could use PHP's SQLite functions to get the info I need, but this sounds a little too expensive to run on every (or close to every) page load.
Any ideas? Breaking out the exec function and hoping that Subversion binaries are installed (and in the $PATH!) is a last resort. To summarize, I need to find a way to locate the .svn directory at the root of the repository (which could be different depending on your checkout location) and then somehow parse a file in there (probably wc.db) in a cost-effective way.
try using a SVN library SVN as it will give you access to repository information and more functionality over SVN repository.
Take a look at function svn_status you will receive and array of svn repository information
Array (
[0] => Array (
[path] => /home/bob/wc/sandwich.txt
[text_status] => 8 // item was modified
[repos_text_status] => 1 // no information available, use update
[prop_status] => 3 // no changes
[repos_prop_status] => 1 // no information available, use update
[name] => sandwich.txt
[url] => http://www.example.com/svnroot/deli/trunk/sandwich.txt
[repos] => http://www.example.com/svnroot/
[revision] => 123 // <-- Current Revision
//..
)
)
You can write a script to svn update for you every time, and scrap the revision number from the update's output into a file. The following bash script should more or less do it:
#!/bin/bash
svn --non-interactive update ..
svn --non-interactive update .. | perl -p -e "s/.* revision ([\d]*)\./\$1/" > ../version.phtml;
I implemented a clumsy (but relatively thorough) implementation of Subversion 1.7 revision retrieval in TheHostingTool. Check the commit message for an explanation. The function is contained in class_main.php which is one directory after /trunk (/trunk/includes/class_main.php). You'll need to adjust relative paths for your specific needs. This is a slightly modified version of the function found in class_main.php for use elsewhere.
public function getSubversionRevision() {
// This will work for Subverson 1.6 clients and below
if(file_exists("../.svn/entries")) {
$svn = File("../.svn/entries");
return (int)$svn[3];
}
// Check the previous directories recursively looking for wc.db (Subversion 1.7)
$searchDepth = 3; // Max search depth
// Do we have PDO? And do we have the SQLite PDO driver?
if(!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
$searchDepth = 0; // Don't even bother...
}
for($i = 1; $i <= $searchDepth; $i++) {
$dotdot .= '../';
if(!file_exists("$dotdot.svn/wc.db")) {
continue;
}
$wcdb = new PDO("sqlite:$dotdot.svn/wc.db");
$result = $wcdb->query('SELECT "revision" FROM "NODES" WHERE "repos_path" = "'.basename(realpath('..')).'"');
return (int)$result->fetchColumn();
}
if($this->canRun('exec')) {
exec('svnversion ' . realpath('..'), $out, $return);
// For this to work, svnversion must be in your PHP's PATH enviroment variable
if($return === 0 && $out[0] != "Unversioned directory") {
return (int)$out[0];
}
}
return false;
}
Instead of .= '../' you may want to use str_repeat if you don't start at 1. Or, you could simply define $dotdot ahead of time to wherever you would like to start.
Or just query:
SELECT "changed_revision" FROM "NODES" ORDER BY changed_revision DESC LIMIT 1;
if you wonder the last revision of the whole repo! :)
Excuse me, but if you want portable and bullet-proof solution, why not call Subversion CLI inside PHP?
You can use svn info inside any directory of WC (capture, parse output and get a set of data) of svnversion if you want to get only global revision
Once I've identified identified the email addresses of my list segment (using get_emails() custom function, I am setting up my list segment as follows:
$batch = get_emails();
//now create my list segment:
$api->listStaticSegmentAdd(WEDDING_LIST_ID, 'new_wedding_guests');
$api->listStaticSegmentMembersAdd(WEDDING_LIST_ID, 'new_wedding_guests', $batch);
//do I build vars for a campaign?
$options = array (
'list_id' => WEDDING_LIST_ID, //What value id's my list segment?
'subject' => 'Alpha testing.',
'from_email' => 'wedding#juicywatermelon.com',
'from_name' => 'Pam & Kellzo',
'to_name' => $account->name,
);
From here can I use a basic campaign and send it?
$content['text'] = "Some text.";
$content['html'] = get_link($account);
$cid = $api->campaignCreate('regular', $options, $content);
$result = $api->campaignSendNow($cid);
I'm not sure if I'm understanding the api documentation correctly. I also tried 'list_id' => 'new_wedding_guests'; which failed to create a campaign.
Thanks!
I'll assume this is test code and just make the cursory mention of how you probably don't need to be creating a new Static Segment every time. However, your call to add members is not going to work. Per the listStaticSegmentMembersAdd documentation, you should be passing the static segment id, not the name of it. Also note that the docs cross-reference themselves when input params can come from other calls - that parameter there is a good example (it also happens to be returned by listStaticSegmentAdd).
Your options for campaignCreate look like a good start. The documentation for it has examples below - those examples are included in the PHP MCAPI wrapper you likely downloaded. As per above, the list_id you need is the one for the list you used in the listStaticSegment calls (also linked in the documentation).
Now the real key - further down in the campaignCreate docs is the segment_opts parameter - that is how you control segmentation. Follow the link it gives you and you'll find tons of info on the ways you can do segmentation, including using a static_segment.
Hopefully all of that made sense, if not, take a step back and check out these links (and play with segmentation in the app), then it should:
Introduction to MailChimp List Management
How can I send to a segment of my list?
Our Release Info on how Static Segments are used