Simple Way to Parse an x509 Certificate with PHP

Posted by Nessa | Posted in uncategorized | Posted on 03-11-2010

5

PHP has a nifty little function for parsing an x.509 SSL certificate into an array to easily pull out the elements: openssl_x509_parse .Essentially, all you need to do is load up the contents of the certificate, either through a file or POST value, and enclose it in the array. Here’s a simple script:

<?php
$sslcert = file_get_contents("/etc/ssl/certs/secure.v-nessa.net");
$sslcert = array(openssl_x509_parse($sslcert,TRUE));
//print_r($sslcert);
foreach ($sslcert as $name => $arrays){
foreach ($arrays as $title => $value){
if(is_array($value)){
echo $value . "\n";
foreach($value as $subtitle => $subvalue){
echo $subtitle . " : " . $subvalue . "\n";
}
}else{
echo $title .  "\n";
}
}
}
?>

The results are several multidimensional arrays, so depending on the data you need, you may need to keep adding foreach loops to get that data.

Using PHP to Perform DNS Lookups

Posted by Nessa | Posted in uncategorized | Posted on 30-06-2010

16

PHP has a couple DNS functions you can use to perform record lookups.

Most of us are familiar with the two basic ones – gethostbyname() and gethostbyaddr(), both of which perform a single function – returning a hostname or IP address. Here’s an example of both:

<?php

$ip = gethostbyname("v-nessa.net");
$host = gethostbyaddr("69.174.114.71");

echo "v-nessa.net has the IP $ip, which reverses to $host";
?>

The above will return:

v-nessa.net has the IP 69.174.114.71, which has a PTR of server.v-nessa.net

Similarly to gethostbyname, there’s gethostbynamel which is useful for hostnames with multiple A records:

$ips = gethostbynamel("test.v-nessa.net");
foreach ($ips as $ip => $value){
echo $value . "\n";
}

Will return:

69.174.114.71
69.174.115.243

A more advanced function is dns_get_record, which can pull any valid record for a hostname or IP.  Think about the dig command you use in Unix to find DNS records:

nessa@nessa-desktop:~$ dig +short v-nessa.net A
69.174.114.71

The dns_get_record function works in a similar way, and can obtain the following DNS record types:

DNS_A, DNS_CNAME, DNS_HINFO, DNS_MX, DNS_NS, DNS_PTR, DNS_SOA, DNS_TXT, DNS_AAAA, DNS_SRV, DNS_NAPTR, DNS_A6, DNS_ALL or DNS_ANY.

The following will give you a similar result:

$recs = dns_get_record("v-nessa.net", DNS_A);
print_r($recs);

Will return:

Array
(
[0] => Array
(
[host] => v-nessa.net
[type] => A
[ip] => 69.174.114.71
[class] => IN
[ttl] => 13728
)
)

If you want to obtain multiple DNS types, you can do so by separating the record types with a plus sign:

$recs = dns_get_record("v-nessa.net", DNS_A + DNS_MX);

Will return:

Array
(
[0] => Array
(
[host] => v-nessa.net
[type] => A
[ip] => 69.174.114.71
[class] => IN
[ttl] => 13736
)

[1] => Array
(
[host] => v-nessa.net
[type] => MX
[pri] => 0
[target] => v-nessa.net
[class] => IN
[ttl] => 14145
)

)

You’ll notice that the output is a double array, so to call individual values you can do either of the following:

// will return the IP for array 0 ( A record)

echo $recs[0]['ip'];

// will return results for common records

foreach ($recs as $type => $value){
echo $value[ip] . "\n";
}

Similar to the example above, you can use the DNS_ALL type to show any records available for the hostname, and use a minus sign to exclude certain record types. For example, the below code will return all DNS results for v-nessa.net, but exclude NS records:

$recs = dns_get_record("v-nessa.net", DNS_ALL - DNS_NS );

foreach ($recs as $type => $value){
echo $value[type] . "\n";
}

The following records were returned:

A
SOA
MX
TXT

Here’s a more practical example – a simple PHP DNS lookup script. Say you have a form on your site that allows a user to type in a hostname and select a type of record: A, MX, NS, and TXT. The passed form values are $host and $type, and the below script will accept the variables and output the results according to the record type:

HTML Form:

<form action="dns.php" method="POST">
Hostname: <input type="text" name="host" />
Type: <select name="type">
<option value="a">A</option>
<option value="mx">MX</option>
<option value="ns">NS</option>
<option value="txt">TXT</option> </select>
</form>

PHP Script (dns.php):

<?php

if(!empty($_POST['host']) && !empty($_POST['type'])){

    // Grab variable from form and define valid types

    $host = $_POST['host'];
    $type = strtoupper($_POST['type');
    $validtypes=array("A","MX","NS","TXT");

    // Check that dns type is defined or allowed

    if(!defined("DNS_" . $type) or !in_array($type,$validtypes)){
       echo "Invalid DNS Type!";
    }else{

       $type = constant("DNS_" . $type);
       $rec = dns_get_record($host, $type);

       // Set result types - can be modified by using available elements from $rec array

       switch($type){
             case DNS_A:
                    $recvals=array("Hostname" => "host","Type" => "type", "IP" => "ip");
                    break;
             case DNS_MX:
                    $recvals=array("Hostname" => "host","Type" => "type", "Target" => "target", "Priority" => "pri");
                    break;
             case DNS_NS:
                    $recvals=array("Hostname" => "host","Type" => "type", "Target" => "target");
                    break;
             case DNS_TXT:
                    $recvals=array("Hostname" => "host","Type" => "type", "Record" => "txt");
                    break;
        }

      // Output results

      foreach ($rec as $arr => $num){
             foreach ($recvals as $title => $value){
                    echo $title . " : " . $num[$value] . "\n";
             }
      }

    }
} else {

     echo "Either hostname or record type is missing";
}

It’s of course easy to modify the above code accordingly, and I’m sure there may be better ways to do this. Feel free to post your own code snippets or comments.

WHM APF Plugin

Posted by Nessa | Posted in uncategorized | Posted on 05-06-2008

19

I’m happy to announce my first ever perl-written plugin for WebHost Manager, which was developed by myself and one of my fellow sysadmins at InMotion Hosting. The first release is available here:

Download v.1.05

Download Now

To explain a little bit of background here, many hosting companies that give some sort of **** about security will leave SSH port 22 closed except for specified IPs. Us being one of them, requests from customers for us to add their IPs to their firewalls is rather redundant when we host over 900 V-dedicated systems and 100 Dedicated boxes where customers can opt for SSH access. Therefore, I decided that it would be well worth our efforts to create a WHM plugin to allow customers to add their own IPs without ever having to contact us. I am aware that some plugin developer already has a more sophisticated APF plugin for WHM but you have to pay for it.

Requirements:

- cPanel/WHM (tested on version 11.18)

- APF 0.9 (tested on 0.9.6)

- iptables enabled and working (if you are able to restart APF without any errors, it’s probably fine)

Note: default privileges will allow anyone with WHM/reseller access to use this plugin. You can manually change this in the addon_add2apf.cgi file if you want.

Installation:

cd /usr/local/cpanel/whostmgr/docroot/cgi
wget http://v-nessa.net/wp-content/scripts/apfadd_whm_1.05.tar.gz
tar -xvzf apfadd_whm_1.05.tar.gz
rm -f apfadd_whm_1.05.tar.gz

Simple enough?

From there, load up WHM and on the left side you’ll see an option to “Add IP to Firewall” and the plugin page will give you examples of what you can add. The script is set up to allow:

Hostname – v-nessa.net
Single IP – 205.134.252.71
IP CIDR – 205.134.252.71/24
Port/IP – d=22:s=205.134.252.71
Port/CIDR – d=22:s=205.134.252.71/24

Of course, the error checking is not perfect, so just be aware of what you’re adding or you might unintentionally ‘break’ your firewall, which usually results in blocked traffic.

Eventually I’m sure I’ll end up adding the ability to manage ports and remove IPs for users with a certain access level, but currently I don’t see a need to do so because I don’t believe that non-root users should have that type of access. Note that per the readme, you can edit the addon file to limit access to users with a certain reseller ACL privilege.

Convert Database to UTF-8

Posted by Nessa | Posted in uncategorized | Posted on 06-12-2007

15

We seriously see a ton of customers coming in with the type of databases that are a nightmare to move over. When you’re dealing with special characters in a database, you have to make sure that the charset and collation are dumped *with* the database, so that when you move it to another server the tables and data create properly. The biggest annoyance so far is converting tables back to UTF-8, as when this is done through the MySQL shell or phpmyadmin is had to be done table-by-table. So, I wrote this simple PHP script to do it all at once:

<?php
// Database info

$dbhost = 'localhost';
$dbuser = 'db_user';
$dbpass = 'password';
$dbname = 'db_name';

//---------------

header('Content-type: text/plain');

$dbconn = mysql_connect($dbhost, $dbuser, $dbpass) or die( mysql_error() );
$db = mysql_select_db($dbname) or die( mysql_error() );


$sql = 'SHOW TABLES';
$result = mysql_query($sql) or die( mysql_error() );

while ( $row = mysql_fetch_row($result) )
{
$table = mysql_real_escape_string($row[0]);
$sql = "ALTER TABLE $table DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci";
mysql_query($sql) or die( mysql_error() );
print "$table changed to UTF-8.\n";
}


mysql_close($dbconn);
?>

If course, you can adjust the ALTER TABLE statement to any character set and collation that you need.

MX Validation in PHP

Posted by Nessa | Posted in Uncategorized | Posted on 01-12-2007

9

Hosting companies have all kinds of tactics to keep spam away from their customers, but one very common complaint I get is the amount of spam coming in through contact forms. Even though we don’t allow ‘nobody’ mail through the php mail() function and we provide the best server-side spam filters available, local mail cannot be filtered or limited. In other words, no spam filter in the world is going to save you from your shitty contact form.

I started recommending to our customers to implement MX checks in their forms as spam bots nowadays can easily get past things like captcha and textual confirmations. Spammers rarely send email from valid mail hosts so it’s very easy to filter these out with just a few lines of code:

<?php
list($user, $domain) = split(“@”, $email);
if (checkdnsrr($domain, “MX”)) {
} else {
}

?>

To explain the code a little bit, you’re basically taking your stored email address variable ($email) and using the split() function to single out the domain name into one variable, $domain. When you pass the domain through the checkdnsrr() function, PHP will return either a ’1′ or ’0′ result, which is interpreted as either true or false. The above is just the basic code, but you can have it spit out errors as well:

if (checkdnsrr($domain, “MX”)) {
} else {
echo "Invalid email";
}

The checkdnsrr() function can also be used to check for other records as well, like A, CNAME, NS, etc.

Mass-Moving cPanel Accounts

Posted by Nessa | Posted in Uncategorized | Posted on 22-10-2007

15

If you’ve ever needed to move a ton of cPanel accounts from one server to another, you probably know the following:

  1. The multiple account move feature in WHM fails miserably
  2. It takes forever to do them one by one, esp. when you have other things to do

I figure the best way to mass-move accounts is by listing all the accounts in a file, then using a simple bash script to repetitively package all the accounts to they can be moved to another server.

First, you need to create a file (like users.txt) to list all the users you wish to move. If you’re only doing a few you can just go ahead and list the users in there manually, but if you’re doing like a whole server, you can get all the users in one file like so:

ls /var/cpanel/users > users.txt

You’ll then need to manually edit the users file and remove the ‘.’ and ”..’ entries, and any others that should not be included. The users.txt file should also be in a format like this:

user1
user2
user3

Next, you may want to set up an SSH key between servers so all the cpmove files and be transferred automatically. If you don’t know how to do this, here is a simple and straight-through tutorial.

Create a bash script called ‘cpbackup.sh’ with the following lines:

#! /bin/bash
for i in `cat users.txt`; do /scripts/pkgacct $i; done
scp -r /home/cpmove* root@server:/home
echo "Account backups done" > email.txt
mail -vv -s "Account backups done" your@emailaddress.com < email.txt


This will run cPanel backups up all the accounts listed in users.txt, scp them over to the new server, then email you when it’s done. Note that if you do not have SSH keys installed, you will have to manually type in the new server’s password when the script is ready to transfer the accounts over.

The on the new server you can use the same script and users.txt file, just change it around:

#! /bin/bash
for i in `cat users.txt`; do /scripts/restorepkg $i; done

This will restore all the user accounts that you copied over. From there, you’re all done!
One simple note: I’m not a shell programmer so I’m sure there are a lot better ways to accomplish this..if so, you can post them in a comment. The downside of using this method to move accounts is that all of the accounts are backed up in order so if you have a hundred some users, the backup files might be sitting on the server for hours until they are moved and restored, so you’ll want to make sure that your users are not making changes to their sites during the move process. Also, since the backup files are stored on the server it’s a good idea to figure out how much disk space you are going to need to avoid going over. You may be able to modify the script to do groups of 10-20 then delete the cpmove files if disk space is a concern.

Using PHP to Display Version Info – Part II

Posted by Nessa | Posted in Uncategorized | Posted on 17-10-2007

3

Earlier I showed some examples of commands you can use to pull software versions into a simple PHP script. In the next example I’ll show how to do the distro and kernel versions. I’m sure that there’s a much simpler way to do this (i.e. a simple cat command on the /proc/version file), but I figured I’d use a more advanced example in the form of a function. I did the distro as a variable to show how easy this is:


<?php
function kernelinfo () {
$buf = exec( 'cat /proc/version');
if ( $buf == "ERROR" ) {
$result = "N.A.";
} else {
if (preg_match('/version (.*?) /', $buf, $ar_buf)) {
$result = $ar_buf[1];


if (preg_match('/SMP/', $buf)) {
$result .= ' (SMP)';
}
}
}
return $result;
}


$distro = exec('cat /etc/redhat-release');


$kernel = kernelinfo();


echo $distro;


echo "<html><br></html>";


echo $kernel;

?>

This will show a result of the following, obviously various depending on your platform:

CentOS release 4.5 (Final)
2.6.9-023stab033.9-enterprise (SMP)

Keep in mind that this script (as the last) requires the ability to pass shell commands through PHP. So, you would need to be able to the exec function, or replace it with system, passthru, escapeshellcmd, or shell_exec if those are allowed in your php.ini configuration. Also, if you run PHP as an Apache module, this will not work if you have open_basedir protection enabled.

Using PHP to Display Version Info

Posted by Nessa | Posted in Uncategorized | Posted on 15-10-2007

3

I’ve been working on this application for work that does some simple server reporting, part of which involves displaying the versions of major software running on the machines. The importance of this to me personally is that since we have over 30 shared servers hosting multiple customers, we are continually moving websites between servers. Some of our older generation servers are still running MySQL 4.0 and PHP 4.3, so I need to be aware of this to make sure that customers are being moved to servers with compatible versions. It’s also good in tracking and planning upgrades.

I find it best to use the exec() function since it’s not blocked by most hosts. However, if you are on a shared host it’s very likely that certain PHP functions are disallowed in the php.ini. In that case you may be able to subsitute exec with system, passthru, escapeshellcmd, or shell_exec…unless those are blocked too. Then I guess you’re out of luck.

Start by creating some variables to store ordinary shell commands. If you wanted to find the php version from command line, you would usually type:

php -v

This will give a huge chunk of crap that you really don’t need if you’re making a simple version display script. In this case, you can use grep, awk, sed, and cut to trim down the output into a one-liner:

php -v |grep built |awk {‘print $2′}

The awk command prints out columns, so in the above example I’m printing out the second column of the line that contains the word ‘built’. Once the desired output is figured out, you simply assign it to a variable passed through exec() or a similar function:


$phpver = exec("php -v |grep built |awk {'print $2'}");

Then you can call the variable $phpver anywhere in your script:

echo "PHP Version: $phpver";

You can probably go through and figure out the commands to show other software versions on your server. In my script I’m showing the perl, php, mysql, apache, python, cpanel, and ruby versions. Here are the commands I used:

<?php

$perlver = exec("perl -v |grep linux |awk {'print $4'}|sed -e 's/v//'");
$phpver = exec("php -v |grep built |awk {'print $2'}");
$mysqlver = exec("mysql -V |awk {'print $5'} | sed -e 's/,//'");
$apachever = exec("apachectl -v |grep version |awk {'print $3 $4'}|sed -e 's/Apache\///'");
$pythonver = exec("python -V 2>&1 | sed -e 's/Python //'");
$cpanelver = exec("cat /usr/local/cpanel/version");
$rubyver = exec("ruby -v |awk {'print $2'}"); ?>

Then I just echoed out all the variables to display my version numbers:

See here .

Ugh….CIDR

Posted by Nessa | Posted in Uncategorized | Posted on 28-08-2007

0

I kinda wish I paid attention in class when the professor was talking about CIDR notations and shit…because it would have come in handy tonight. But being that I’m lazy I did some looking around and found that there’s a perl module out there that will convert an IP range to CIDR for you with a simple 4-line script. You’ll need to install the Net::CIDR module for this to work
If you’re on cPanel just type:

# /scripts/perlinstaller Net::CIDR

Here’s the script:

#!/usr/bin/perl

use Net::CIDR;

$range = shift;

print (join(“\n”, Net::CIDR::range2cidr(“$range”)) . “\n” );


Usage:

root@vps [~]# perl convert.pl 192.168.0.1-192.168.1.1
192.168.0.1/32
192.168.0.2/31
192.168.0.4/30
192.168.0.8/29
192.168.0.16/28
192.168.0.32/27
192.168.0.64/26
192.168.0.128/25
192.168.1.0/31

b2Evolution SMTP Patch

Posted by Nessa | Posted in uncategorized | Posted on 08-08-2007

6

Update (4/21/09) – This plugin is obsolete and is probably never going to be updated…use at your own risk!

I was debating whether or not to publish this since I wrote it mainly to target our customers, but I figured it might help someone who needs it.

Many hosting companies block PHP mail coming from the user ‘nobody’, which is a default if your host runs PHP as an Apache module. At the time of my writing, b2evolution does not support STMP, so I created a simple patch that will allow you to use phpMailer to send mail through SMTP instead of the send_mail() function that b2 uses.

Download b2evo_phpMailer-1.03.tar.gz (76k)

Requirements:

  • b2evolution 1.9.x or higher (tested with 1.9.3, but may work with other versions)
  • php 4.3.1 or higher (required by b2)
  • Valid SMTP server

Usage:

The instructions are specifically outlined in the readme.txt file within the archive, but it’s basically as simple as:

  1. Edit the ‘settings.php’ file in htsrv/phpmailer
  2. Upload the patched files to their corresponding locations on your webserver

And yea…that’s it.

Notes:

- This script was tested on a Linux platform running on cPanel, so the actual email setting and port numbers could be different on your server. If you are unsure about this, please ask your host.

- phpMailer does not explicity support SSL or TLS connections yet, so neither does this patch. You can essentially connect through any port as long as 1) your server runs SMTP on it and 2) it doesn’t require SSL or TLS.

I am not affiliated with b2evolution, so please do not contact me with questions about the software that do not pertain to the usage of this patch. This patch was mainly written to make b2evolution functional on some of our servers that block php mailing, and is not meant to be a major project. That being said, if you’re having difficulty getting my patch to work with your installation I would love to know about it, but please do not make your expectations too high. I have a life too =)