一、首先,要弄明白修改Windows活動目錄中使用者的密碼需要注意的地方:
1.在活動目錄中,使用者的密碼是unicode編碼,所以密碼必須由ascii轉換成為unicode編碼,如下shell命令轉換
[root@local~]echo -n "/"ppAA1234/"" | iconv -f UTF8 -t UTF16LE | base64 -w 0
IgBwAHAAQQBBADEAMgAzADQAIgA=
2.為了與AD 伺服器能正常通訊,必須使用SSL串連
二、樣本
#include <stdio.h>
#include <stdlib.h>
#include <ldap.h>
#include <unistd.h>
using namespace std;
#ifndef PASS_LOG
#define PASS_LOG(fmt, arg...) printf(fmt, ##arg)
#endif
#define BUF_MAX_LEN 1024
#define PASSWD_MAX_LEN 512
#define AD_LDAP_PORT 636
#define AD_LDAP_URL "ldaps://ad02.example.com:636"
char g_admin_dn[BUF_MAX_LEN]= "cn=admin,ou=finance,dc=example,dc=com";
char g_admin_pass[BUF_MAX_LEN]= "123456";
//char g_user_dn[BUF_MAX_LEN]= "CN=user_test,OU=finance,DC=example,DC=com";
char g_base_dn[BUF_MAX_LEN]= "ou=finance,dc=example,dc=com";
int modifyAccountAttributeInActivityDirectory(char *admin_dn, char *admin_pass, char* username, char *user_pass)
{
char filter[BUF_MAX_LEN];
char * user_dn = NULL;
LDAP *ld = NULL;
LDAPMessage *result = NULL, *element = NULL;
LDAPMod mod, mod2;
LDAPMod *mods[3];
struct berval bvalold;
struct berval bvalnew;
struct berval *bvalsold[2];
struct berval *bvalsnew[2];
char old_password_with_quotes[PASSWD_MAX_LEN], new_password_with_quotes[PASSWD_MAX_LEN];
char old_unicode_password[PASSWD_MAX_LEN *2], new_unicode_password[PASSWD_MAX_LEN* 2];
const char *new_password = NULL;
const char *old_password = "1234PPmm";
int ldap_version = LDAP_VERSION3;
int rc = -1, err_code = -1;
int i = 0;
if (NULL == admin_dn || NULL == admin_pass || NULL == username || NULL == user_pass)
{
PASS_LOG("modifyAccountAttributeInActivityDirectory: Parameters is NULL/n");
err_code = -1;
goto clean;
}
rc = ldap_initialize(&ld, AD_LDAP_URL);
if ( rc != LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_initialize" );
PASS_LOG("ldap_initialize: %s/n", ldap_err2string (rc));
err_code = rc;
goto clean;
}
rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
if ( rc != LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_set_option" );
PASS_LOG("ldap_set_option: %s/n", ldap_err2string (rc));
err_code = rc;
goto unbind_ld;
}
rc = ldap_simple_bind_s( ld, admin_dn, admin_pass);
if ( rc != LDAP_SUCCESS )
{
ldap_perror(ld, "ldap_simple_bind_s" );
PASS_LOG("ldap_simple_bind_s: %s/n", ldap_err2string (rc));
err_code = rc;
goto unbind_ld;
}
memset(filter,0,sizeof(filter));
sprintf(filter,"cn=%s", username);
rc = ldap_search_s(ld,
g_base_dn,
LDAP_SCOPE_SUBTREE,
filter,
NULL,
0,
&result);
if (rc != LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_search_s" );
PASS_LOG("ldap_search_s: %s/n", ldap_err2string (rc));
err_code = rc;
goto unbind_ld;
}
for ( element= ldap_first_entry( ld, result ); element != NULL; element = ldap_next_entry( ld, element ) )
{
user_dn = ldap_get_dn(ld, element);
PASS_LOG("dn:%s/n", user_dn);
}
if (user_dn == NULL)
{
PASS_LOG("dn is NULL/n");
err_code = -1;
goto free_msg;
}
new_password = user_pass;
memset(new_password_with_quotes, 0, sizeof(new_password_with_quotes));
snprintf (new_password_with_quotes, sizeof (new_password_with_quotes), "/"%s/"", new_password);
memset (new_unicode_password, 0, sizeof (new_unicode_password));
for (i = 0; i < strlen (new_password_with_quotes); i++)
{
new_unicode_password[i * 2] = new_password_with_quotes[i];
}
bvalnew.bv_val = new_unicode_password;
bvalnew.bv_len = strlen (new_password_with_quotes) * 2;
bvalsnew[0] = &bvalnew;
bvalsnew[1] = NULL;
mod.mod_vals.modv_bvals = bvalsnew;
mod.mod_type = (char *) "unicodePwd";
#if 0
/* user must supply old password */
memset(old_password_with_quotes, 0, sizeof(old_password_with_quotes));
snprintf (old_password_with_quotes,
sizeof (old_password_with_quotes), "/"%s/"",
old_password);
memset (old_unicode_password, 0, sizeof (old_unicode_password));
for (i = 0; i < strlen (old_password_with_quotes); i++)
{
old_unicode_password[i * 2] = old_password_with_quotes[i];
}
bvalold.bv_val = old_unicode_password;
bvalold.bv_len = strlen (old_password_with_quotes) * 2;
bvalsold[0] = &bvalold;
bvalsold[1] = NULL;
mod2.mod_vals.modv_bvals = bvalsold;
mod2.mod_type = (char *) "unicodePwd";
mod2.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
mod.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
mods[0] = &mod2;
mods[1] = &mod;
mods[2] = NULL;
#else
mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
mods[0] = &mod;
mods[1] = NULL;
#endif
rc = ldap_modify_s(ld, user_dn, mods);
if (rc != LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_modify_s" );
PASS_LOG("ldap_modify_s: %s/n", ldap_err2string (rc));
}
err_code = rc;
PASS_LOG("Modify account's attribute in activity directory Ok/n");
if (NULL != ld)
{
free(user_dn);
}
free_msg:
if (NULL != result)
{
ldap_msgfree(result);
}
unbind_ld:
if (NULL != ld)
{
ldap_unbind(ld);
}
clean:
return err_code;
}
int main(int argc, char *argv[])
{
char *new_password = "ooXX1234";
if (modifyAccountAttributeInActivityDirectory(g_admin_dn, g_admin_pass, "user_test", new_password) < 0)
{
PASS_LOG("Failed to modify account's attribute in activity directory");
return -1;
}
return 0;
}
[root@local~]g++ change_passwd.cpp -lldap -g -DLDAP_DEPRECATED=1 -o change_passwd
三、CA認證
[root@local~]# ./change_passwd
ldap_simple_bind_s: Can't contact LDAP server (-1)
additional info: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
解決方案:
1. 從域控匯出.cer檔案
2. 把該cer檔案格式改為pem
[root@local~]#openssl x509 -inform DER -in /root/ad02.cer -out /root/ad02.pem -outform PEM
3.配置/etc/openldap/ldap.conf
[root@local~]#vim /etc/openldap/ldap.conf
use_sasl on
ssl on
sasl start_tls
SASL_MECH GSSAPI
tls_checkpeer no
tls_ciphers TLSv1
TLS_REQCERT never
chasereferrals yes
deref always
uri ldaps://ad02.example.com:636
binddn cn=admin,ou=finance,dc=example,dc=com
# Tell GSSAPI not to negotiate a security or privacy layer since
# AD doesn't support nested security or privacy layers
sasl_secprops minssf=0,maxssf=0
tls_cacertfile /root/ad02.pem
[root@local~]#./change_passwd
Modify account's attribute in activity directory Ok
Creating Active Directory Accounts
四、參考資料
OpenLDAP Server With Server-Side SSL/TLS and Client Authentication(最具價值參考ldap_initialize)
pam_ldap.c中_get_authtok(最具價值參考 unicode轉換)
LDAP Authentication and Password Management
如何更改通過 LDAP 的 Windows 2000 使用者的密碼
啟用 LDAP 用戶端通過 SSL 與 LDAP 伺服器進行通訊的說明
LDAP C programming development - SDK Man Pages
Mozilla LDAP C SDK Programmer's Guide
ldap 636 Java Python C# Cold Fusion Perl PHP Ruby