Ok, I figured it out and got it working. The problem was that the code, as posted here, was wrapped in a class (class rcube_dscl_password) with a function named save. Looking at the readme in the password plugin (as well as a couple of the existing drivers), however, showed that the proper format was to simply have a function named password_save that took two arguments. So changing the above code to the following got it working:
<?php
/**
* DSCL Driver
*/
function password_save($currpass, $newpass)
{
$username = escapeshellarg($_SESSION['username']);
$userpass = escapeshellarg($currpass);
$newuserpass = escapeshellarg($newpass);
$cmd = exec('dscl -u '.$username.' -P '.$userpass.' /LDAPv3/127.0.0.1 -passwd /Users/'.$username.' '.$newuserpass);
$handle = popen($cmd, "w");
if (pclose($handle) == 0) {
return PASSWORD_SUCCESS;
}
else {
raise_error(array(
'code' => 600,
'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Unable to execute $cmd"
), true, false);
}
return PASSWORD_ERROR;
}