1 module ldap; 2 3 import std.stdio; 4 import std.format : format; 5 import std.exception : enforce; 6 import std.string : fromStringz; 7 8 import ldapd; 9 10 private void tool_unbind_local(LDAP *ld) @trusted { 11 int err = ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, null); 12 13 if(err != LDAP_OPT_SUCCESS) { 14 throw new Exception("Could not unset controls"); 15 } 16 17 ldap_unbind_ext( ld, null, null ); 18 } 19 20 private int tool_exit_local( LDAP *ld, int status ) @trusted { 21 if(ld != null) { 22 tool_unbind_local(ld); 23 } 24 return status; 25 } 26 27 private LDAP* tool_conn_setup_local(char* lhost, int lport ) @trusted { 28 LDAP *ld = null; 29 30 int rc; 31 char *ldapuri; 32 33 scope(exit) { 34 ldap_memfree(ldapuri); 35 } 36 37 //if( ( lhost != null || lport ) && ( ldapuri == null ) ) { 38 assert(lhost !is null && lport ); 39 /* construct URL */ 40 LDAPURLDesc url; 41 42 url.lud_scheme = cast(char*)"ldap".ptr; 43 url.lud_host = lhost; 44 url.lud_port = lport; 45 url.lud_scope = LDAP_SCOPE_DEFAULT; 46 47 ldapuri = ldap_url_desc2str(&url); 48 49 //writefln("ldap_initialize( %s )", ldapuri !is null ? ldapuri : "<DEFAULT>" ); 50 51 // BUG 52 // BUG ldap_initialize leaks 40 byte of memory 53 // BUG 54 rc = ldap_initialize(&ld, ldapuri); 55 56 if(rc != LDAP_SUCCESS) { 57 throw new Exception(format( 58 "Could not create LDAP session handle for URI=%s (%d): %s", 59 ldapuri, rc, ldap_err2string(rc))); 60 } 61 62 /* referrals: obsolete */ 63 if(ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON) != LDAP_OPT_SUCCESS) { 64 throw new Exception(format("Could not set LDAP_OPT_REFERRALS on")); 65 } 66 67 int lprotocol = LDAP_VERSION3; 68 if(ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &lprotocol) 69 != LDAP_OPT_SUCCESS ) 70 { 71 throw new Exception(format("Could not set LDAP_OPT_PROTOCOL_VERSION %d", 72 lprotocol) ); 73 } 74 75 rc = ldap_start_tls_s( ld, null, null ); 76 if(rc != LDAP_SUCCESS) { 77 char* msg; 78 scope(exit) { 79 ldap_memfree(msg); 80 } 81 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg); 82 throw new Exception( 83 format("ldap_start_tls %d %s", rc, fromStringz(msg).idup)); 84 } 85 86 timeval nettimeout; 87 if(nettimeout.tv_sec > 0) { 88 if(ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &nettimeout) 89 != LDAP_OPT_SUCCESS) 90 { 91 throw new Exception(format("Could not set LDAP_OPT_NETWORK_TIMEOUT %s", 92 cast(long)nettimeout.tv_sec)); 93 } 94 } 95 96 return ld; 97 } 98 99 private int tool_exit(LDAP *ld, int status) @trusted { 100 if(ld !is null) { 101 tool_unbind_local(ld); 102 } 103 return status; 104 } 105 106 private int tool_bind_local(LDAP *ld, char* passLocal, char* binddn) @trusted { 107 LDAPControl **sctrlsp; 108 LDAPControl[4] *sctrls; 109 int nsctrls = 0; 110 111 int rc; 112 int msgid; 113 LDAPMessage *result; 114 115 int err; 116 char *matched; 117 char *info; 118 char **refs; 119 LDAPControl **ctrls; 120 char[256] msgbuf; 121 122 msgbuf[0] = 0; 123 124 if(nsctrls) { 125 sctrlsp = cast(LDAPControl**)sctrls.ptr; 126 } 127 128 //assert( nsctrls < cast(int) (sctrls.sizeof / sctrls[0].sizeof) ); 129 130 char *pw = passLocal; 131 132 berval passwd; 133 passwd.bv_val = ber_strdup(pw); 134 passwd.bv_len = strlen(passwd.bv_val); 135 136 scope(exit) { 137 ber_memfree(passwd.bv_val); 138 ber_memfree(matched); 139 ber_memfree(info); 140 ber_memvfree(cast(void **)refs); 141 } 142 143 /* simple bind */ 144 rc = ldap_sasl_bind(ld, binddn, LDAP_SASL_SIMPLE, &passwd, 145 sctrlsp, null, &msgid); 146 if(msgid == -1) { 147 tool_exit(ld, rc); 148 throw new Exception(format("ldap_sasl_bind: %s (code: 0x%x)", fromStringz(ldap_err2string(rc)), rc)); 149 } 150 151 scope(failure) { 152 tool_exit(ld, LDAP_LOCAL_ERROR); 153 } 154 155 rc = ldap_result(ld, msgid, LDAP_MSG_ALL, null, &result); 156 enforce(rc != -1, format("ldap_result %d", -1)); 157 enforce(rc != 0, format("ldap_result %d", LDAP_TIMEOUT)); 158 159 if(result) { 160 rc = ldap_parse_result( ld, result, &err, &matched, &info, &refs, 161 &ctrls, 1 ); 162 if(rc != LDAP_SUCCESS) { 163 return tool_exit(ld, LDAP_LOCAL_ERROR); 164 } 165 } 166 167 if(err != LDAP_SUCCESS 168 || msgbuf[0] 169 || (matched && matched[ 0 ]) 170 || (info && info[ 0 ]) 171 || refs) 172 { 173 if(err != LDAP_SUCCESS) { 174 return tool_exit(ld, err); 175 } 176 } 177 return 0; 178 } 179 180 /* 181 int main(string[] args) { 182 string un = "testuser@host.com"; 183 string pwd = "$ymm3try86!"; 184 185 string host = "ad.host.com"; 186 LDAPLoginResult ret = login(host, un, pwd); 187 writeln(ret); 188 return ret.returnCode; 189 }*/ 190 191 struct LDAPLoginResult { 192 int returnCode; 193 string userId; 194 } 195 196 LDAPLoginResult login(string host, string username, string password) @trusted { 197 import std.string : toStringz; 198 import std.conv : to; 199 int rc; 200 LDAP* ld; 201 char* matcheddn; 202 char* text; 203 char** refs; 204 berval* authzid; 205 int id; 206 int code = 0; 207 LDAPMessage* res; 208 LDAPControl** ctrls; 209 210 char* binddn = ber_strdup(toStringz(username)); 211 char* ldaphost = ber_strdup(toStringz(host)); 212 213 scope(exit) { 214 ldap_msgfree(res); 215 ber_memfree(text); 216 ber_memfree(matcheddn); 217 ber_memvfree(cast(void **) refs); 218 ber_bvfree(authzid); 219 ber_memfree(binddn); 220 ber_memfree(ldaphost); 221 } 222 223 /* LDAPv3 only */ 224 225 ld = tool_conn_setup_local(ldaphost, 389); 226 enforce(ld != null, format("Failed to connect to LDAP server")); 227 228 int ret = tool_bind_local( ld, cast(char*)toStringz(password), binddn); 229 230 enforce(ret == LDAP_SUCCESS, format("LDAP bind failed: %s (code: 0x%x)", fromStringz(ldap_err2string(ret)), ret)); 231 232 rc = ldap_whoami( ld, null, null, &id ); 233 234 enforce(ret == LDAP_SUCCESS, format("ldap_whoami failed: %s (code: 0x%x)", fromStringz(ldap_err2string(rc)), rc)); 235 236 for( ; ; ) { 237 scope(failure) { 238 rc = tool_exit_local( ld, rc ); 239 } 240 241 timeval tv; 242 243 tv.tv_sec = 0; 244 tv.tv_usec = 100000; 245 246 rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); 247 enforce(rc >= 0, format("ldap_result: %s (code: 0x%x)", fromStringz(ldap_err2string(rc)), rc)); 248 249 if(rc != 0) { 250 break; 251 } 252 } 253 254 rc = ldap_parse_result( ld, res, 255 &code, &matcheddn, &text, &refs, &ctrls, 0 ); 256 257 if(rc == LDAP_SUCCESS) { 258 rc = code; 259 } 260 261 enforce(rc == LDAP_SUCCESS, format("ldap_parse_result: %s (code: 0x%x)", fromStringz(ldap_err2string(rc)), rc)); 262 263 rc = ldap_parse_whoami( ld, res, &authzid ); 264 enforce(rc == LDAP_SUCCESS, format("ldap_parse_whoami: %s (code: 0x%x)", fromStringz(ldap_err2string(rc)), rc)); 265 266 LDAPLoginResult rslt; 267 if(authzid != null) { 268 if(authzid.bv_len == 0) { 269 rslt.userId = "anonymous"; 270 } else { 271 rslt.userId = to!string(fromStringz(authzid.bv_val)); 272 } 273 } 274 275 /* disconnect from server */ 276 rslt.returnCode = tool_exit_local(ld, code == LDAP_SUCCESS ? 0 : 1 ); 277 return rslt; 278 }