이 과정에서 PHP Ldap API를 이용하여 Active Directory 를 관리하는데, 다른 정보를 변경하는 것은 별 무리가 없으나, Active Directory 의 Password 를 변경할 경우, AD에서 ldap ssl 로 연결을 할 경우에만 Password 변경이 가능 합니다. 이 부분 때문에 엄청난 삽질을 하게 되었습니다. 뭐 기본적으로 ldap 에 대한 지식이 별로 없었던 것이 삽질의 가장 주된 원인이기는 했지만, 검색에 걸린 대부분의 문서들이 ldap 에 대한 지식이 있다는 가정하에 설명을 하다보니 미치고 환장하겠더군요 ^^;
각설하고, 여기에서는 Active Directory 의 Password 변경이 가능하도록 PHP에서 Ldap SSL 로 연결하는 것에 대한 설명을 하고자 합니다.
기본적인 전제 조건으로는, Windows 2003 에 Active Directory Domain controller 가 구성이 되어 있고, 이 서버에서 Ldap SSL (ldaps://) connection 이 가능하도록 설정이 되어 있다는 가정하에 설명을 하도록 하겠습니다. Domain controller 에 Ldap SSL 을 설정하는 것은 googling 으로 쉽게 찾을 수 있으므로.. 생략 하도록 하겠습니다.
1. Client 에서 사용할 CACERT 인증서 생성
Domain controller 에서 사용되는 인증서를 "DER로 인코딩된 X.509 바이너리(.CER)" 로 export 를 합니다. export 한 CA 인증서를 Unix 서버로 이동한 후에, 다음의 명령으로 .pem file 로 전환을 합니다.
shell> openssl x509 -in cacert.cer -inform DER -out cacert.pem -outform PEM
2. PHP Ldap Extension 확인
phpinfo() 함수를 이용해서, ldap extension 이 지원되는지와, ldap 에서 SASL 지원이 되는지를 확인 합니다.
LDAP Support => enabled
RCS Version => $Id: ldap.c,v 1.161.2.3.2.13 2008/05/04 21:19:17 colder Exp $
Total Links => 0/unlimited
API Version => 3001
Vendor Name => OpenLDAP
Vendor Version => 20327
SASL Support => Enabled
3. Coding
Active Directory 에서 AD Password 는 unicodePwd 라는 entry 에 UTF16 형식의 plain text 를 입력을 해 주면 됩니다. 이 의미는 abcd 를 입력하려면 'a\000b\000c\000d\000' 과 같이 입력을 하면 된다는 의미 입니다.
간단하게 코드의 예를 들자면 다음과 같이 생성할 수 있습니다. (단 주의할 것은 ASCII 범위 밖의 문자열을 입력하시면 안됩니다.)
<?php
function make_ad_passwd ($pass) {
$pass = '"' . $pass . '"';
$len = strlen ($pass);
for ( $i=0; $i<$len; $i++ )
$newpass .= $pass[$i] . "\000";
return $newpass;
}
?>
그럼, AD password 함수를 변경하는 코드의 예를 보도록 하겠습니다. 이 코드에서 중요한 것은, 또는 다른 문서에 나오지 않는 내용은, unicodePwd entry 를 변경하기 위해서는 ldap ssl 연결을 사용해야 한다는 것이고, 또 하나는 SSL 연결을 위한 인증서 설정 입니다.
<?php
# SSL 로 접속을 해야 하기 때문에 ldaps:// protocol 을 사용한다.
$host = "ldaps://ad.domain.controller.com";
# ldaps 의 기본 포트는 636 을 사용한다.
$port = "636";
# 인증서 설정
$certfile = '/some/path/cacert.pem';
# default DN
$defaultdn = 'domain_manager@DOMAIN.NAME.COM';
$accesspw = 'password_of_domain_manager';
# 관리 OU
$ou = "OU=Users,DS=DOMAIN,DS=NAME,DS=com"
$username = $argv[1];
$userpass = $argv[2] ? $argv[2] : 'abcd';
if ( ! $username ) {
echo "ERROR: You must input changed username on argv[1]\n";
exit 1;
}
#
# system 의 ldap 설정을 건드리지 않고 하기 위해서 다음의 환경 변수를 설정한다.
putenv ('LDAPTLS_REQCERT=never');
putenv ("LDAPTLS_CACERT={$certfile}");
$ldap = ldap_connect ($host, $port);
if ( $ldap === false ) {
echo "ERROR: connect failed to $host\n";
exit 1;
}
$bind = @ldap_bind ($ldap, $defaultdn, $accesspw);
if ( $bind === false ) {
echo ldap_error ($ldap) . "\n";
exit 1;
}
$filter = "(samaccountname={$username})";
$result = ldap_search ($ldap, $ou, $filter);
ldap_sort ($ldap, $result, 'sn');
$info = ldap_get_entries ($ldap, $result);
for ( $i=0; $i<$info['count']; $i++ ) {
echo "You are changing the password for " .
$info[$i]['givenname'][0] . ", " .
$info[$i]['sn'][0] . " (" .
$info[$i]['samaccountname'][0] .
") to {$userpass}\n";
$userdata['unicodePwd'] = make_ad_passwd ($userpass);
$result = ldap_mod_replace ($ldap, $info[$i]['distinguishedname'][0], $userdata);
if ( $result === false )
echo "There was a problem changing your password, please call IT for help\n";
else
echo "Your password han been changed!\n";
}
@ldap_close ($ldap);
?>
Posted by 김정균


