Greg`s Tech blog

My technical journal where I record my challenges with Linux, open source SW, Tiki, PowerShell, Brewing beer, AD, LDAP and more...

Signing Powershell scripts

Friday 02 of October, 2009
I've recently begun writing powershell scripts. I'm a bit late to this game, but better late than never. As the PS team did a great job of ensuring PS scripts were secure by default, I want to do the right thing and sign all my scripts rather than weaken the security setting.

That's easy to do. We have a CA on our domain and I signed up for a code signing cert from the server. I then wrote a small function to sign my scripts and added to my profile. It looks like this:
function signIt {
	Set-AuthenticodeSignature $args[0] @(Get-ChildItem cert:\CurrentUser\My -codesigning)[0]

To sign a script you can enter this at a PS prompt:
signit c:\mycode\mypsscript.ps1

All well and good, right?

OK, so that worked fine. Several weeks later, I created a new script and when I tried to sign it I would get an "unknown error" saying the "data is invalid". It took a fair amount of googling with Bing to find no answer. I turned to the MS news groups and found and answer from Robert Robelo. He said this:

"It's the encoding.
By default PowerShell's ISE encodes a new script in BigEndian Unicode.
PowerShell can't sign BigEndian Unicode encoded scripts. (Oops!)
So, for any new script you create -or any created before- through the ISE that you want to sign, open it and set the encoding you prefer.
Besides BigEndian Unicode, the other valid values are:

Sure enough, looking at the file encoding using Notepad++, I could the file encoded as UCS-2 BigEndian. I used Notepad++ to convert the file to UTF-8 and I was able to successfully sign the script. Hat's off to Robert for the tip. I'm documenting it here so others may find it easier.


Stopping Akonadi in KDE 4.2

Saturday 05 of September, 2009
I loaded Slackware 13 on a laptop I use mostly for remote administration and other casual uses. At every startup, Akonadi also starts up; the progress bar jumps on top and stays there for 30 seconds and its generally make a nuisance of itself. Since I'm not likely to use this laptop as a PIM, I poked around to get it to stop.

- First thing, by default, "Korganizer" runs in the tray for reminder notification. I right-clicked and disabled notifications.

- Next I looked at KResources in the "KDE Setting"s applet and made sure none of them we associated with an Akonadi store.

- Finally, I stopped the kres-migrator from running (and trying to convert my non-existant data into Akonadi) using this command:
kwriteconfig file kres-migratorrc group Migration key Enabled type bool false

Printing to the Lexmark e260dn from Linux

Saturday 29 of August, 2009
Found a great tip on how to print to this printer here (cache). Here's my attempt.

I have the e260dn attached to a WinXP desktop that has IPP printing enabled.
- Download the PPD file for the e352dn from here (cache) and save it to a file

- From within the Cups Printer mgmt page select Add Printer
- Specify the name and other details, click Continue
- For Device select Appsocket/HPJetdirect, click continue
- In the URI field enter http://printserver/printers/printersharename/.printer replacing printserver with the WinXp computername and printersharename with the windows printer share name.
- upload the PPD file saved in step one to the CUPS server in the 'Provide a PPD file' box, click Add Printer

Slackware 13 & Broadcom wireless

Monday 10 of August, 2009
What a pain this was to figure out. Here are my notes

- Read this link (cache)

- You need to install and build two packages from Slackbuild.org
-- b430-fwcutter
-- b43-firmware

-- run b43-fwcutter-012/b43-fwcutter -w ".lib/firmware wl_apsta_mimo.o

-- install wicd from /extra


Cisco VPN Client problem

Monday 11 of May, 2009
My company uses Cisco VPN 5.x and Alladin eTokens for 2 factor authentication.
I was getting this error trying to use my token for vpn:
Error 32: unable to verify certificate.

Turning up logging in the VPN client dug up some more detail that said “Cert chain missing”

So I opened the certificate manager in IE (tools, Internet options, Content, Certificates). It listed my certificate in there under Personal.
I viewed the cert and it was listed as invalid.
Under Certification Path it showed the cert chain was failing for the Root CA.
There was, conveniently, an Import button. I pressed it and voila, the Root CA cert was imported.

I was then able to successfully login using the token.

(Note: If it matters, we have an Enterprise Root CA and an Intermediate CA in our network. All certs are issues from the intermediate)

Clean up old computer accounts

Wednesday 06 of May, 2009
We needed a way to delete aging computer accounts from AD. This script uses the DS* tools from MS (included in Win2k3, Win2k8 and Vista).

  • You need to specify the root OU and directories for the email tool.
  • You need to specify the inactive timer (currently 12 weeks)
  • You need to set the search limit (currently 100 accounts
  • To make it take action, you must call it with a parameter of 'Prod' else it will run in test (no delete mode)
  • Any computer account that has the string !!Do Not Delete!! in the description will not be deleted.
  • Any computer account with child objects (e.g. virtual server hosts) will not be deleted.
  • the script uses blat to send email with results. You can rip that out by commenting out the line 'goto :-SendReport'

Please leave a comment should you make use of this tool.

@echo off
:: FindAgingCompAccts - GjM - 5/1/09
:: Uses MS tools (dsquery, dsget, dsmod) to locate inactive accounts and disable them
:: Computer accounts with !!Do Not Delete!! in the Description will not be disabled.
:: set blatbin, dsbin, SCRIPT_DIR, & Mode before running

::blatexe is directory containing blat
Set blatexe=c:\netadmin\bin\blat.exe
:: dsbin is location of dsquery & other tools (leave blank if in path)
:: dsbin is location of dsquery & other tools (leave blank if in path)
set dsbin=
::SCRIPT_DIR is location of this script - created dynamically based on calling location
set SCRIPT_DRV=%~d0
set SCRIPT_DIR=%~p0
echo scriptdir: %SCRIPT_DIR%
set LogDir=%SCRIPT_DIR%logs
set TempDir=%SCRIPT_DIR%temp
set DataDir=%SCRIPT_DIR%data
set OldAcct=No value assigned\oldacct.txt
set logfile=No value assigned\Oldcomp.log
set actlog=No value assigned\action.log
set inactlog=No value assigned\inaction.log
set errlog=No value assigned\error.log
set resultfile=%TempDir%\results.log
set tempout=%TempDir%\temp.log

set RootOU="DC=corp,DC=com"

:: Call batch file with PROD as a parameter in order to disable accounts
set MODE=%1
set MODE=Test
echo The script must be called with a parameter of 'Prod' in order to_
change accounts (ex: 'FindAgingCompAccts Prod')
echo Mode is: %MODE%
set SKIP_FLAG=!!Do Not Delete!!
set ISFlagged=0

::for search_limit use 0 to find all inactive accounts
set Search_Limit=100

::Cleanup previous session
copy action_history.log+action.log action.tmp
del action_history.log
ren action.tmp action_history.log

copy error_history.log+error.log error.tmp
del error_history.log
ren error.tmp error_history.log

del No value assigned
del No value assigned
del No value assigned
del No value assigned

set ActCount=0
set SkipCount=0
set PrevCount=0
set ErrCount=0
set count=0

::cd %WORK_DIR%

::query AD for inactive accounts
echo %Date% %Time% Starting automatic account maintenance to clean inactive computer accounts
echo %Date% %Time% Starting automatic account maintenance to clean inactive computer accounts >>No value assigned
echo Querying inactive accounts
echo %Date% %Time% >%OldAcctNo value assigneddsbin%dsquery computer %RootOU% -inactive %INACTIVE_PERIOD% -limit %Search_Limit% 1>%OldAcct% 2>dsquery.err
if No value assigned NEQ 0 goto :ERR

::Count inactive accounts
for /f "delims=?" %%a in (%OldAcct%) do set /a count+=1 >nul
echo Inactive accounts to process: No value assigned

::This is the main script loop
::Loop through the list of inactive accounts and check their status
for /f "delims=?" %%a in (%OldAcct%) do call :ChkUserStatus %%a
goto :-SendReport
goto :EOF

:: Check description for flag that tells us not to disable
:: Disable account if not flagged
::echo on
set CN=%1
echo %CN%
if %CN%=="" goto :EOF
for /f "delims=: tokens=2" %%b in ('No value assigneddsget computer -desc -q -L
"') do (
:: %%b contains the description from AD. This line uses findstr to look for the FLAG in the description
echo "%%b" |findstr /i /c:"%SKIP_FLAG%" >nul
:: findstr returns errorlevel 1 if no match is found
call :-DeleteAcct %CN%
) ELSE (
call :-SkipAcct %CN%
goto :EOF

::Delete the account
if %MODE%==Prod (
echo Trying to delete computer account: %CN% >> No value assigned
echo Trying to delete computer account: %CN%
set /a ActCount+=1
for /f "tokens=2 delims=: " %%c in ('dsrm
" -noprompt -subtree 2
&1 ^|findstr "failed" ') do (
if /i %%c EQU failed (
echo Error deleting %CN%
echo Error deleting %CN% >>No value assigned
set /a ErrCount+=1
set /a ActCount-=1
) else (
echo Computer account deleted: %CN% >> No value assigned
echo Computer account deleted: %CN%
set /a ActCount+=1
) else (
echo Mode is %MODE% - not deleting, %CN% >>No value assigned
echo Mode is %MODE% - not deleting, %CN%
set /a TestCount+=1
goto :EOF

::Log accounts not being disabled
echo Account flagged, skipping computer, %1 >>No value assigned
echo Account flagged, skipping computer, %1
set /a SkipCount+=1
goto :EOF

echo Mode is: %MODE%
echo DeletedAccounts: %ActCount%
echo FlaggedAccounts: %SkipCount%
echo ErrorAccounts: %ErrCount%
echo Test Accounts: %TestCount%

echo Mode is: %MODE% >>No value assigned
echo Deleted Accounts: %ActCount% >>No value assigned
echo Flagged Accounts: %SkipCount% >>No value assigned
echo Error Accounts: %ErrCount% >>No value assigned
echo Test Count: %TestCount% >>No value assigned

echo See inaction.log at \\exchmonitor\c$%SCRIPT_DIR% >>No value assigned
type %SCRIPT_DIR%\usagenote.txt >> No value assigned
if %Mode%==Prod No value assigned No value assigned -tf %SCRIPT_DIR%\recips.txt -subject_
"Computer account maintenance" -attacht %WORK_DIR%\results.txt -attacht_
No value assigned -attacht No value assigned -server smtpint.corp.com -f _
goto :EOF

:: Checks user disable flag and sets ISDIS to 1 if disabled
for /f "delims=: tokens=2" %%c in ('dsget user -disabled -q -L %1') do (
for %%e in (%%c) do (
if %%e==yes (
set ISDIS=1
) else (
set ISDIS=0
goto :EOF

echo Error retrieving inactive computer accounts >>No value assigned
echo No value assigned >>No value assigned
echo Error retrieving inactive computer accounts
type %SCRIPT_DIR%\usagenote.txt >>No value assigned
No value assigned No value assigned -tf %SCRIPT_DIR%\recips.txt -subject "Error with _
computer account maintenance" -attacht No value assigned -attacht No value assigned -server _
smtpint.corp.com -f AccountManagers@corp.com _
goto :EOF


A better nagios interface

Tuesday 03 of March, 2009
I'm always hacking at something. Today I turned my 3.0.3 Nagios interface into something more exciting. I converted it to the Nuvola style (cache) which I think looks great. Especially compared to the plain and aged Nagios interface.

While playing with the interface, I had the idea of adding a countdown timer until page refresh so I went looking for a way to do it.

First I found a javascript timer at hashemian.com (cache) (thanks Robert). I copied the countdown.js file to the root of my web server to save him bandwidth.

Nagios will include customer header or footer files if you create them in the right place and name them correctly. I created a file called status-footer.ssi in the nagios/ssi directory. The contents of the file are as follows:

<script language="JavaScript">
TargetDate = new Date();
TargetDate.setSeconds(TargetDate.getSeconds() + 90);

BackColor = "ltgray";
ForeColor = "dkgray";
CountActive = true;
CountStepper = -1;
LeadingZero = true;
DisplayFormat = "Status refresh in: %%M%% minute %%S%% seconds.";
FinishMessage = " Refresh!";
<div class='countdown'>
<script language="JavaScript" src="/countdown.js"></script>

This code is a modified version of what Robert posted to his blog. The first two lines set the countdown time to now plus 90 seconds which is the default refresh time for the pages.

I then

Searching with Index Service

Tuesday 10 of February, 2009
Sorry I'm so late to this party, but...

We have an intranet site that uses Frontpage's search webbot. It stopped working and because I didn't want to learn how it did work, I decided to implement the search using Windows Index service and .asp pages. Here's how I did it

The problem:
  • Documents are separated into to high level groups, In house and home workers. Separate catalogs exists for these document trees
  • Searches must be restricted to documents in the tree
  • Documents consist of a collection of .htm files contained within a particular subdirectory within the tree
  • a search from a given document should only return hits from that document

  • Create a search form to be added to the index page of each document
  • Have the form post to a results.asp page within the same directory as the document
  • Use the server variable SCRIPT_NAME to capture the directory of the current document
  • Use the directory to limit the scope of the search
  • Use an include statement in results.asp to call the real search script

The search form

Note: This is an html file stripped of html. Sorry for the confusion, but this is a well know problem and you can find the info other places.

form action="results.asp" method=post
Search text:
input type=text name="searchstring" size="50" maxlength="100" value=" "
Ex: 'meter percent' will return documents with either meter or percent
Ex: 'meter & percent' will return documents with both meter and percent
button type=submit>Submit
button type=reset>Clear Form

(Properly formatted html file with this in the body)
#include virtual="/_Search/search.asp"

The Search.asp file
Again, any html is stripped. In particular you have to provide the table definition for the response.write statements. contact me if you need details.

' search.asp - Greg Martin - feb 2009
' uses index service to provide search function for Reedman
' search.asp is meant to be 'included' in results.asp.  In fact, it 
' relies on the fact that it is called through results.asp located in the directory of the document that need to be searched 
' so that the current directory can be read from the "SCRIPT_NAME server variable
' This section sets the various configuration variables

Dim formscope, pagesize, maxrecords, searchstring
dim catalogtosearch, searchrankorder, origsearch
dim q, filepath, CRLF, rs
Dim lsPath, arPath, CurPath

pagesize = 500

' we have two catalogs - one for homeworkers and one for Prod
' here we select the catalog based on the location fo the results page
if instr(Request.ServerVariables("SCRIPT_NAME"),"homeworker") > 0 then
	response.write "Searching eHomeworker catalog"
	response.write "Searching Production catalog"
end if

CRLF = Chr(13) & Chr(10)

if trim(searchstring) = "" then

   response.write "No search terms entered"

	' this code effectively removes the filename and leaves the path
	' it also replaces / with \ so the regex search works
	lsPath = Request.ServerVariables("SCRIPT_NAME")
	arPath = Split(lsPath, "/")
	arPath(UBound(arPath,1)) = ""
	CurPath = Join(arPath, "\")
	'This section performs the query
	set q=server.createobject("ixsso.query")
	' this query restricts results to docs in the filepath using regex & #path
	filepath= "*" & CurPath & "*"
	q.query=searchstring & " & #path " & filepath
	q.columns="doctitle, filename, size, write, rank, directory, vpath, path"
	'Displays the results
	set rs=q.createrecordset("nonsequential")
	response.write"<p>Your search for <b>" & origsearch & "</b> produced "
	if rs.recordcount=0 then response.write "no results"
	if rs.recordcount=1 then response.write "1 result: "
	if rs.recordcount>1 then response.write(rs.recordcount) & " results: </p>"
	response.write "I cannot post this code"
	do while not rs.EOF
	   response.write 	"I cannot post this code
	response.write ""
end if

set rs=nothing
set q=nothing
set util=nothing

Substrings in Windows batch scripts

Thursday 22 of January, 2009
I needed a way to convert the contents of the %DATE% variable (which looks like: Thu 01/22/2009) into mmddyy format. The windows SET command can do this.
 Set Shortdate=%DATE:~4,2%%DATE:~7,2%%DATE:~12,2% 

So, let's disect it by looking at a smaller portion first. (Remember that Windows command shell variables are reference as No value assigned. When you use the Set command, the variable name can have modifiers)
Set Shortdate=%DATE:~4,2% 

%DATE% is the variable we are searching.
%DATE:~4 says to start at offset of four from the beginning of the string
,2% says return two characters.
 Set Shortdate=%DATE:~4,2%
 echo %Shortdate% 

Would print: 01 (assuming it is January in the US)
So we could string this mechanism together like this
 Set Shortdate=%DATE:~4,2%
 Set shortdate=%shortdate%%DATE:~7,2%
 Set shortdate=%shortdate%%DATE:~12,2% 

or really shorten the process as shown in the first example

MythTV Time

Friday 26 of December, 2008
I'm building my own PVR. Lots of reasons why, but the biggest is the hassle of trying to continue using the VCR. So in the the new century I come - again..

I have a Dell GX280 that I won at a work give-away. 2GB RAM, 2.4GHz Pentium-4.
Video out - Diamond HD 2600xt Sb Edition (ATI Radeon)
Video in - A loaner that I'm not sure of.

I installed Mythbuntu 8.10 without incident.
I found this in the dmesg log

 Linux video capture interface: v2.00
[   10.024756] ivtv:  Start initialization, version 1.4.0
[   10.024846] ivtv0: Initializing card #0
[   10.024855] ivtv0: Unknown card: vendor/device: 4444/0016
[   10.024861] ivtv0:               subsystem vendor/device: 1002/ffff
[   10.024867] ivtv0:               cx23416 based
[   10.024871] ivtv0: Defaulting to Hauppauge WinTV PVR-150 card
[   10.024875] ivtv0: Please mail the vendor/device and subsystem vendor/device IDs and what kind of
[   10.024880] ivtv0: card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)
[   10.024885] ivtv0: Prefix your subject line with [UNKNOWN IVTV CARD].
[   10.024977] ivtv 0000:04:00.0: PCI INT A -> GSI 16 (level, low) -> IRQ 16
[   10.026646] tveeprom 0-0050: Huh, no eeprom present (err=-6)?
[   10.026652] tveeprom 0-0050: Encountered bad packet header [00]. Corrupt or not a Hauppauge eeprom.
[   10.026658] ivtv0: Invalid EEPROM
[   10.462996] cx25840 0-0044: cx25  0-21 found @ 0x88 (ivtv i2c driver #0)
[   10.469463] wm8775 0-001b: chip found @ 0x36 (ivtv i2c driver #0)
[   10.472660] wm8775 0-001b: I2C: cannot write 000 to register R23
[   10.475913] wm8775 0-001b: I2C: cannot write 000 to register R7
[   10.479109] wm8775 0-001b: I2C: cannot write 021 to register R11
[   10.482291] wm8775 0-001b: I2C: cannot write 102 to register R12
[   10.485503] wm8775 0-001b: I2C: cannot write 000 to register R13
[   10.488659] wm8775 0-001b: I2C: cannot write 1d4 to register R14
[   10.491875] wm8775 0-001b: I2C: cannot write 1d4 to register R15
[   10.495044] wm8775 0-001b: I2C: cannot write 1bf to register R16
[   10.498220] wm8775 0-001b: I2C: cannot write 185 to register R17
[   10.504745] wm8775 0-001b: I2C: cannot write 0a2 to register R18
[   10.522574] wm8775 0-001b: I2C: cannot write 005 to register R19
[   10.534303] wm8775 0-001b: I2C: cannot write 07a to register R20
[   10.547012] wm8775 0-001b: I2C: cannot write 102 to register R21
[   10.547834] ivtv0: Registered device video0 for encoder MPG (4096 kB)
[   10.547940] ivtv0: Registered device video32 for encoder YUV (2048 kB)
[   10.548046] ivtv0: Registered device vbi0 for encoder VBI (1024 kB)
[   10.548153] ivtv0: Registered device video24 for encoder PCM (320 kB)
[   10.548259] ivtv0: Registered device radio0 for encoder radio
[   10.548266] ivtv0: Initialized card #0: Hauppauge WinTV PVR-150
[   10.548367] ivtv:  End initialization

Disabling Inactive Active Directory accounts

Wednesday 24 of December, 2008
We needed a method to disable inactive accounts in Active Directory. The DSQuery tool has an -inactive switch & DSMod can disable accounts. problem is they are all or nothing affairs. We needed a way to exclude some accounts (system accounts & other special cases).

We accomplished this by adding some flag text to the description of the special accounts. The following script will search for accounts that haven't been used in 4 weeks and if they don't have the flag in the description, will disable them.

@echo off
:: FindAgingAccts - GjM - 12/22/08
:: Use as you'd like, please attribute - thanks
:: Uses MS tools to locate inactive accounts and disable them
:: Accounts with !!Do Not Disable!! in the Description will not be disabled.
Set blatbin=c:\acc
set dsbin=c:\acc\ad
set SCRIPT_DIR=\data\dev\aging_accts

:: Set MODE=Prod inorder to disables accounts
set MODE=Test
set SKIP_FLAG=!!Do Not Disable!!

::Cleanup previous session
del results.txt
del action.log
del inaction.log
set ActCount=
set SkipCount=
set count=
copy inactive.old+inactive.txt inactive.tmp
del inactive.old
ren inactive.tmp inactive.old

::Locate old accounts
echo Starting automatic account maintenance
echo Querying inactive accounts
echo %Date% %Time% >inactive.txt
%dsbin%\dsquery user -inactive %INACTIVE_PERIOD% -limit 0  1>>inactive.txt 2>dsquery.err
if %errorlevel% NEQ 0 goto :ERR

::Count results
for /f "delims=? skip=1" %%a in (inactive.txt) do set /a count+=1 >nul
echo Inactive accounts to check: %count%

::Loop through the list of aging accounts and check their description
for /f "delims=? skip=1" %%a in (inactive.txt) do call :ChkUserStatus %%a
goto :SendReport
goto :EOF

:: Check description for flag that tells us not to disable
:: take action based on results
if %1=="" goto :EOF
for /f "delims=: tokens=2" %%b in ('%dsbin%\dsget user -desc -q -L %1') do (
	:: %%b contains the description from AD.  This line uses findstr to look for the FLAG in the description
	echo %%b |findstr /i /c:"%SKIP_FLAG%" >nul
	:: findstr returns errorlevel 1 if no match is found
		call :DisableAcct %1
	) ELSE ( 
		call :SkipAcct %1
goto :EOF

::Disable the account
echo %Date% %Time%, Disabling User, %1 >>action.log
set /a ActCount+=1
if %MODE%==Prod dsmod user -disabled yes %1
goto :EOF

::Log accounts not being disabled
echo %Date% %Time%, Account flagged, skipping User %1 >>inaction.log
set /a SkipCount+=1
goto :EOF

echo Mode is: %MODE%
echo DisabledAccounts: %ActCount%
echo SkippedAccounts: %SkipCount%
echo DisabledAccounts: %ActCount% >>results.txt
echo SkippedAccounts: %SkipCount% >>results.txt
echo Mode is: %MODE% >>results.txt
echo See inaction.log at \\exchmonitor\c$%WORK_DIR% >>results.txt
::Note: The following must all be on a single line
%blatbin%\blat results.txt -tf recips.txt 
   -subject "Automatic account maintenance" 
   -attacht %WORK_DIR%\results.txt -attacht %WORK_DIR%\action.log
   -server exch05.my.com -f admin@my.com
goto :EOF

echo Error retreiving inactive users
echo Error retreiving inactive users>>results.txt
goto :EOF

  • The ds* tools from Win2k3 server must be available in the path or as defined in dsbin
  • This script uses blat to send smtp mail. If you aren't aware of it search the web
  • You must set the MODE variable to Prod for the script to make changes to AD.
  • If you wish to use a different flag, modify the SKIP_FLAG variable

Editing custom AD attributes

Tuesday 23 of December, 2008
I have the need to edit the employeeID and employeeNumber attributes in AD. These attributes are not exposed in ADUC. Here's some reference material and a short script for adding the capability.

  • There is a straight forward way of adding items to the right-click menu in AD using the Display Specificers in the AD Configuration container
  • You can use a simple VB script to edit simple attributes
  • You could edit more complex attributes by writing a more complex program (Say with VB), but we won't cover that here.

  • Create a script to edit the attribute
    Here is a simple script (eeID.vbs) to edit the employeeID.
    (Note: To test the script, call it from the command line with a full LDAP path to a user object (ex: 'cscript eeid.vbs LDAP://cn=gmartin,cn=users,dc=somedomain,dc=com'))

Create and save this script somewhere in your path.
' EEID.vbs - GjM - 12/22/08
' Displays and allows edits to employeeID atttribute in AD
Option Explicit
Dim Args, oUsr, sNewID
Set Args = Wscript.Arguments
Set oUsr = GetObject(Args(0))
sNewID = InputBox("LDAP path: " & Args(0) & vbCRLF & vbCRLF & "The Employee ID of the user is: " & oUsr.employeeID_
  & vbCRLF & "If you would like enter a new number or modify the existing number, enter the new number_
  in the textbox below")
if sNewID <> "" then 
	oUsr.Put "employeeID",sNewID
end if
Set oUsr = Nothing

  • Add the item to the user admin context menu
    • Open ADSIEdit and connect to the Configuration container
    • Browse to CN=DisplaySpecifiers, CN=409 (or your language specifier)
    • Right click on CN=user-Display and select Properties
    • Highlight adminContextMenu and click Edit
    • Enter '6,&Employee ID, eeid.vbs' into the "Value to add" field and click Add
      • (Note: the number 6 represents the canonical order of the item in the conext menu. Feel free to play with this value to move your new item into the position you'd like.)
      • (Note: to remove this item from the contect menu, open the edit box again, highlight the EmploteeID line you added and click 'Remove')
    • Click OK to exit all the way out

TechNet article that discusses this process, but beware if you do not know what the script there does (cache)
Article at softheap.com that discusses this, but missed a step (cache)

Slackware 12.1 & VirtualBox

Wednesday 25 of June, 2008
I am running Slack 12.1 as a virtual guest on my Vista laptop. I just spent sevarl hours over several days getting the linux additions to run. So here are some tips (thanks to T3slider over at LinuxQuestions.org for helping me get to the bottom of this.

To install this you mount the vbox additions iso and run ./VBoxLinuxAdditions.run

The first error I saw was from the install script saying something like:
Please install the build and header files for your Linux kernel

This waqs solved by loading the kernel-headers- package from disk 1 of the Slackware set in the slackware/d directory. (try: installpkg kernel-headers-

After this the addditions still wouldn't compile, but the error was now hidden in a log file in /var/log/vboxadditions.log
The error made reference to:
ERROR: Kernel configuration is invalid
include/linux/autoconf.h or auto.conf are missing


bin/sh scripts/mod/modpost: No such file or directory

Took me awhile, but T3 mentioned off-hand that it sounded like the kernel-source was missing. I found that on disk 2 of the Slackware set in /slackware/k directory (try installpkg kernel-source-

Take note that the installer adds several new scripts to /etc/rc.d and add references to the script to rc.local to load the drivers at start up.

I'm still having a problem with pointer integration. It says it is working, but while I see the mousein the window, it doesn't do anything. I'll post back when I know more.

Loading Slackware 12.1

Sunday 11 of May, 2008
Release 12.1 of the famous Slackware disto went gold this past week. I loaded it this week with few issues. A couple things of note.

Pat highly recommends the use of a generic kernel. I installed it today with a initrd file. Here's the mkintrd command I needed

mkinitrd -c -k -f ext3 -m ext3 -r /dev/sda1

BTW, I confused the underscore and hyphen before the smp in the kernel spec and the system wouldn't boot ("no kernel modules found").


Slackware 12.1 goes RC-2

Tuesday 22 of April, 2008
Monday afternoon Patrick Volkerding announced Slackware 12.1 went RC-2 and is close to release. From the Changelog (cache)

Mon Apr 21 16:47:32 CDT 2008
We have now reached the Slackware 12.1 RC2 milestone. :-) We're beyond
updating packages or fixing minor cosmetic bugs at this point (actually, we
had hoped to be past that with RC1, but there were still items in need of
attention). What we have here now has proven to be stable for our testers,
so unless some real showstoppers are found we'll be releasing this as Slackware
12.1-final soon.

Kernel version:
KDE: 3.5.9
Install: Now supports network installs vis http & ftp


BotNet: Call to action

Thursday 10 of April, 2008
I read this morning of the new Kraken botnet (cache) and how it is quickly displacing Storm as the latest generator of SPAM and other internet trash. It is time to take serious action to rid the Internet of this blight and I think the large technology companies should be leading the charge. Can the likes of Microsoft, Sun, Google, IBM, Cisco, ATT, Verizon and others (sorry for the US-centric list of companies!) come together in an all-out assault on the existing and proliferating armies of centrally controlled and probably useless to their owner PCs that are making life risky for the PC owner, miserable for the spam police and nerve-racking for the security professional.

I foresee a multi-pronged, multi-media, high and low tech campaign to seek-out and block infected PCs, educate end-users, provide low-cost PC clean-up services and security software.

These companies should see the combined affect of the poor health of the Internet affects the view of their software, and distracts them from the innovation and product development by requiring them to expend resources on value-less changes to their exisiting products.

Hey, Big IT - how about it?


Logon script

Thursday 27 of March, 2008
More on the corporate logon script.

So here's the final script. There is a lot going on because we're trying to think ahead and be prepared for various user &amp; corporate needs. We based this on a script from one staffers previous job. Had we started from scratch, we would have a slightly lighter script, but would invest more time later solving problems.

Note the use of '::' as a comment lead in (as opposed to REM) for easier readability.

:: *******************************************************************************
:: This is the default Somecorp Windows Login batch file.
:: Modifications
:: 3/17/08 GjM Original script
:: 3/26/08 GjM Moved personal script to end; cleanup
:: 3/26/08 GjM Added setdrive.cmd to unmap then remap drives
:: 3/26/08 GjM Added test to see if were logging into a server.

:: *******************************************************************************
:: Change title of window for NT machines
echo Please wait while your logon is processed...

:: *******************************************************************************
:: ********************* Set a temporary path for executables. ************************
:: ********************* Used only during logon process. *************************
SET PATH=\\somecorp.com\sysvol\somecorp.com\scripts\bin;%PATH%
SET LOGONBIN=\\somecorp.com\sysvol\somecorp.com\scripts\bin
SET USERLOGONBIN=\\somecorp.com\sysvol\somecorp.com\scripts\users

:: Test to see if we should run this script
cscript /nologo %LOGONBIN%\Groupcheck.vbs "MigratedUsers"
if %errorlevel% EQU 0 (
   echo Failed groupcheck, exiting...
   Goto :EOF

:: Test to see if we are on a server and shouldn't run
cscript /nologo %LOGONBIN%\IsServerOS.vbs
if %errorlevel% EQU 0 (
   echo Running on a server; skipping logon script!
   Goto :EOF

goto Main

:: *******************************************************************************
:: **************** Look for LOGONSERVER and reset if necessary ******************
:: **************** This may be necessary if user logins in off net and joins later
:: **************** Ex: mobile user VPNS and runs script to map drives ***********
:: Check for LOGONSERVER var reset for logon process if necessary for remote access
:: CScript %LOGONBIN%\RegWrite.vbs "HKEY_CURRENT_USER\Volatile Environment\LOGONSERVER" \\server REG_SZ

:: **********************************************************
:: Map Corporate shares
call setdrive H \\somecorp.com\Corp\All_HomeDIR\%username%
call setdrive I \\somecorp.com\Corp
call setdrive J \\somecorp.com\Tech
call setdrive K \\somecorp.com\Prod

:: ********************* Look for and run personal logon script ******************
@echo Checking for Personal Logon script...

If exist %USERLOGONBIN%\%username%.cmd (
	@echo Found personal login script
	call %USERLOGONBIN%\%username%.cmd

::A real personal login script...
::This is a Netadmin secret (shhh!)
@if exist c:\local\mylogon.bat call c:\local\mylogon.bat

:: *******************************************************************************
:: ************** Inventory Plus REMd out on 013001 by bsc ***************
:: echo %DATE% %TIME% "Starting Inventory Select: " /n
:: INVselect.vbs abcdefg

:: ********************************************************************************
:: **************************** Launch homepage ********************************
::************************** Added 03/22/02 BSC ********************************
::start http://www.somecorp.com

There are calls to several external scripts

These scripts are documented elsewhere in this blog

As with all this, use it if it helps.



Thursday 27 of March, 2008
More on the corporate logon script.
We needed a way to prevent to script from running if we were logging onto a server. We developed a vbscript to test the ProductName registry key for the presence of the word "server". This works for at least Windows 2000 & 2003. Probably will work for 2008, probably not for NT 4. Neither have been tested yet.

'IsServerOS.vbs GjM  3/26/08
'Looks to see if ProductName reg key contains "Server"
'Returns 1 if yes and 2 if no

Option explicit
const HKEY_CURRENT_USER = &H80000001
const HKEY_LOCAL_MACHINE = &H80000002

dim strComputer, oReg, objargs
dim strValueName, strValue, strKeyPath

strComputer = "."

' Bind to WMI registry provider
Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" &_
 strComputer & "\root\default:StdRegProv")

strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
strValueName = "ProductName"
' query the path and key.  results returned in strValue
oReg.GetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strValue

'Check for "server" in returned value
If instr(strValue,"Server") > 0 then
	'wscript.echo "Running on a server: " & strValue
	wscript.quit 0
	'wscript.echo "Not running on a server: " & strValue
	wscript.quit 1
end if

We then made use of it by adding this to the logon script

:: Test to see if we are on a server and shouldn't run
cscript /nologo IsServerOS.vbs
if %errorlevel% EQU 0 (
   echo Running on a server; skipping logon script!
   Goto :EOF

Hope it's clear how this is working. Feel free to borrow



Thursday 27 of March, 2008
More on the corporate logon script....

We needed a way to control drive mapping based on group membership (or not). We built a vbscript that returns an error code based on user membership in a active directory group. If they are in the group, return 1, 0 if not.

'On Error Resume Next
' GroupCheck - GjM - returns errorlevel 1 if user is member of group, else returns 0
' EX: groupcheck.vbs 
option explicit
Dim objADSysInfo, strUser, objGroup, objNetwork, strGroup, objUser, group, bMatched
Dim strGroupToTest, objArgs

set objArgs = wscript.arguments
strGroupToTest = objargs(0)
bMatched = False

'Make no changes below this point (unless you know why!)

Set objADSysInfo = CreateObject("ADSystemInfo")
strUser = objADSysInfo.UserName
Set objUser = GetObject("LDAP://" &amp; strUser)

For Each group in objUser.memberOf
    Set objGroup = GetObject("LDAP://" &amp; group)
    If trim(objGroup.CN) = trim(strGroupToTest) Then 
          bMatched = True
	  'wscript.echo "Group match"
        Exit For
    End If

If bMatched then 
	'wscript.echo "User in group"
	wscript.quit 1
	'wscript.echo "User not in group"
	wscript.quit 0
End If

To make use of this, we added this to the logon script:
:: Test to see if we should run this script
cscript /nologo Groupcheck.vbs "MigratedUsers"
if %errorlevel% EQU 0 (
   echo Failed groupcheck, exiting...
   Goto :EOF

In this example, if the user is part of a group called MigratedUsers the script will continue, else it exits.

This could be adopted to run optional parts of the script based on group membershoip. For example, to map a particular drive.

Feel free to borrow this.



Thursday 27 of March, 2008
For our corporate logon script, we wanted to make sure all net use commands succeeded even if a drive was already mapped. We developed the following script to do this.

::Setdrive.cmd GjM 3/26/08
:: Unsets and sets a drive mapping
:: Usage:  Setdrive  
:: Ex: Setdrive P \\myco.com\Corp
@echo off
::The for /f acts to split the drive letter from the colon so we don't wind up with k::
for /f "delims=:" %%a in ("%1") do (
	:: if the drive is mapped, delete it and remap it; otherwise just map it
	if exist %a:\ (
		Net use %%a: /del 
		Net use %%a: %2%3%4 
	   ) else (
		Net use %%a: %2%3%4

Here's how you might use this. Note: the setdrive script deals with the presence or lack of the colon after the drive letter.
:: **********************************************************
:: Map Corporate shares
call setdrive H \\myco.com\Corp\All_HomeDIR\%username%
call setdrive I \\myco.com\Corp

It's well commented so I'll leave it at that. Feel free to borrow it.


Album ripping with Linux

Friday 28 of December, 2007
Album ripping with Linux

Getting started

I have a collection of several hundred vinyl albums from back in the day. I've made a couple attempts at recording them with limited success. I received an audio-technica at-PL50 turntable for Christmas and plan to get back on track.

The turntable is actually part of the AT-LP2Da package which is designed to assist the Windows user in recording their albums.
  • The turntable has a built-in preamp so there is no need for an amplifier (most amps provide a tape-out which is provides a 'hot' output usable for recording through your sound card.)
  • The package includes a copy of Cakewalk Pyro for recording under Windows XP
  • Lastly the package includes an RCA-female to mini-plug converter, and a mini-plug to RCA-male converter.


  • My recording PC
I run Slackware 12 on a Dell Optiplex GX620 that has 2GB RAM and a dual core Pentium M processor. Typically it has over 1GB free RAM and the CPU load is typically less than 1. All this says is there is a lot of performance capacity to handle the recording. Slackware 12 runs KDE 3.5.7 so most utilities will be KDE-based.

  • SoundCard
The Optiplex is a business class PC with a built-in soundcard based on the Intel 945 chipset. kMix reports it as Intel ICH7

  • Turntable
Audio-Technica AT-PL50 belt-drive, pre-amp output 200mV at 1KHz, 5 cm/sec

  • Software
    • Audacity 1.3.3
Audacity is a popular open-source audio recording and editing package for linux (and Windows). I figured I start with that. I'm running version 1.33. (Note: I had to compile this version for Slackware 12. I used the build script from SlackBuilds.org. I had a problem from wxGTK, so I needed to compile that first, then recompile Audacity against the latest library)

    • normalize 0.7.7
This tool gooses the volume of WAV files so the play well digitized. Its available from nongnu.org

    • lame 3.97
The quintessential mp3 encoder. I used this command-line to get great quality MP3s (at about 5Mb / 3 minute song):
lame --vbr-new -V2 -q0
To encode a directory of .wav files, try this:
for i in *.wav; do lame --vbr-new -V2 -q0 "$i" "${i%%.wav}.mp3";done

    • Easytag 2.1.4
A great tool for applying ID3 tags. Once the files were split and encoded as MP3, easytag was able to look up the album via keyword and apply the correct tags automagically. (Hint: sort the files alphabetically)

  • The Process

  1. Recording
There is a lot of information on recording with Audacity at the audacity wiki.


I've collected the following links on recording LPs
Another's thoughts on ripping (cache)

OpenLDAP 2.4

Saturday 17 of November, 2007
I upgraded to openldap 2.4.6 recently and converted from the slapd.conf file to cn=confg and slapd.d directory. The bottom line is the directory config is now controlled through the directory service rather than the config file and config changes are dynamic, happening immediately rather than requiring a directory restart.

(I guess in this sense,it caught up with Active Directory although that comment would be argued fiercely on the openldap list.)

The switch to a /slapd.d-based config is straight forward. You can feed the slapd.conf file through a conversion process by using one of the slap utilities,

The command:
slaptest -f /etc/openldap/slapd.cong -F /etc/openldap/slapd.d
will create the cn=config structure and create the various ldif files that control the frontend, config and backend databases.

Been away

Monday 15 of October, 2007
I've been away for awhile working on a significant project at work. We combined 5 offices and three server rooms into a single building and state-of-the-art data center. It was hugely successful. But this blog and this server have suffered from inattention for the past 6-9 months. The only thing I pulled off during that time was an upgrade to Slackware 12 within hours of release. I really couldn't help myself (I run Vista, too). But it went well except. Some minor issues:

  • apache is now at v2
    • apache config is now in /etc/httpd
    • apache logs in /var/log/httpd
    • apache docroot now in /srv/httpd

So there are a whole crop of things that need work

  • My CA cert is expired! Brilliant, I know. Apparently I only issue the CA cert for 1 week

  • Gallery needs an update, but more importantly there are thousands of spam comment. Mostly rude. Apparently there is a captcha weakness. I've killed many, but the cleanup will be tough.

Classmates extortion?

Thursday 22 of March, 2007
I have to comment on this. I received an e-mail today from Classmates.com today telling me that someone had posted a message to my profile. So I went to the site and was told I would have to upgrade in order to read the message. So do they get to hold that message hostage until I pay? Even after the sender paid for the privilege of sending the message? Brings a whole to meaning to the concept of sending a message, huh? Give me a break.

I've been a non-paying member there for years, but got pretty disgusted a few years back when they went to a pay model and locked down all the information they'd harvested over the years. And that's what they'd done. They provided a useful service for several years in order to gather all the information they could and then put up walls to extort you into signing up for the service. It's lame.

Stay away. Stay far away.



Classmates.com wrote:

                    Who checked you out?

 Hi Greg,

 1 person signed your profile!

 Think you know who it was? Find out who's thinking of 
 you now. 

So I clicked the link and found this:


Friday 09 of March, 2007
I've had a bear of a time getting OpenLDAP to configure for SSL/TLS. I made a couple discoveries today that I want to note.
(Note: this is not a OpenLDAP/TLS HowTo. If you are just starting, please read the OpenLDAP.org docs on configuring TLS)

I was receiving one main error:
no shared cipher

I couldn't figure out whether slapd was configured properly. So first I tested the certs using OpenSSL
I ran this in one shell to set up a listner:
openssl s_server 
    -CAfile /var/data/ca/cacert.pem
    -cert /var/data/ca/newcerts/ldap1cert.pem
    -key /etc/openldap/ldap1keyclear.txt -accept 99
    -cipher DHE-RSA-AES256-SHA

and this in another to connect to the listner:
openssl s_client 
    -host uslack2.gmartin.org 
    -port 99 
    -cipher DHE-RSA-AES256-SHA
    -ssl3 (or -tls1)
(note:These commands use your cert files to set up a server and client to exchange data over ssl or tls.)
For me, the connection established and data was exchanged. Thereby proving the certs &amp; CA were correct.
Next I ran slapd with -d 255 to enable debugging. What I found was I using an incorrect directive for the TLS options.

I was using:


Looks as though I confused ldap.conf and slapd.conf directives. Why are they different one wonders?

However, I was still receiving "no shared cipher" error. I was using this as a test tool:
To test for SSL on port 636:
ldapsearch  -H ldaps://uslack2.gmartin.org 
    -vvv cn=gmartin -D cn=Manager,dc=gmartin,dc=org 
    -w password -x

To test for TLS on port 389:
ldapsearch  -H ldap://uslack2.gmartin.org 
    vvv cn=gmartin -D cn=Manager,dc=gmartin,dc=org 
    -w password -x -ZZ

I had the following in slapd.conf and ldap.conf:

(which I cut and pasted from 'openssl ciphers')

I replaced it with the following to fix the issue:

It's still not clear to me what the syntax should be - the OpenLDAP docs are poor here IMO. Trying to translate the openssl -v ciphers into what's mentioned in the manpage doesn't help me much. Perhaps I'm dense.

So I posted a couple questions to openldap mailing list that don't need answers:
- would there be value in making the slapd.conf and ldap.conf TLS directives align?
- Should slaptest report the bad TLS directives?

And one more. In the man page for slapd, there is this explanation for the -h option:
slapd will by default serve ldap:/// (LDAP over TCP on all interfaces on default
LDAP port).  That is, it will bind using INADDR_ANY and port 389. The -h option 
may be used  to  specify  LDAP  (and  other scheme) URLs  to  serve.   For
example,  if  slapd  is  given -h "ldap:// ldaps:/// ldapi:///",
it will listen on for LDAP, for LDAP over TLS,

The last part seems inexact. It says -h ldaps:/// will cause slapd to listen on port 636 for LDAP over TLS. Should that say something like:

"will cause slapd to listen for LDAP over SSL on port 636 and for start_tls on port 389. With properly configured TLS directives, specifying '-h ldap:///' will make available TLS over port 389"
And for posterity, here are the TLS directives from my conf files:
TLSCACertificateFile /var/data/ca/cacert.pem
TLSCertificateFile /var/data/ca/newcerts/ldap1cert.pem
TLSCertificateKeyFile /etc/openldap/ldap1keyclear.txt
TLSVerifyClient never

TLS_CACERT /var/data/ca/cacert.pem