Category Archives: Bash

Integrating Open Directory and Google Apps (Natively Syncing Open Directory Passwords to Google Apps)

I have been reviewing for some time the different ways which are available to push password change updates from a Apple Open Directory (OpenLDAP) Master to our Google Apps domain, and I have waited for some time for a solution I could go with. However, I was and have been unsatisfied with the solutions which are available for OS X. I wanted a very simple, secure, and natively run solution – running on my snow leopard server or lion server. Simple is a major part here, while some people don’t mind getting into configs and changing them I wanted it to be, run a installer, answer some easy questions, and bam! So below are my personal requirements for this first round of development.

 

Integrating Google Apps with Open Directory Requirements

  1. Do NOT store a plaintext password on disk.
  2. No extra services (such as MySQL), I wanted to use a simple flat file structure, similar to svn, because it is one less thing to fail.
  3. Easily configurable, easy to extend, easy to archive, easy to remove.
  4. When a user changes their LDAP password it should change their Google Apps password.
  5. If a user sets a password on a LDAP account which does not exist in Google Apps, it should be created.
  6. It should work for multiple Google Apps Domains.
  7. Install it & forget about it.

So I wrote a simple series of bash scripts, and an easy installer & uninstaller to accomplish what I wanted. While this is my first iteration of this tool it is currently in the last stages of production deployment testing, and I believe this to be ready to be used by most. I tried reasonably well to make the setup easy and very straight forward. Everything that is needed for this to work is already on OS X Server or included in the installer.

This is designed to be installed on your ODM, while the installer is NOT fool proof – I believe it to handle the most common cases and setups without problem. I will be developing this more and maturing the features, but I am currently focusing on my own needs, so I would really like to hear what would be popular to add.

It makes heavy use of openssl for storing confidential information using public key cryptography, this also allows root to actually recover a password if the situation ever arises. The tools that are installed with this are required to be run by root as well as access to the flat file structure it uses to store information in, this is intentional so that it adds a measure of security as to who can access it. Because if someone has access to your ODM as root all bets are off anyway.

It also uses Apple’s native launchd instead of cron because of the discontinuing support of cron on Apple’s platform. I believe this is the easiest most straight forward solution I have come across for syncing passwords from OpenDirectory to Google Apps on OS X.

There are some conventions to follow in order for this to work properly and they are as follows:

  1. In each of the user accounts in OD make sure their full Google App email account is entered under the user info tab, it should be the only email address entered.
  2. In each of the Google App domains make sure you create the SAME domain admin user (the part before the @) with the SAME password.

All messages are printed to the system.log file, so watch this file if you want to see any errors or it just working. You might have to issue a -HUP or restart PasswordServer or ODM for changes to take effect, but I did not have to. Formal documentation will follow after the next release.

 

Installation is simple:

  1. Download latest zip file to Open Directory Master.
  2. Unzip file.
  3. Open Terminal
  4. Change to the setup directory inside the package (this is a must!).
  5. CD to the newly unzipped folder
  6. Run: sudo ./install.sh

googlePasswordSync Release Log:

CURRENT RELEASE: org.theObfuscated.googlePasswordSync

SPECIAL NOTE: Bugs should be filed under the issues section on GitHub at https://github.com/jjviscomi/googlePasswordSync/issues. Please include all the output from the logs and whatever else is necessary to help correct or identify the issue.

- Added Google Apps Directory Sync Integration Capability. (If you choose to it will now modify the users LDAP record to include a SHA hash of the password so that GADS can push that information to Google Apps.) However if you choose this option make sure you use DACL to prevent everyone from seeing this information.

Read more »

Removing ‘other’ Users Home Folders – in a mobile home folder environment

OS X Home FolderIf you have a large network that uses mobile home folders which allows multiple users to access any given machine, but each machine is assigned a primary user then this little addition to your login hook might be for you. Let me clarify when I say ‘Allow multiple users access’, I mean they can authenticate to a machine but normally DO NOT work on it. The ‘Primary User’ is the person who is expected to be logged into this machine the majority of the time, but say another user need to check their e-mail or get a document, or something where they need access for a limited time – then this little bit of code will remove any of their personal files when the ‘Primary User’ logs back in.

# Author: Joseph J. Viscomi    E-Mail: jjviscomi [at] gmail [dot] com || jviscomi [at] brehm [dot] org
# Date: 3/11/2011
# Description: This script can be included in a login hook when you want to remove all mobile or other home folders that
#              do not belong to the primary user of this computer. Mainly used to remove unwanted files and free disk space.
#              Once this script is applied in order for it to work correctly the first person that logs in must be the
#              'Primary User' of the computer.
#
#		Every time the 'Primary User' signs on to the computer it will remove all other home folders.
#
#              If you want to reset the 'Primary User' run the command - sudo rm -f /var/log/currentUser.log
#              then restart the computer and login as the 'Primary User'

#REMOVE HOME FOLDERS FOR NON-PRIMARY USERS
#CHECK TO MAKE SURE THE USER LOGGING IN HAS A HOME FOLDER
if [ -d "/Users/$1" ]; then
	#CHECKS TO SEE IF THE FILE THAT CONTAINS THE 'PRIMARY USER' EXISTS
	if [ -e "/var/log/currentUser.log" ]; then
		#CHECK TO SEE IF THE CURRENT USER IS THE 'PRIMARY USER'
		if [ `cat /var/log/currentUser.log` = $1 ]; then
                        #REMOVE ALL OTHER USER HOME FOLDERS - NOTE: DO NOT REMOVE Shared FOLDER, THIS CAN CAUSE PROBLEMS!
			#LOG THE OPERATIONS TO SYSLOG
			ls  /Users/ | grep -v `cat /var/log/currentUser.log` | grep -v Shared | xargs -I {} sudo rm -vrf /Users/{} >> /var/log/system.log
		fi
	else
		#IF THE FILE WHICH CONTAINS THE CURRENT USER DOESN'T EXISTS THEN THE PERSON THAT IS LOGGIN IN MUST THE 'PRIMARY USER'
		#RECORD THEIR USER NAME IN THE LOG FILE FOR FUTURE USE.
		sudo echo $1 > /var/log/currentUser.log
	fi
fi

The above script has been simplified for easy readability and commented to improve understanding of what is going on.

Tracking a Mac laptop over the internet (locating a stolen laptop)

This is an example of what you can achieve with just a little work, this is meant to provide more of an idea than an actual production piece of code (however it seems like a good place to start). So the problem I want to address is the ability to track Mac laptops in the wild, via the internet. I want to know where in the entire world and when my laptops are on line, even be able for them to fetch commands (future post) based on their status. This is ment to be simple I threw this idea together in about two hours (from start to finish) so not polished, but effective. This tutorial has a couple of distinct pieces you need:

  1. Client computers running os x, 10.6 in this case, but 10.5 should also work.
  2. A web server that is accessible over the internet and able to run PHP server side scripts.
  3. A mysql server accessible by the web server.

Lets begin by examining the client bash script that will send data to our web server, there are a few things to configure in it.

#!/bin/bash

# Author: Joseph J. Viscomi    E-Mail: jjviscomi [at] gmail [dot] com || jviscomi [at] brehm [dot] org
# Website: http://www.theobfuscated.org
# Date: 4/6/2011
# Description: This script when run will send information about itself to a webserver.
#              It can be used to track computers in the wild, on the internet. This is
#              the first of two files that will need to be installed on the clients.
#              For full functionality you will also need the server side code to handle
#              this script.
#              I have provided a basic verson of this for educational purposes only.
#              This is an attempt to show you what can be possible, this can be extended
#              to relay any information back to a server, and it can even be used for
#              two-way communications (post to follow).

# STEP 1.
#       CREATE A DIRECTORY ON THE CLIENT MACHINES: sudo mkdir /etc/.callHome/
#       CHANGE THE HOMEADDRESS TO POINT TO THE CORRECT DIRECTORY ON YOUR WEBSERVER WHERE THE callHome.php IS LOCATED
#       SET THE UNIQUE LOCATIONID - USE ONLY UPPERCASE AND NUMBERS, UPTO 64 CHARS
#       SAVE AND SET THE FILE TO EXECUTABLE, sudo chmod +x callHome.sh
#       PLACE THIS FILE IN /etc/.callHome/

# STEP 2.
#       PLACE THE org.theObfuscated.callHome.plist FILE IN THE /Library/LaunchDaemons/ DIRECTORY
#       IT IS SET TO CALL IN EVERY 10 MIN, THIS CAN BE CHANGED.

# STEP 3.
#       launchctl load /Library/LaunchDaemons/org.theObfuscated.callHome.plist

#THIS IS THE ADDRESS THE COMPUTER CALLS HOME TO, IP OR FQDN
# - CHANGE THIS TO REFLECT WHERE THE SERVER SIDE SCRIPT RESIDES
HOMEADDRESS="www.theobfuscated.org/callHome/"
#THIS IS THE NAME OF THE SERVER SIDE SCRIPT TO HANDLE THE REQUEST
SCRIPTNAME="checkin.php"

#THIS IS LIKE A SITE PASSWORD, JUST A BASIC SECURITY MEASURE - MAKE SURE IT MATCHES WHAT YOU HAVE IN THE DB
#EACH SITE SHOULD HAVE A UNIQUE LOCATIONID
LOCATIONID="SDJELK45689DSK"

#GET THE MAC ADDRESS OF THE MACHINE THAT IS CALLING HOME
LOCALMAC=`(/sbin/ifconfig en0 | awk '/ether/ { gsub(":", ""); print $2 }')`
#GET THE PUBLIC IP ADDRESS OF WHERE THIS MACHINE IS CURRENTLY CONNECTED TO THE INTERNET
EXTERNALIP=`(curl -s http://checkip.dyndns.org | awk '{print $6}' | awk ' BEGIN { FS = "<" } { print $1 } ')`
#GET THE SERIAL NUMBER OF THIS MACHINE
SERIALNUMBER=`ioreg -l | grep IOPlatformSerialNumber | awk '{ print $4 }' | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
#GET THE TIME IN DAYS SINCE THE LAST RESTART
UPTIMEDAYS=`(uptime | awk '{ print $3 }')`
#GET THE USERNAME OF THE CURRENT USER
CURRENTUSER=`whoami`
#GET THE CURRENT HOSTNAME
HOSTNAME=`hostname`

#CHECKIN SITE
HOMESITE="http://"$HOMEADDRESS"/"$SCRIPTNAME"?mac="$LOCALMAC"&hostname="$HOSTNAME"&user="$CURRENTUSER"&days="$UPTIMEDAYS"&ip="$EXTERNALIP"&serial="$SERIALNUMBER"&loc="$LOCATIONID

#GET THE RESPONSE FROM THE SERVER
HOMERESPONSE=`(curl -s $HOMESITE)`

#RECORD IN THE LOCAL LOG
echo "$HOMERESPONSE" >> /Library/Logs/callHome.log

The above script can be download here, for simplicity call it what we do, callHome.sh. The first thing you will need to edit the script at line # 32
HOMEADDRESS="www.theobfuscated.org/callHome/"
It should point to the directory on your web server where the server side script will reside. It is important to exclude the http:// but to include the trailing slash. Now depending on if you modify the name of the file on the server (I recommend you do not) you might need to change line # 34, this needs to be the file that will accept the client communications.

Line # 38 is the next important line, this is here for some simple security. This is a randomly generated “Location ID”, randomly type some upper case & numeric characters here (up to a maximum length of 64). However you have to write this down for a later step, we will refer to this as the LOCATIONID, this should not be shared with anyone else.

This is all the editing you have to do in this script. Save it to the following location: /etc/.callHome/callHome.sh
Make sure this script is executable (i.e. sudo chmod +x /etc/.callHome/callHome.sh). Just because we have this script now on the client computers doesn’t mean we are finished yet.

We want to make sure it will regularly contact our web server and register with it, and to do that we will run it as a Launch Deamon. To simplify this process I have created a plist file that will tell launchd to run this script every 10 minuets, it is displayed below:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>org.theObfuscated.callHome</string>
    <key>OnDemand</key>
    <true/>
    <key>StartInterval</key>
    <integer>600</integer>
    <key>ProgramArguments</key>
    <array>
      <string>/private/etc/.callHome/callHome.sh</string>
    </array>
  </dict>
</plist>

Line # 10 specifies how often the script should be run, in this case every 600 seconds. Feel free to change this, but I would suggest to wait until after you confirm this is working. This file should be called org.theObfuscated.callHome.plist , down load it here. This file needs to be saved in the following location: /Library/LaunchDaemons/org.theObfuscated.callHome.plist and then we just need to tell launchd to load it, however this should wait until we have our servers configured properly first.

 

Configuring the Server side PHP Recording Script & MySQL DB.

Copy  this file to the location you desire on your web server, it should be the same location and name as indicated earlier when configuring the client script.

<?php
/**
 * User: Joe Viscomi | jjviscomi [at] gmail [dot] com | www.theobfuscated.org
 * Date: 4/6/11
 * Time: 8:29 PM
 * Description: Simple PHP Script to handle reporting from clients.
 */

    //#CHANGE THESE SETTING TO REFLECT YOUR DATABASE
    $DBHOST="localhost";
    $DBUSER="root";
    $DBPASSWORD="root";
    $DBNAME="call_home";

    //#GET KEY VAULES TO AUTHENTICATE THE REMOTE COMPUTER
    $location_hash = $_GET['loc'];
    $mac_address = $_GET['mac'];
    $computer_serial = $_GET['serial'];

    //#CONNECT TO THE DATABASE
    $db_link = mysql_connect($DBHOST,$DBUSER,$DBPASSWORD);
    mysql_select_db($DBNAME, $db_link);

    //#THIS IS THE AUTHENTICATION QUERY
    $sql  = "SELECT `LOCATION`.`LOCATION_id`, `LOCATION`.`LOCATION_name`, `COMPUTER`.`COMPUTER_mac`, ";
    $sql .= "`STATUS`.`STATUS_name`, `STATUS`.`STATUS_id`, `COMPUTER`.`COMPUTER_id` ";
    $sql .= "FROM `LOCATION`, `COMPUTER`, `STATUS` ";
    $sql .= "WHERE `LOCATION`.`LOCATION_active` = '1' AND `LOCATION`.`LOCATION_hash` = '".$location_hash;
    $sql .= "' AND `COMPUTER`.`COMPUTER_mac` = '". $mac_address ."' AND ";
    $sql .= "`COMPUTER`.`LOCATION_id` = `LOCATION`.`LOCATION_id` AND ";
    $sql .= "`COMPUTER`.`COMPUTER_serial` = '". $computer_serial ."' AND `COMPUTER`.`COMPUTER_active` = '1' AND ";
    $sql .= "`COMPUTER`.`STATUS_id` = `STATUS`.`STATUS_id`";

    //#QUERY THE DB TO SEE IF THIS COMPUTER SHOULD EVEN CALL HOME
    $result = mysql_query($sql, $db_link);
    $computer = mysql_fetch_array($result);
    mysql_free_result($result);

    //IF $COMPUTER HAS A SIZE OF 12 THEN A RECORD EXISTS FOR THE REMOTE COMPUTER
    if(sizeof($computer) === 12){

        //GET LAST CONTACT TIME
        $sql  = "SELECT MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`) FROM `CONTACT-LOG` WHERE `CONTACT-LOG`.`COMPUTER_id` = '";
        $sql .= $computer['COMPUTER_id']."' AND `CONTACT-LOG_active` = '1'";
        $result = mysql_query($sql, $db_link);
        $last = mysql_fetch_array($result);
        mysql_free_result($result);

        //LOG THE REMOTE CONTACT
        $sql  = "INSERT INTO `call_home`.`CONTACT-LOG` (`CONTACT-LOG_id`, `COMPUTER_id`, `STATUS_id`, ";
        $sql .= "`CONTACT-LOG_hostname`, `CONTACT-LOG_user`, `CONTACT-LOG_uptime`, `CONTACT-LOG_ip`, ";
        $sql .= "`CONTACT-LOG_client`, `CONTACT-LOG_doc`, `CONTACT-LOG_active`) VALUES (NULL, ";
        $sql .= "'" . $computer['COMPUTER_id'] . "', '" . $computer['STATUS_id'] . "', '" . $_GET['hostname'];
        $sql .= "', '".$_GET['user']."', '".$_GET['days']."', '".$_GET['ip']."', '" .$_SERVER['REMOTE_ADDR'];
        $sql .= "', CURRENT_TIMESTAMP, '1')";
        mysql_query($sql);

        //GET LAST CONTACT TIME - NOW
        $sql  = "SELECT MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`) FROM `CONTACT-LOG` WHERE `CONTACT-LOG`.`COMPUTER_id` = '";
        $sql .= $computer['COMPUTER_id']."' AND `CONTACT-LOG_active` = '1'";
        $result = mysql_query($sql, $db_link);
        $current_date = mysql_fetch_array($result);
        mysql_free_result($result);

        //COMPUTER HAS ACCOUNT
        echo("[" . $_SERVER['SERVER_ADDR'] ."][" .$current_date['MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`)'] . "]:" .
             "Computer Account Found\r\n");
        echo("[" . $_SERVER['SERVER_ADDR'] ."][" .$current_date['MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`)'] . "][" .
             "STATUS]: " . $computer['STATUS_name'] . "\r\n");
        echo("[" . $_SERVER['SERVER_ADDR'] ."][" .$current_date['MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`)'] . "][" .
             "TIME OF LAST CONTACT]: " . $last['MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`)']);

    }else{
        echo("[" . $_SERVER['SERVER_ADDR'] ."][" .$current_date['MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`)'] . "]:" .
             "Computer Account Not Found\r\n");
    }

    mysql_close($db_link);

?>

Lines # 11-14 are the only lines you need to change here, they need to reflect the setting to access your mysql database server. Then save this file as stated earlier in the proper location in you web server this can be downloaded here in zip format.

The last step is to setup the MySQL server DB and table structure and populate it with the inventory of the computers it is suposed to keep track of. You can download the sql file that will create all the necessary tables and default values for you here. It is also displayed below for you to read if you wish, however the detail is not that important.

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- Database: `call_home`
--

-- --------------------------------------------------------

--
-- Table structure for table `COMPUTER`
--

CREATE TABLE `COMPUTER` (
  `COMPUTER_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `LOCATION_id` int(10) unsigned NOT NULL,
  `COMPUTER_serial` varchar(64) NOT NULL,
  `COMPUTER_mac` varchar(64) NOT NULL,
  `STATUS_id` smallint(5) unsigned NOT NULL DEFAULT '1',
  `COMPUTER_active` smallint(5) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`COMPUTER_id`),
  UNIQUE KEY `COMPUTER_serial` (`COMPUTER_serial`,`COMPUTER_mac`),
  KEY `COMPUTER_active` (`COMPUTER_active`),
  KEY `LOCATION_id` (`LOCATION_id`),
  KEY `STATUS_id` (`STATUS_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `COMPUTER`
--

-- --------------------------------------------------------

--
-- Table structure for table `CONTACT-LOG`
--

CREATE TABLE `CONTACT-LOG` (
  `CONTACT-LOG_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `COMPUTER_id` int(10) unsigned NOT NULL,
  `STATUS_id` smallint(5) unsigned NOT NULL,
  `CONTACT-LOG_hostname` varchar(128) NOT NULL,
  `CONTACT-LOG_user` varchar(128) NOT NULL,
  `CONTACT-LOG_uptime` smallint(5) unsigned NOT NULL,
  `CONTACT-LOG_ip` varchar(64) NOT NULL,
  `CONTACT-LOG_client` varchar(64) NOT NULL,
  `CONTACT-LOG_doc` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `CONTACT-LOG_active` smallint(5) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`CONTACT-LOG_id`),
  KEY `CONTACT-LOG_hostname` (`CONTACT-LOG_hostname`,`CONTACT-LOG_user`,`CONTACT-LOG_uptime`,`CONTACT-LOG_ip`,`CONTACT-LOG_client`),
  KEY `CONTACT-LOG_active` (`CONTACT-LOG_active`),
  KEY `COMPUTER_id` (`COMPUTER_id`),
  KEY `STATUS_id` (`STATUS_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `CONTACT-LOG`
--

-- --------------------------------------------------------

--
-- Table structure for table `LOCATION`
--

CREATE TABLE `LOCATION` (
  `LOCATION_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `LOCATION_hash` varchar(64) NOT NULL,
  `LOCATION_name` varchar(128) NOT NULL,
  `LOCATION_active` smallint(6) NOT NULL DEFAULT '1',
  PRIMARY KEY (`LOCATION_id`),
  UNIQUE KEY `LOCATION_hash` (`LOCATION_hash`),
  KEY `LOCATION_active` (`LOCATION_active`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Dumping data for table `LOCATION`
--

-- --------------------------------------------------------

--
-- Table structure for table `STATUS`
--

CREATE TABLE `STATUS` (
  `STATUS_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `STATUS_name` varchar(64) NOT NULL,
  `STATUS_active` smallint(6) NOT NULL DEFAULT '1',
  PRIMARY KEY (`STATUS_id`),
  KEY `STATUS_name` (`STATUS_name`,`STATUS_active`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `STATUS`
--

INSERT INTO `STATUS` VALUES(1, 'NORMAL', 1);
INSERT INTO `STATUS` VALUES(2, 'UPDATES REQUIRED', 1);
INSERT INTO `STATUS` VALUES(3, 'CRITICAL ERRORS', 1);
INSERT INTO `STATUS` VALUES(4, 'FORCE SHUTDOWN', 1);
INSERT INTO `STATUS` VALUES(5, 'STOLEN', 1);

Just make sure you create a database with the same name that is configured in the php file above (i.e. $DBNAME=”call_home”;). I suggest using a tool like phpMyAdmin to import the sql file and to make the following inserts, we will not cover this in this article, I will assume you can do/ figure this out:

INSERT INTO  `call_home`.`LOCATION` (
`LOCATION_id` ,
`LOCATION_hash` ,
`LOCATION_name` ,
`LOCATION_active`
)
VALUES (
NULL ,  'SDFA78ASDFA98F',  'MY HOME',  '1'
);

The insert above registers your LOCATIONID that was set on the client script, so replace SDFA78ASDFA98F with what ever you set earlier. Also Replace MY HOME with a description that is meaningful to your site location. Next we need to insert inventory of all the client computers this server can expect to hear from:

INSERT INTO  `call_home`.`COMPUTER` (
`COMPUTER_id` ,
`LOCATION_id` ,
`COMPUTER_serial` ,
`COMPUTER_mac` ,
`STATUS_id` ,
`COMPUTER_active`
)
VALUES (
NULL ,  '1',  'SERIALNUMBER',  'MACADDRESS',  '1',  '1'
);

Please note that you need to insert the serial number and the mac address for each Mac you want to track, you can get that by running the following commands respectively, then simply copy and paste it in place of  ’SERIALNUMBER’ and ‘MACADDRESS’:

ioreg -l | grep IOPlatformSerialNumber | awk '{ print $4 }' | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"
/sbin/ifconfig en0 | awk '/ether/ { gsub(":", ""); print $2 }'

That is all we have to do to prepare the MySQL Server to record the clients announcements. It is importiant that if you do not insert the correct values then the computer calls will NOT be recorded by the server.

You can view the results in phpMyAdmin or you can include this server side script to display a simple table of the latest requests from all registered clients (I know this is sloppy but, again it is just a proof of concept):

<?php
/**
 * User: Joe Viscomi | jjviscomi [at] gmail [dot] com | www.theobfuscated.org
 * Date: 4/6/11
 * Time: 8:41 PM
 * Description: To Display the latest results from all registered clients!
 */

    //#CHANGE THESE SETTING TO REFLECT YOUR DATABASE
    $DBHOST="localhost";
    $DBUSER="root";
    $DBPASSWORD="root";
    $DBNAME="call_home";

    $db_link = mysql_connect($DBHOST,$DBUSER,$DBPASSWORD);
    mysql_select_db($DBNAME, $db_link);

    $sql  = "SELECT MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`), `CONTACT-LOG`.`CONTACT-LOG_user`, `CONTACT-LOG`.`CONTACT-LOG_ip`, ";
    $sql .= "`CONTACT-LOG`.`CONTACT-LOG_client`, `CONTACT-LOG`.`CONTACT-LOG_hostname`, `CONTACT-LOG`.`CONTACT-LOG_uptime` FROM `CONTACT-LOG` ";
    $sql .= "WHERE EXISTS (SELECT * FROM COMPUTER WHERE `CONTACT-LOG`.COMPUTER_id = COMPUTER.COMPUTER_id ";
    $sql .= "AND COMPUTER_active = 1)";

    $result = mysql_query($sql,$db_link);

    $lastContactPage  = "<table class=\"sample\">\n";
    $lastContactPage .= "<tr>";
    $lastContactPage .= " <th>HOSTNAME</th>\n";
    $lastContactPage .= " <th>PUBLIC IP</th>\n";
    $lastContactPage .= " <th>CLIENT IP</th>\n";
    $lastContactPage .= " <th>USER</th>\n";
    $lastContactPage .= " <th>UP TIME (DAYS)</th>\n";
    $lastContactPage .= " <th>TIME OF LAST CONTACT</th>\n";
    $lastContactPage .= "</tr>";

    while($row = mysql_fetch_array($result)){
        $lastContactPage .= "<tr>\n";
        $lastContactPage .= "  <td>" . $row['CONTACT-LOG_hostname'] . "</td>\n";
        $lastContactPage .= "  <td>" . $row['CONTACT-LOG_ip'] . "</td>\n";
        $lastContactPage .= "  <td>" . $row['CONTACT-LOG_client'] . "</td>\n";
        $lastContactPage .= "  <td>" . $row['CONTACT-LOG_user'] . "</td>\n";
        $lastContactPage .= "  <td>" . $row['CONTACT-LOG_uptime'] . "</td>\n";
        $lastContactPage .= "  <td>" . $row['MAX(`CONTACT-LOG`.`CONTACT-LOG_doc`)'] . "</td>\n";
        $lastContactPage .= "</tr>\n";
    }

    $lastContactPage .= "</table>\n";

    mysql_free_result($result);
    mysql_close($db_link);

    echo "<html><head><title>Client Up Time</title>";
    echo "<style type=\"text/css\">" .
         "  table.sample {" .
	     "    border-width: 0px;" .
	     "    border-spacing: 2px;" .
	     "    border-style: none;" .
	     "    border-color: gray;" .
	     "    border-collapse: separate;" .
	     "    background-color: white;" .
         "  }" .
         "  table.sample th {" .
	     "    border-width: 0px;" .
	     "    padding: 2px;" .
	     "    border-style: inset;" .
	     "    border-color: gray;" .
	     "    background-color: rgb(250, 240, 230);" .
	     "    -moz-border-radius: ;" .
         "  }" .
         "  table.sample td {" .
	     "    border-width: 0px;" .
	     "    padding: 2px;" .
	     "    border-style: inset;" .
	     "    border-color: gray;" .
	     "    background-color: rgb(250, 240, 230);" .
	     "   -moz-border-radius: ;" .
         "  }" .
         "</style>";
    echo "</head>";
    echo "  <body>".$lastContactPage."</body>";
    echo "</html>";

That is it all we have to do now is enable the clients, or load our script to launchd. So on each of the clients run the following command:

launchctl load /Library/LaunchDaemons/org.theObfuscated.callHome.plist

That should be it … I have tested this, but in my hurry to put this together I might have included some errors in this post, please let me know so that I can correct them! I hope this provides a clear and clean example of how this all could work. Eventually I might post a more polished packaged solution that can be used by anyone.

Feel free to extend this or clean it up, and let me know so I can put it on the site.

Getting the size of a directory using bash

Sometimes as a system admin you would like to automate tasks based on how large a particular directory/folder is on a computer, or for a homework assignment in school. The very simple script below takes one argument, a path to the folder you would like to know the size of. It then will calculate the total size on the disk this folder and it contents take up and return in MB the folder size. It is a very simple script so I could see this being helpful to people new to bash scripting.

#!/bin/bash

# Author: Joseph J. Viscomi   E-Mail: jviscomi [at] brehm [dot] org || jviscomi [at] brehm [dot] org
# Date:   3/23/2011
# Arguments: Absolute path to a folder.
# Description: Prints the size of the folder in MB to standard out.
# Erros: Should handle all errors gracfully by returning -1.

# Checks to see that you gave it a directory and that it
# exists!
if [ $# = 1 ] && [ -d $1 ]
then
  du -sm $1 | cut -f 1
else
  # Echo -1 since NO folder can take up -1 M
  echo -1
fi

exit

How to reset an Open Directory Administrator’s password

Resting a Open Directory PasswordThis is a trivial task when you are a Directory Administrator, however what if you find yourself in a situation of when your admin has left or disappeared without notice and no one else has the directory admin password … or in my case: Something really bad happened to your Open Directory Master and the directory administrator can no longer access the directory.

In the latter some might say restore from backups, but that should be an absolute last resort, and in reality backups are good for data restoration NOT system states. Well the key here is you still have to have full (root) access to the Directory Master (locally). In plain english you need to retrive the slot-id for the directory administrator and change its db password hash. I think if I have to explain it all to you then you probably shouldn’t be the one in charge to remedy this situation or you will understand by just looking how to do it. So for both cases here is a little script changeDirectoryAdminPassword.sh.

#!/bin/bash

# Author: Joseph J. Viscomi    E-Mail: jjviscomi [at] gmail [dot] com || jviscomi [at] brehm [dot] org
# Date: 2/10/2010
# Description: This script is interactive, it prompts you for a new password. It
#              will get the slot-id for the given user and attempt to change it's
#              password. This needs to be run using sudo.

if [ $# = 1 ]; then
  echo "Going to change password for $1"
  echo -n "slot id: "
  echo `sudo mkpassdb -dump | grep $1 | awk '{ print $3 }'`
  sudo mkpassdb -setpassword `sudo mkpassdb -dump | grep $1 | awk '{ print $3 }'`
fi

The above script takes exactly one argument, the short name of the directory administrator.  If you follow the manual when creating your Open Directory Master it is labeled as diradmin (which should be changed …) so this command would be run like the following:

sudo ./changeDirectoryAdminPassword.sh diradmin

Simply follow it if you want to know what it is doing, line 13 is where the magic happens! I have never actually run this script so their might be a typo or something. If there is a problem let me know and I will correct it, but it looks good. I hope no one ever needs to deal with this.

Performance Optimization WordPress Plugins by W3 EDGE