0

So i'm coding a program in Go, with C bindings thanks to CGo, and i'm using ldap to perform search, add and modify operations. I could manage to do all that, but now im trying to set a password in the unicodePwd mod_type and i can't seem to get around the Error 53: Server is unwilling to perfom.

I know a lot of stuff can cause this error so: I'm connected with ldaps. I hard-coded for testing purpose a password made of 10 characters, with double quotes at the beginning and the end, and got that in UTF-16LE, Base64. The password hase lowercase letters, uppercase letters and punctuations symbols.

Here are some samples of my code, im just testing stuff right now so the coding is really bad:

Setting options :C.ldap_set_option(l, LDAP_OPT_PROTOCOL_VERSION, unsafe.Pointer(&version)) C.ldap_set_option(l, LDAP_OPT_REFERRALS, unsafe.Pointer(&v))

Initialization:C.ldap_initialize(&l, C.CString("ldaps://**.**.**.**:636"))

Binding:rc := C.ldap_simple_bind_s(l, C.CString("CN=Administrator,CN=Users,DC=intra,DC=localdomain,DC=com"), C.CString("**********"))

And now the important part, adding a user with a password :

add_user(l, "ldaps://**.**.**.**", "636", "CN=Administrator,CN=Users,DC=intra,DC=localdomain,DC=com", "OU=*******,DC=intra,DC=localdomain,DC=com")

func add_user(l *C.LDAP, host string, port string, login string, container string) {

var mods [5]*C.LDAPModStr
var modClass, modCN, modSN, modPass C.LDAPModStr
var vclass [5]*C.char
var vcn [4]*C.char
var vsn [2]*C.char
var vpass [2]*C.char
modClass.mod_op = 0
modClass.mod_type = C.CString("objectclass")
vclass[0] = C.CString("top")
vclass[1] = C.CString("person")
vclass[2] = C.CString("organizationalPerson")
vclass[3] = C.CString("User")
vclass[4] = nil
modClass.mod_vals = &vclass[0]

modCN.mod_op = 0
modCN.mod_type = C.CString("cn")
vcn[0] = C.CString("john")
vcn[1] = nil
modCN.mod_vals = &vcn[0]

modSN.mod_op = 0
modSN.mod_type = C.CString("sn")
vsn[0] = C.CString("mclane")
vsn[1] = nil
modSN.mod_vals = &vsn[0]

modPass.mod_op = 0
modPass.mod_type = C.CString("unicodePwd")
vpass[0] = C.CString("IgBTAHcAZQBlAHQATgBlAHcAUAB3AGQAMQAyADMAIQAiAA==")
vpass[1] = nil
modPass.mod_vals = &vpass[0]

mods[0] = &modClass
mods[1] = &modCN
mods[2] = &modSN
mods[3] = &modPass
mods[4] = nil

dn := "cn=john,OU=*********,DC=intra,DC=localdomain,DC=com"

rc := C._ldap_add(l, C.CString(dn), &mods[0])

if rc != LDAP_SUCCESS {
    er := C.ldap_err2string(rc)
    fmt.Println("ADD ERROR")
    fmt.Println(rc)
    fmt.Println(C.GoString(er))
}

Oh, and heres the definition of the type LDAPModStr:

typedef struct ldapmod_str {
    int  mod_op;
    char      *mod_type;
    char    **mod_vals;} LDAPModStr;

And _ldap_add :

int _ldap_add(LDAP *ld, char* dn, LDAPModStr **attrs){

            return ldap_add_ext_s(ld, dn, (LDAPMod **)attrs, NULL, NULL);
        }

I'm probably missing something obvious here, since i'm kinda new to GO and LDAP, but if you could help me solve this i would really be grateful. I dont know if this is relevant but the programs connects to the Active Directory of Windows Server 2012 R2, which is running on the same computer in a virtual machine. Also i'm kinda new here, i can post all of my code here if its easier for you, but i thought posting just important steps was maybe better.

madraven
  • 109
  • 6
  • 2
    You should probably be passing the plaintext password, and if you're using OpenLDAP with the password policy extension you should be using the modify-password extended operation, not just an attribute change. – user207421 Oct 13 '15 at 09:18
  • Using a non-encoded password in my code doesn't work either. As for your other suggestion, do you mean using ldappasswd in a shell command ? – madraven Oct 13 '15 at 09:31
  • 1
    @madraven, no, that's a special kind of operation (examples of operations are "search" and "update") supported by some LDAP servers -- see [RFC 3062](https://www.ietf.org/rfc/rfc3062.txt). – kostix Oct 13 '15 at 10:53
  • Ok I think I get it now, so i must use something like ldap_extended_operation_s ? I'm gonna try that and let you guys know if it works ! Thanks ! – madraven Oct 13 '15 at 11:50
  • Hi again @kostix ! Ok so i tried using ldap_passwd_s, which apparently calls ldap_extended_operations, but i keep getting a Protocol Error... any idea what could cause that ? – madraven Oct 14 '15 at 08:28
  • 1
    @madraven, a bit of further googling tells me that my suggestion might have completely missed the point: the AD sems to not support the password modify operation. [This post](http://www.dirmgr.com/blog/2010/8/26/ldap-password-changes-in-active-directory.html) deals with AD-specific warts with regard to plain modify operation on that special attribute. – kostix Oct 14 '15 at 11:25
  • 1
    @madraven, you might as well try another route and rather than trying to talk to AD using LDAP -- a technique never officially endorsed by MS itself -- you could try using WinAPI to make MS own stuff talk to AD in a way it prefers. Say, [`NetUserChangePassword`](https://msdn.microsoft.com/en-us/library/aa370650) from `netapi32.dll` seems like a good fit. Of course, this would only work if your code runs on Windows. In return, to call a WInAPI functon you don't need to use `cgo` which is IMO a win. – kostix Oct 14 '15 at 11:31
  • Thanks so much for your help @kostix ! So unfortunately i can't run my code on windows... Would you happen to know another way ? But there is something i don't understand: my company has some php code, that uses ldap to change passwords without any problems. They want to get rid of the php and i have have to code it in C / Go. Why would these almost same functions work with php and not in C ? – madraven Oct 14 '15 at 12:36
  • Well, OK, at this point I'd turn to debugging. I'd take the working PHP code, modify it a bit to use LDAP (not LDAPS), and run the user addition operation while having `tcpdump` capturing the relevant traffic. The operation will supposedly fail (because of SSL/TLS turned off) but you could grab the dump file and inspect it using Wireshark which has excellent dissectors for LDAP. The next step would be to run your Go/C code using the same approach (turn off SSL/TLS and dump the traffic) and then compare the on-the-wire LDAP commands sent in both cases. – kostix Oct 14 '15 at 13:50
  • And while we're on it, you could consider using [a native Go library for LDAPv3](https://godoc.org/github.com/go-ldap/ldap) instead of interfacing `libldap` via `cgo`. – kostix Oct 14 '15 at 13:52
  • Again, thank you man. So first i tried using the native Go library for LDAP, but of course still the protocol error (even though you were right: so much easier to code !!). I'm now gonna try what you suggested about capturing the tcp traffic, but I dont really understand everything xD – madraven Oct 14 '15 at 14:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/92278/discussion-between-kostix-and-madraven). – kostix Oct 14 '15 at 14:41

1 Answers1

1

I don't know if anybody would be interested but i found a solution with the help of @kostix and i thought i'd share: the extended modify operations in C (using CGO) didn't work, but a simple modify operation using go-ldap with the correct encoding allowed me to change a user password on windows AD.

madraven
  • 109
  • 6