一、首先,要弄明白修改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