Looking for a better solution on Google, I quickly discovered the source of the bad code, the OpenLDAP FAQ-o-matic. In this FAQ entry, there are code examples for a variety of languages, many offering higher security. However, the PHP example included only a SHA-1 variant, and was copy-pasted nearly verbatim into my subject code (I wonder how many other web applications have done this very thing).
OpenLDAP has support for a variety of different hashing algorithms, including an optional SHA-2 module, and passthrough to the OpenSSL crypto(3) library. The strongest native cipher supported by OpenLDAP is salted-SHA1 (SSHA), which allows sufficient strength for this application, when used with a decently-large salt. I wrote the following function to generate such hashes from PHP, provided here in hopes that people will quit using unsalted SHA in their web-apps.
/*
* This is a helper function, returning a Salted SHA-1 hash, suitable for LDAP.
* OpenLDAP uses a slightly strange scheme for generating these hashes, but it's
* far better than unsalted SHA. The only limit on salt length appears to be the
* maximum length of the userPassword attribute in LDAP (128), allowing us to
* safely use a 64-byte salt, resulting in a 118-byte SSHA. For the curious,
* this works out to:
*
* 6B ({SSHA} tag) + 28B (20B SHA, B64 encoded) + 88B (64B salt, B64 encoded)
*
*/
function generate_ssha_hash($cleartext)
{
/*
* Generate a unique 64-byte salt value for the salt.
*
* Use mcrypt_create_iv to generate some random bytes. PHP 7 has a
* random_bytes() function, and the OpenSSL extension provides
* openssl_random_pseudo_bytes(), which may be better, but this
* should work
*/
$pw_salt = mcrypt_create_iv(64, MCRYPT_DEV_URANDOM);
// Concatenate and hash password+salt.
$hashed = sha1($cleartext . $pw_salt, TRUE);
// Add the tag and encoded hash+salt.
$hashed = '{SSHA}' . base64_encode($hashed . $pw_salt);
// Clean up
unset($pw_salt);
return $hashed;
} // generate_ssha_hash