Simple AFP Forensics using Access Logs

Mac OS X Server’s AFP server access logs aren’t the greatest (no full paths is a glaring omission), but if you have them enabled, they can be useful for finding who deleted a file or folder for example.

If the item’s name starts with “Important File”, this command gives us the ip address of the client that deleted the item :
file_server:~ root# grep -i "Delete Important File*" /Library/Logs/AppleFileService/AppleFileServiceAccess.log
IP 10.1.21.6 - - [08/Jul/2008:14:26:14 -0500] “Delete Important File 2009.xls” 0 0 0

Then we pass the ip address into this command to give us the login of the user:
file_server:~ root# grep 10.1.21.6 /Library/Logs/AppleFileService/AppleFileServiceAccess.log | grep Login
IP 10.1.21.6 - - [08/Jul/2008:09:05:43 -0500] “Login mpickens” 0 0 0

Finally we can use dscl to lookup the full name the user:
file_server:~ root# dscl localhost read /Search/Users/mpickens RealName
RealName: Pickens, Mary Ellen

Older logs are available too in zipped form. Use gunzip -c to read the contents.
file_server:~ root# gunzip -c '/Library/Logs/AppleFileService/AppleFileServiceAccess.log 12.11.07.gz' | grep Login | grep mpickens
IP 10.1.21.143 - - [14/Dec/2007:19:12:38 -0500] “Login mpickens” 0 0 0
IP 10.1.21.143 - - [14/Dec/2007:19:24:32 -0500] “Login mpickens” 0 0 0
IP 10.1.21.143 - - [17/Dec/2007:09:21:38 -0500] “Login mpickens” 0 0 0
IP 10.1.21.143 - - [17/Dec/2007:10:37:49 -0500] “Login mpickens” 0 0 0

get parent process from ps

While booted from NetRestore I couldn’t figure out what was launching Terminal.app. Finally found the script that was launching it using an option the ps to show the parent process.

Simple, but useful.

ps auxww -o ppid

It was rc.install.

Fix for slow AD logins/joins caused by macAddress query

I’ve been hassling Apple about this issue for quite a while.

Apple has two workarounds for this problem:

1.) Index the macAddress attribute in AD. Even though the macAddress is not part of the Computer class by default, the AD plugin queries on it for joins ( to ensure the the computer that you are adding doesn’t already exist ), and for MCX ( managed client information ). Normally I would frown on any changes to AD since the Enterprise doesn’t like making changes to their infrastructure just to support Macs. However, supposedly in Windows 2008 Server, the macAddress attribute is indexed by default, so at least their is some justification there.

2.) If you’d prefer to make changes on your client machines rather than bother your AD administrators with a Mac-specific fix, remove the ENetAddress mapping from /Library/Preferences/ActiveDirectory.plist. The lines to remove look like this:

<key>1.3.6.1.1.1.1.22</key>
<string>dsAttrTypeStandard:ENetAddress</string>

The key is the OID for the macAddress attribute in AD.
The string value is the mapping to a native Open Directory attribute, which Apple calls ENetAddress.

Update:
You’ll also need to remove /Library/Preferences/DirectoryService/ActiveDirectoryDynamicData.plist as this file also contains the cached mappings.

Then killall -9 DirectoryService or reboot the machine.

Notes on Leopard AD Plugin 10.5.3

All of the bugs listed here are fixed in 10.5.3.

What isn’t fixed is the slow logins on AD environments with an R2 schema that hasn’t been extended with Mac attributes.

Again, please file bug reports if you are experiencing this issue.

dockutil 1.0 released

As a Mac sysadmin, I’ve had the need to manipulate the dock on hundreds of systems at a time.

I used to cobble together terrible shell scripts to do the job, but now thanks to plistlib and python, plist manipulation is really easy. I am releasing this utility free under the Apache 2.0 license. Hopefully some other sysadmins will find it useful.

dockutil is a command line utility for managing Mac OS X dock items.
It can add, replace, list, move, find, and delete dock items. It supports Applications, Folders, Stacks, and URLs. It can act on a specific dock plist or every dock plist in a folder of home directories.
It is compatible with Mac OS X Tiger and Leopard.

Download dockutil here.

Here is the usage information:

usage: dockutil -h
usage: dockutil –add (path to item) | (url) [--label (label)] [ folder_options ] [ position_options ] [ plist_location_specification ]
usage: dockutil –remove (dock item label) [ plist_location_specification ]
usage: dockutil –move (dock item label) position_options [ plist_location_specification ]
usage: dockutil –find (dock item label) [ plist_location_specification ]
usage: dockutil –list [ plist_location_specification ]

position_options:
–replacing (dock item label name) replaces the item with the given dock label or adds the item to the end if item to replace is not found
–position [ index_number | beginning | end | middle ] inserts the item at a fixed position: can be an position by index number or keyword
–after (dock item label name) inserts the item immediately after the given dock label or at the end if the item is not found
–before (dock item label name) inserts the item immediately before the given dock label or at the end if the item is not found
–section [ apps | others ] specifies whether the item should be added to the apps or others section

plist_location_specifications:
(path to a specific plist) default is the dock plist for current user
(path to a home directory)
–allhomes attempts to locate all home directories and perform the operation on each of them
–homeloc overrides the default /Users location for home directories

folder_options:
–view [grid|fan|list|automatic] stack view option
–display [folder|stack] how to display a folder’s icon
–sort [name|dateadded|datemodified|datecreated|kind] sets sorting option for a folder view

Examples:
The following adds TextEdit.app to the end of the current user’s dock:
dockutil –add /Applications/TextEdit.app

The following replaces Time Machine with TextEdit.app in the current user’s dock:
dockutil –add /Applications/TextEdit.app –replacing ‘Time Machine’

The following adds TextEdit.app after the item Time Machine in every user’s dock on that machine:
dockutil –add /Applications/TextEdit.app –after ‘Time Machine’ –allhomes

The following adds ~/Downloads as a grid stack displayed as a folder for every user’s dock on that machine:
dockutil –add ‘~/Downloads’ –view grid –display folder –allhomes

The following adds a url dock item after the Downloads dock item for every user’s dock on that machine:
dockutil –add vnc://miniserver.local –label ‘Mini VNC’ –after Downloads –allhomes

The following removes System Preferences from every user’s dock on that machine:
dockutil –remove ‘System Preferences’ –allhomes

The following moves System Preferences to the second slot on every user’s dock on that machine:
dockutil –move ‘System Preferences’ –position 2 –allhomes

The following finds any instance of iTunes in the specified home directory’s dock:
dockutil –find iTunes /Users/jsmith

The following lists all dock items for all home directories at homeloc in the form: item(tab)path(tab)(section)tab(plist)
dockutil –list –homeloc /Volumes/RAID/Homes –allhomes

Notes:
When specifying a relative path like ~/Documents with the –allhomes option, ~/Documents must be quoted like ‘~/Documents’ to get the item relative to each home

Bugs:
Names containing special characters like accent marks will fail

Contact:
Send bug reports and comments to kcrwfrd at gmail.

Shell Injection with AppleScript’s do shell script

AppleScript’s do shell script capability is immensely useful, but if you are sending variable data to do shell script, always validate input and use quoted form of variableName. See the following example:

set dialogResult to (display dialog "Enter a directory name to pass to ls:" default answer ";say boo" buttons {"Cancel", "Quoted Form", "Raw"})

if button returned of dialogResult is “Quoted Form” then
try
set theCommand to “ls ~/” & quoted form of text returned of dialogResult
display dialog “Will execute:” & return & theCommand & return & “Proceed?”
do shell script theCommand
end try
else
try
set theCommand to “ls ~/” & text returned of dialogResult
display dialog “Will execute:” & return & theCommand & return & “Proceed?”
do shell script theCommand
end try
end if

Note you’ll have to fix the quotes to standard double quotes to get this to compile. I couldn’t get wordpress to cooperate.

10.5 AD plugin slow logins related to macAddress query

The slow login times in the Leopard AD plugin seem to be related to a search by macAddress.  If you killall -USR1 DirectoryService, and login on a Leopard machine bound to AD, you’ll notice a query on macAddress in the /Library/Logs/DirectoryService/DirectoryService.debug.log. I am not sure the purpose of this query, but our computer objects don’t even use the macAddress attribute, so the query always results in no records found.

I can manually execute the same query and the time almost perfectly matches the delay I see with logins; about 45 seconds.

time ldapsearch -v -w password -x -h domaincontroller.domain.forest.com -D username@domain.forest.com -b "DC=domain,DC=forest,DC=com" "(&(objectCategory=cn=computer,cn=schema,cn=configuration,dc=forest,dc=com)
(macAddress=00:1a:22:ee:31:ac))”

Just substitute your own domain, forest, domain controller, username, password, and mac address etc to test.

I’ve tried manually mapping macAddress to another attribute, but it didn’t make a difference, so I don’t have any workaround to offer. Adding the macAddress attribute to your computer objects in AD might speed things up, but I have not tested this.  I’ve notified Apple of the issue in radar 5752763, which is marked as a Duplicate of 5679705.  If you see this macAddress query taking a long time, please report this to Apple so it can get fixed sooner rather than later.  Actually, this same query is used during the join process, which may explain the long join times while it searches for an existing computer.

Play mp3 from python on Mac

This site has a few nice little PyObjC samples.
One of them shows how to play a sound using AppKit’s NSSound. This is exactly what I was trying to do today.

And since PyObjC is built-in in 10.5, no addtional software is required.

Python is already batteries included, with PyObjC, you’ve got batteries galore.

ipython output:

In [1]: from AppKit import NSSound
In [2]: sound = NSSound.alloc()
In [3]: sound.initWithContentsOfFile_byReference_(’/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/TransitionSection.bundle/Contents/Resources/intro-sound.mp3′, True)
Out[3]:
In [4]: sound.play()
Out[4]: True
In [5]: sound.stop()
Out[5]: True

waiting for root device: could be duplicate UUIDs

If you run into waiting for root device when booting in verbose mode or you get a flashing question mark, etc, it could be that your volumes have duplicate UUIDs. The UUID is the primary means that is used to locate the boot volume to root from. If you have duplicate UUIDs, your Mac may not be able to determine which volume to boot from. If you suspect this may be the case, boot from another disk or CD and run diskutil list to get a list of all volumes. Then run diskutil info on each to get the UUIDs. Check for duplicates.

kserver:~ pbuffer$ diskutil list
/dev/disk0
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *74.5 Gi disk0
1: EFI 200.0 Mi disk0s1
2: Apple_HFS int_1 19.8 Gi disk0s2
3: Apple_HFS int_2 19.8 Gi disk0s3
4: Apple_HFS int_3 34.5 Gi disk0s4

kserver:~ pbuffer $ diskutil info disk0s2 | grep UUID
Volume UUID: A1F5866F-8286-359A-B19F-58910918AC5A
kserver:~ pbuffer$ diskutil info disk0s3 | grep UUID
Volume UUID: A1F5866F-8286-359A-B19F-58910918AC5A
kserver:~ pbuffer $ diskutil info disk0s4 | grep UUID
Volume UUID: CBB0B1F8-07D5-3BFC-9B50-4F99033B01D6

If you do find a duplicate, you can generate a new UUID and set it using the following command:

unmount the disk first

diskutil unmountDisk disk0

/System/Library/Filesystems/hfs.fs/hfs.util -s disk0s2

substitute your own device for disk0s2

Once you’ve done this, re-bless the volume.

Duplicate UUIDs shouldn’t happen, but somehow it happened to one of our Xserves with a hardware raid card.

No creation dates from stat

I was surprised to learn that creation dates are not included in stat output. I really thought stat would include something so basic. You get time of last access, time of last data modification, and time of last file status change, but creation date isn’t in there.

On the Mac, this info is stored in the resource fork. You can get it from the command line using GetFileInfo which is installed when you install Apple’s free Developer Tools.

/Developer/Tools/GetFileInfo -d path/to/file

In Applescript you can use info for:

creation date of (info for (choose file))