| rev | line source | 
| bsw@1071 | 1 /* | 
| bsw@1071 | 2  * minimalistic Lua LDAP interface library | 
| bsw@1071 | 3  * | 
| bsw@1071 | 4  * The library does not set any global variable itself and must thus be | 
| bsw@1071 | 5  * loaded with | 
| bsw@1071 | 6  * | 
| bsw@1071 | 7  *     mldap = require("mldap") | 
| bsw@1071 | 8  * | 
| bsw@1071 | 9  * or a similar statement. | 
| bsw@1071 | 10  * | 
| bsw@1071 | 11  * The library provides two functions, conn = bind{...} and unbind(conn) | 
| bsw@1071 | 12  * to connect to and disconnect from the LDAP server respectively. The | 
| bsw@1071 | 13  * unbind function is also provided as a method of the connection userdata | 
| bsw@1071 | 14  * object (see below). | 
| bsw@1071 | 15  * | 
| bsw@1071 | 16  * The arguments to the bind{...} function are documented in the source code | 
| bsw@1071 | 17  * (see C function mldap_bind). In case of success, the bind function returns | 
| bsw@1071 | 18  * a userdata object that provides two methods: query{...} and unbind(). In | 
| bsw@1071 | 19  * case of error, the bind function returns nil as first return value, an | 
| bsw@1071 | 20  * error string as second return value, and a numeric error code as third | 
| bsw@1071 | 21  * return value. A positive error code is an LDAP resultCode, a negative | 
| bsw@1071 | 22  * error code is an OpenLDAP API error code: | 
| bsw@1071 | 23  * | 
| bsw@1071 | 24  *     connection, error_string, numeric_error_code = mldap.bind{...} | 
| bsw@1071 | 25  * | 
| bsw@1071 | 26  * For translating numeric error codes to an identifying (machine readable) | 
| bsw@1071 | 27  * string identifier, the library provides in addition to the two functions | 
| bsw@1071 | 28  * a table named 'errorcodes', for example: | 
| bsw@1071 | 29  * | 
| bsw@1071 | 30  *     49 == mldap.errorcodes["invalid_credentials"] | 
| bsw@1071 | 31  * | 
| bsw@1071 | 32  * and | 
| bsw@1071 | 33  * | 
| bsw@1071 | 34  *     mldap.errorcodes[49] == "invalid_credentials" | 
| bsw@1071 | 35  * | 
| bsw@1071 | 36  * The arguments and return value of the query{...} method of the connection | 
| bsw@1071 | 37  * userdata object are also documented in the source code below (see | 
| bsw@1071 | 38  * C function mldap_query). Error conditions are reported the same way as the | 
| bsw@1071 | 39  * bind{...} function does. | 
| bsw@1071 | 40  * | 
| bsw@1071 | 41  * To close the connection, either the unbind() function of the library or | 
| bsw@1071 | 42  * the unbind() method can be called; it is allowed to call them multiple | 
| bsw@1071 | 43  * times, and they are also invoked by the garbage collector. | 
| bsw@1071 | 44  * | 
| bsw@1071 | 45  */ | 
| bsw@1071 | 46 | 
| bsw@1071 | 47 // Lua header inclusions: | 
| bsw@1071 | 48 #include <lua.h> | 
| bsw@1071 | 49 #include <lauxlib.h> | 
| bsw@1071 | 50 | 
| bsw@1071 | 51 // OpenLDAP header inclusions: | 
| bsw@1071 | 52 #include <ldap.h> | 
| bsw@1071 | 53 | 
| bsw@1071 | 54 // Standard C inclusions: | 
| bsw@1071 | 55 #include <stdlib.h> | 
| bsw@1071 | 56 #include <stdbool.h> | 
| bsw@1071 | 57 #include <unistd.h> | 
| bsw@1071 | 58 | 
| bsw@1071 | 59 // Error code translation is included from separate C file: | 
| bsw@1071 | 60 #include "mldap_errorcodes.c" | 
| bsw@1071 | 61 | 
| bsw@1071 | 62 // Provide compatibility with Lua 5.1: | 
| bsw@1071 | 63 #if LUA_VERSION_NUM < 502 | 
| bsw@1071 | 64 #define luaL_newlib(L, t) lua_newtable((L)); luaL_register(L, NULL, t) | 
| bsw@1071 | 65 #define lua_rawlen lua_objlen | 
| bsw@1071 | 66 #define lua_len lua_objlen | 
| bsw@1071 | 67 #define luaL_setmetatable(L, regkey) \ | 
| bsw@1071 | 68   lua_getfield((L), LUA_REGISTRYINDEX, (regkey)); \ | 
| bsw@1071 | 69   lua_setmetatable((L), -2); | 
| bsw@1071 | 70 #endif | 
| bsw@1071 | 71 | 
| bsw@1071 | 72 // prefix for all Lua registry entries of this library: | 
| bsw@1071 | 73 #define MLDAP_REGKEY "556aeaf3c864af2e_mldap_" | 
| bsw@1071 | 74 | 
| bsw@1071 | 75 | 
| bsw@1071 | 76 static const char *mldap_get_named_string_arg( | 
| bsw@1071 | 77   // gets a named argument of type "string" from a table at the given stack position | 
| bsw@1071 | 78 | 
| bsw@1071 | 79   lua_State *L,         // pointer to lua_State variable | 
| bsw@1071 | 80   int idx,              // stack index of the table containing the named arguments | 
| bsw@1071 | 81   const char *argname,  // name of the argument | 
| jbe@1641 | 82   bool mandatory        // if not 0, then the argument is mandatory and an error is raised if it isn't found | 
| bsw@1071 | 83 | 
| bsw@1071 | 84   // leaves the string as new element on top of the stack | 
| bsw@1071 | 85 ) { | 
| bsw@1071 | 86 | 
| bsw@1071 | 87   // pushes the table entry with the given argument name on top of the stack: | 
| bsw@1071 | 88   lua_getfield(L, idx, argname); | 
| bsw@1071 | 89 | 
| bsw@1071 | 90   // check, if the entry is nil or false: | 
| bsw@1071 | 91   if (!lua_toboolean(L, -1)) { | 
| bsw@1071 | 92 | 
| bsw@1071 | 93     // throw error, if named argument is mandatory: | 
| bsw@1071 | 94     if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), NULL; | 
| bsw@1071 | 95 | 
| bsw@1071 | 96     // return NULL pointer, if named argument is not mandatory: | 
| bsw@1071 | 97     return NULL; | 
| bsw@1071 | 98 | 
| bsw@1071 | 99   } | 
| bsw@1071 | 100 | 
| bsw@1071 | 101   // throw error, if the value of the argument is not string: | 
| bsw@1071 | 102   if (lua_type(L, -1) != LUA_TSTRING) return luaL_error(L, "Named argument '%s' is not a string", argname), NULL; | 
| bsw@1071 | 103 | 
| bsw@1071 | 104   // return a pointer to the string, leaving the string on top of the stack to avoid garbage collection: | 
| bsw@1071 | 105   return lua_tostring(L, -1); | 
| bsw@1071 | 106 | 
| bsw@1071 | 107   // leaves one element on the stack | 
| bsw@1071 | 108 } | 
| bsw@1071 | 109 | 
| bsw@1071 | 110 | 
| bsw@1071 | 111 static int mldap_get_named_number_arg( | 
| bsw@1071 | 112   // gets a named argument of type "number" from a table at the given stack position | 
| bsw@1071 | 113 | 
| bsw@1071 | 114   lua_State *L,             // pointer to lua_State variable | 
| bsw@1071 | 115   int idx,                  // stack index of the table containing the named arguments | 
| bsw@1071 | 116   const char *argname,      // name of the argument | 
| jbe@1641 | 117   bool mandatory,           // if not 0, then the argument is mandatory and an error is raised if it isn't found | 
| jbe@1641 | 118   lua_Number default_value  // default value to return, if the argument is not mandatory and nil | 
| bsw@1071 | 119 | 
| bsw@1071 | 120   // opposed to 'mldap_get_named_string_arg', this function leaves no element on the stack | 
| bsw@1071 | 121 ) { | 
| bsw@1071 | 122 | 
| bsw@1071 | 123   lua_Number value;  // value to return | 
| bsw@1071 | 124 | 
| bsw@1071 | 125   // pushes the table entry with the given argument name on top of the stack: | 
| bsw@1071 | 126   lua_getfield(L, idx, argname); | 
| bsw@1071 | 127 | 
| bsw@1071 | 128   // check, if the entry is nil or false: | 
| bsw@1071 | 129   if (lua_isnil(L, -1)) { | 
| bsw@1071 | 130 | 
| bsw@1071 | 131     // throw error, if named argument is mandatory: | 
| bsw@1071 | 132     if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), 0; | 
| bsw@1071 | 133 | 
| bsw@1071 | 134     // set default value as return value, if named argument is not mandatory: | 
| bsw@1071 | 135     value = default_value; | 
| bsw@1071 | 136 | 
| bsw@1071 | 137   } else { | 
| bsw@1071 | 138 | 
| bsw@1071 | 139     // throw error, if the value of the argument is not a number: | 
| bsw@1071 | 140     if (lua_type(L, -1) != LUA_TNUMBER) return luaL_error(L, "Named argument '%s' is not a number", argname), 0; | 
| bsw@1071 | 141 | 
| bsw@1071 | 142     // set return value to the number: | 
| bsw@1071 | 143     value = lua_tonumber(L, -1); | 
| bsw@1071 | 144 | 
| bsw@1071 | 145   } | 
| bsw@1071 | 146 | 
| bsw@1071 | 147   // remove unnecessary element from stack (not needed to avoid garbage collection): | 
| jbe@1641 | 148   lua_pop(L, 1); | 
| jbe@1641 | 149 | 
| bsw@1071 | 150   return value; | 
| bsw@1071 | 151 | 
| bsw@1071 | 152   // leaves no new elements on the stack | 
| bsw@1071 | 153 } | 
| bsw@1071 | 154 | 
| bsw@1071 | 155 | 
| bsw@1640 | 156 static bool mldap_get_named_boolean_arg( | 
| bsw@1640 | 157   // gets a named argument of type "boolean" from a table at the given stack position | 
| bsw@1640 | 158 | 
| bsw@1640 | 159   lua_State *L,             // pointer to lua_State variable | 
| bsw@1640 | 160   int idx,                  // stack index of the table containing the named arguments | 
| bsw@1640 | 161   const char *argname,      // name of the argument | 
| jbe@1641 | 162   bool mandatory,           // if not 0, then the argument is mandatory and an error is raised if it isn't found | 
| bsw@1640 | 163   bool default_value        // default value to return, if the argument is not mandatory and nil | 
| bsw@1640 | 164 | 
| bsw@1640 | 165   // opposed to 'mldap_get_named_string_arg', this function leaves no element on the stack | 
| bsw@1640 | 166 ) { | 
| bsw@1640 | 167 | 
| bsw@1640 | 168   bool value;  // value to return | 
| bsw@1640 | 169 | 
| bsw@1640 | 170   // pushes the table entry with the given argument name on top of the stack: | 
| bsw@1640 | 171   lua_getfield(L, idx, argname); | 
| bsw@1640 | 172 | 
| bsw@1640 | 173   // check, if the entry is nil: | 
| bsw@1640 | 174   if (lua_isnil(L, -1)) { | 
| bsw@1640 | 175 | 
| bsw@1640 | 176     // throw error, if named argument is mandatory: | 
| bsw@1640 | 177     if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), 0; | 
| bsw@1640 | 178 | 
| bsw@1640 | 179     // set default value as return value, if named argument is not mandatory: | 
| bsw@1640 | 180     value = default_value; | 
| bsw@1640 | 181 | 
| bsw@1640 | 182   } else { | 
| bsw@1640 | 183 | 
| bsw@1640 | 184     // throw error, if the value of the argument is not a number: | 
| bsw@1640 | 185     if (lua_type(L, -1) != LUA_TBOOLEAN) return luaL_error(L, "Named argument '%s' is not a boolean", argname), 0; | 
| bsw@1640 | 186 | 
| bsw@1640 | 187     // set return value to the number: | 
| bsw@1640 | 188     value = lua_toboolean(L, -1); | 
| bsw@1640 | 189 | 
| bsw@1640 | 190   } | 
| bsw@1640 | 191 | 
| bsw@1640 | 192   // remove unnecessary element from stack (not needed to avoid garbage collection): | 
| bsw@1640 | 193   lua_pop(L, 1); | 
| bsw@1640 | 194 | 
| bsw@1640 | 195   return value; | 
| bsw@1640 | 196 | 
| bsw@1640 | 197   // leaves no new elements on the stack | 
| bsw@1640 | 198 } | 
| bsw@1640 | 199 | 
| bsw@1640 | 200 | 
| bsw@1071 | 201 static int mldap_scope( | 
| bsw@1071 | 202   // converts a string ("base", "onelevel", "subtree", "children") to an integer representing the LDAP scope | 
| bsw@1071 | 203   // and throws an error for any unknown string | 
| bsw@1071 | 204 | 
| bsw@1071 | 205   lua_State *L,             // pointer to lua_State variable (needed to throw errors) | 
| bsw@1071 | 206   const char *scope_string  // string that is either ("base", "onelevel", "subtree", "children") | 
| bsw@1071 | 207 | 
| bsw@1071 | 208   // does not affect or use the Lua stack at all | 
| bsw@1071 | 209 ) { | 
| bsw@1071 | 210 | 
| bsw@1071 | 211   // return integer according to string value: | 
| bsw@1071 | 212   if (!strcmp(scope_string, "base"))     return LDAP_SCOPE_BASE; | 
| bsw@1071 | 213   if (!strcmp(scope_string, "onelevel")) return LDAP_SCOPE_ONELEVEL; | 
| bsw@1071 | 214   if (!strcmp(scope_string, "subtree"))  return LDAP_SCOPE_SUBTREE; | 
| bsw@1071 | 215   if (!strcmp(scope_string, "children")) return LDAP_SCOPE_CHILDREN; | 
| bsw@1071 | 216 | 
| bsw@1071 | 217   // throw error for unknown string values: | 
| bsw@1071 | 218   return luaL_error(L, "Invalid LDAP scope: '%s'", scope_string), 0; | 
| bsw@1071 | 219 | 
| bsw@1071 | 220 } | 
| bsw@1071 | 221 | 
| bsw@1071 | 222 | 
| bsw@1071 | 223 static int mldap_bind(lua_State *L) { | 
| bsw@1071 | 224   // Lua C function that takes named arguments as a table | 
| bsw@1071 | 225   // and returns a userdata object, representing the LDAP connection | 
| bsw@1071 | 226   // or returns nil, an error string, and an error code (integer) on error | 
| bsw@1071 | 227 | 
| bsw@1071 | 228   // named arguments: | 
| bsw@1071 | 229   // "uri"      (string)  server URI to connect to | 
| bsw@1071 | 230   // "who"      (string)  DN to bind as | 
| bsw@1071 | 231   // "password" (string)  password for DN to bind as | 
| bsw@1071 | 232   // "timeout"  (number)  timeout in seconds | 
| bsw@1640 | 233   // "tls"      (boolean) use TLS | 
| bsw@1071 | 234 | 
| bsw@1071 | 235   static const int ldap_version = LDAP_VERSION3;  // providing a pointer (&ldap_version) to set LDAP protocol version 3 | 
| bsw@1071 | 236   const char *uri;           // C string for "uri" argument | 
| bsw@1640 | 237   bool tls;                  // boolean indicating if TLS is to be used | 
| bsw@1071 | 238   const char *who;           // C string for "who" argument | 
| bsw@1071 | 239   struct berval cred;        // credentials ("password") are stored as struct berval | 
| bsw@1071 | 240   lua_Number timeout_float;  // float (lua_Number) for timeout | 
| bsw@1071 | 241   struct timeval timeout;    // timeout is stored as struct timeval | 
| bsw@1071 | 242   int ldap_error;            // LDAP error code (as returned by libldap calls) | 
| bsw@1071 | 243   LDAP *ldp;                 // pointer to an opaque OpenLDAP structure representing the connection | 
| bsw@1071 | 244   LDAP **ldp_ptr;            // pointer to a Lua userdata structure (that only contains the 'ldp' pointer) | 
| bsw@1071 | 245 | 
| bsw@1071 | 246   // throw error if first argument is not a table: | 
| bsw@1071 | 247   if (lua_type(L, 1) != LUA_TTABLE) { | 
| bsw@1071 | 248     luaL_error(L, "Argument to function 'bind' is not a table."); | 
| bsw@1071 | 249   } | 
| bsw@1071 | 250 | 
| bsw@1071 | 251   // extract arguments: | 
| bsw@1071 | 252   uri = mldap_get_named_string_arg(L, 1, "uri", true); | 
| bsw@1640 | 253   tls = mldap_get_named_boolean_arg(L, 1, "tls", false, false); | 
| bsw@1071 | 254   who = mldap_get_named_string_arg(L, 1, "who", false); | 
| jbe@1638 | 255   cred.bv_val = (char *)mldap_get_named_string_arg(L, 1, "password", false); | 
| jbe@1638 | 256   // use (char *) cast to suppress compiler warning (should be const anyway) | 
| bsw@1071 | 257   if (cred.bv_val) cred.bv_len = strlen(cred.bv_val); | 
| bsw@1071 | 258   else cred.bv_len = 0; | 
| bsw@1071 | 259   timeout_float = mldap_get_named_number_arg(L, 1, "timeout", false, -1); | 
| bsw@1071 | 260   timeout.tv_sec = timeout_float; | 
| bsw@1071 | 261   timeout.tv_usec = (timeout_float - timeout.tv_sec) * 1000000; | 
| bsw@1071 | 262 | 
| bsw@1071 | 263   // initialize OpenLDAP structure and provide URI for connection: | 
| bsw@1071 | 264   ldap_error = ldap_initialize(&ldp, uri); | 
| bsw@1071 | 265   // on error, jump to label "mldap_queryconn_error1", as no ldap_unbind_ext_s() is needed: | 
| bsw@1071 | 266   if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error1; | 
| bsw@1071 | 267 | 
| bsw@1071 | 268   // set LDAP protocol version 3: | 
| bsw@1071 | 269   ldap_error = ldap_set_option(ldp, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); | 
| bsw@1071 | 270   // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called: | 
| bsw@1071 | 271   if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2; | 
| bsw@1071 | 272 | 
| bsw@1071 | 273   // set timeout for asynchronous OpenLDAP library calls: | 
| bsw@1071 | 274   ldap_error = ldap_set_option(ldp, LDAP_OPT_TIMEOUT, &timeout); | 
| bsw@1071 | 275   // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called: | 
| bsw@1071 | 276   if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2; | 
| bsw@1071 | 277 | 
| bsw@1640 | 278   // initiate TLS if requested | 
| bsw@1640 | 279   if (tls) { | 
| bsw@1640 | 280     ldap_error = ldap_start_tls_s(ldp, NULL, NULL); | 
| bsw@1640 | 281     if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2; | 
| bsw@1640 | 282   } | 
| bsw@1640 | 283 | 
| bsw@1071 | 284   // connect to LDAP server: | 
| bsw@1071 | 285   ldap_error = ldap_sasl_bind_s( | 
| bsw@1071 | 286     ldp,               // pointer to opaque OpenLDAP structure representing the connection | 
| bsw@1071 | 287     who,               // DN to bind as | 
| bsw@1071 | 288     LDAP_SASL_SIMPLE,  // SASL mechanism "simple" for password authentication | 
| bsw@1071 | 289     &cred,             // password as struct berval | 
| bsw@1071 | 290     NULL,              // no server controls | 
| bsw@1071 | 291     NULL,              // no client controls | 
| bsw@1071 | 292     NULL               // do not store server credentials | 
| bsw@1071 | 293   ); | 
| bsw@1071 | 294 | 
| bsw@1071 | 295   // error handling: | 
| bsw@1071 | 296   if (ldap_error != LDAP_SUCCESS) { | 
| bsw@1071 | 297 | 
| bsw@1071 | 298     // error label to jump to, if a call of ldap_unbind_ext_s() is required: | 
| bsw@1071 | 299     mldap_queryconn_error2: | 
| bsw@1071 | 300 | 
| bsw@1071 | 301     // close connection and free resources: | 
| bsw@1071 | 302     ldap_unbind_ext_s(ldp, NULL, NULL); | 
| bsw@1071 | 303 | 
| bsw@1071 | 304     // error label to jump to, if no call of ldap_unbind_ext_s() is required: | 
| bsw@1071 | 305     mldap_queryconn_error1: | 
| bsw@1071 | 306 | 
| bsw@1071 | 307     // return three values: | 
| bsw@1071 | 308     lua_pushnil(L);                                  // return nil as first value | 
| bsw@1071 | 309     lua_pushstring(L, ldap_err2string(ldap_error));  // return error string as second value | 
| bsw@1071 | 310     lua_pushinteger(L, ldap_error);                  // return error code (integer) as third value | 
| bsw@1071 | 311     return 3; | 
| bsw@1071 | 312 | 
| bsw@1071 | 313   } | 
| bsw@1071 | 314 | 
| bsw@1071 | 315   // create new Lua userdata object (that will contain the 'ldp' pointer) on top of stack: | 
| bsw@1071 | 316   ldp_ptr = lua_newuserdata(L, sizeof(LDAP *)); | 
| bsw@1071 | 317 | 
| bsw@1071 | 318   // set metatable of Lua userdata object: | 
| bsw@1071 | 319   luaL_setmetatable(L, MLDAP_REGKEY "connection_metatable"); | 
| bsw@1071 | 320 | 
| bsw@1071 | 321   // write contents of Lua userdata object (the 'ldp' pointer): | 
| bsw@1071 | 322   *ldp_ptr = ldp; | 
| bsw@1071 | 323 | 
| bsw@1071 | 324   // return Lua userdata object from top of stack: | 
| bsw@1071 | 325   return 1; | 
| bsw@1071 | 326 | 
| bsw@1071 | 327 } | 
| bsw@1071 | 328 | 
| bsw@1071 | 329 | 
| bsw@1071 | 330 static int mldap_search(lua_State *L) { | 
| bsw@1071 | 331   // Lua C function used as "search" method of Lua userdata object representing the LDAP connection | 
| bsw@1071 | 332   // that takes a Lua userdata object (the LDAP connection) as first argument, | 
| bsw@1071 | 333   // a table with named arguments as second argument, | 
| bsw@1071 | 334   // and returns a result table on success (see below) | 
| bsw@1071 | 335   // or returns nil, an error string, and an error code (integer) on error | 
| bsw@1071 | 336 | 
| bsw@1071 | 337   // named arguments: | 
| bsw@1071 | 338   // "base"  (string)   DN of the entry at which to start the search | 
| bsw@1071 | 339   // "scope" (string)   scope of the search, one of: | 
| bsw@1071 | 340   //                      "base" to search the object itself | 
| bsw@1071 | 341   //                      "onelevel" to search the object's immediate children | 
| bsw@1071 | 342   //                      "subtree" to search the object and all its descendants | 
| bsw@1071 | 343   //                      "children" to search all of the descendants | 
| bsw@1071 | 344   // "filter" (string)  string representation of the filter to apply in the search | 
| bsw@1071 | 345   //                    (conforming to RFC 4515 as extended in RFC 4526) | 
| bsw@1071 | 346   // "attrs"  (table)   list of attribute descriptions (each a string) to return from matching entries | 
| bsw@1071 | 347 | 
| bsw@1071 | 348   // structure of result table: | 
| bsw@1071 | 349   // { | 
| bsw@1071 | 350   //   { dn      = <distinguished name (DN)>, | 
| bsw@1071 | 351   //     <attr1> = { <value1>, <value2>, ... }, | 
| bsw@1071 | 352   //     <attr2> = { <value1>, <value2>, ... }, | 
| bsw@1071 | 353   //     ... | 
| bsw@1071 | 354   //   }, | 
| bsw@1071 | 355   //   { dn      = <distinguished name (DN)>, | 
| bsw@1071 | 356   //     <attr1> = { <value1>, <value2>, ... }, | 
| bsw@1071 | 357   //     <attr2> = { <value1>, <value2>, ... }, | 
| bsw@1071 | 358   //     ... | 
| bsw@1071 | 359   //   }, | 
| bsw@1071 | 360   //   ... | 
| bsw@1071 | 361   // } | 
| bsw@1071 | 362 | 
| bsw@1071 | 363   const char *base;          // C string for "base" argument | 
| bsw@1071 | 364   const char *scope_string;  // C string for "scope" argument | 
| bsw@1071 | 365   int scope;                 // integer representing the scope | 
| bsw@1071 | 366   const char *filter;        // C string for "filter" argument | 
| bsw@1071 | 367   size_t nattrs;             // number of attributes in "attrs" argument | 
| jbe@1638 | 368   const char **attrs;        // C string array of "attrs" argument | 
| bsw@1071 | 369   size_t attr_idx;           // index variable for building the C string array of "attrs" | 
| bsw@1071 | 370   int ldap_error;            // LDAP error code (as returned by libldap calls) | 
| bsw@1071 | 371   LDAP **ldp_ptr;            // pointer to a pointer to the OpenLDAP structure representing the connection | 
| bsw@1071 | 372   LDAPMessage *res;          // pointer to the result of ldap_search_ext_s() call | 
| bsw@1071 | 373   LDAPMessage *ent;          // pointer to an entry in the result of ldap_search_ext_s() call | 
| bsw@1071 | 374   int i;                     // integer to fill the Lua table returned as result | 
| bsw@1071 | 375 | 
| bsw@1071 | 376   // truncate the Lua stack to 2 elements: | 
| bsw@1071 | 377   lua_settop(L, 2); | 
| bsw@1071 | 378 | 
| bsw@1071 | 379   // check if the first argument is a Lua userdata object with the correct metatable | 
| bsw@1071 | 380   // and get a C pointer to that userdata object: | 
| bsw@1071 | 381   ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable"); | 
| bsw@1071 | 382 | 
| bsw@1071 | 383   // throw an error, if the connection has already been closed: | 
| bsw@1071 | 384   if (!*ldp_ptr) { | 
| bsw@1071 | 385     return luaL_error(L, "LDAP connection has already been closed"); | 
| bsw@1071 | 386   } | 
| bsw@1071 | 387 | 
| bsw@1071 | 388   // check if the second argument is a table, and throw an error if it's not a table: | 
| bsw@1071 | 389   if (lua_type(L, 2) != LUA_TTABLE) { | 
| bsw@1071 | 390     luaL_error(L, "Argument to function 'bind' is not a table."); | 
| bsw@1071 | 391   } | 
| bsw@1071 | 392 | 
| bsw@1071 | 393   // extract named arguments (requires memory allocation for 'attrs'): | 
| bsw@1071 | 394   base = mldap_get_named_string_arg(L, 2, "base", true);  // pushed to 3 | 
| bsw@1071 | 395   scope_string = mldap_get_named_string_arg(L, 2, "scope", true);  // pushed to 4 | 
| bsw@1071 | 396   scope = mldap_scope(L, scope_string); | 
| bsw@1071 | 397   lua_pop(L, 1);  // removes stack element 4 | 
| bsw@1071 | 398   filter = mldap_get_named_string_arg(L, 2, "filter", false);  // pushed to 4 | 
| bsw@1071 | 399   lua_getfield(L, 2, "attrs");  // pushed to 5 | 
| bsw@1634 | 400   nattrs = luaL_len(L, -1); | 
| bsw@1071 | 401   attrs = calloc(nattrs + 1, sizeof(char *));  // memory allocation, +1 for terminating NULL | 
| bsw@1071 | 402   if (!attrs) return luaL_error(L, "Memory allocation error in C function 'mldap_queryconn'"); | 
| bsw@1071 | 403   for (attr_idx=0; attr_idx<nattrs; attr_idx++) { | 
| bsw@1071 | 404     lua_pushinteger(L, attr_idx+1); | 
| bsw@1071 | 405     lua_gettable(L, 5);  // pushed onto variable stack position | 
| bsw@1071 | 406     if (lua_type(L, -1) != LUA_TSTRING) { | 
| bsw@1071 | 407       free(attrs); | 
| bsw@1071 | 408       return luaL_error(L, "Element in attribute list is not a string"); | 
| bsw@1071 | 409     } | 
| bsw@1636 | 410     attrs[attr_idx] = lua_tostring(L, -1); | 
| bsw@1071 | 411   } | 
| bsw@1071 | 412   // attrs[nattrs] = NULL;  // unnecessary due to calloc | 
| bsw@1071 | 413 | 
| bsw@1071 | 414   // execute LDAP search and store pointer to the result in 'res': | 
| bsw@1071 | 415   ldap_error = ldap_search_ext_s( | 
| bsw@1071 | 416     *ldp_ptr,  // pointer to the opaque OpenLDAP structure representing the connection | 
| bsw@1071 | 417     base,      // DN of the entry at which to start the search | 
| bsw@1071 | 418     scope,     // scope of the search | 
| bsw@1071 | 419     filter,    // string representation of the filter to apply in the search | 
| jbe@1638 | 420     (char **)attrs,  // array of attribute descriptions (array of strings) | 
| jbe@1638 | 421                // cast to suppress compiler warning (should be const anyway) | 
| bsw@1071 | 422     0,         // do not only request attribute descriptions but also values | 
| bsw@1071 | 423     NULL,      // no server controls | 
| bsw@1071 | 424     NULL,      // no client controls | 
| bsw@1071 | 425     NULL,      // do not set another timeout | 
| bsw@1071 | 426     0,         // no sizelimit | 
| bsw@1071 | 427     &res       // store result in 'res' | 
| bsw@1071 | 428   ); | 
| bsw@1071 | 429 | 
| bsw@1071 | 430   // free data structures that have been allocated for the 'attrs' array: | 
| bsw@1071 | 431   free(attrs); | 
| bsw@1071 | 432 | 
| bsw@1071 | 433   // return nil, an error string, and an error code (integer) in case of error: | 
| bsw@1071 | 434   if (ldap_error != LDAP_SUCCESS) { | 
| bsw@1071 | 435     lua_pushnil(L); | 
| bsw@1071 | 436     lua_pushstring(L, ldap_err2string(ldap_error)); | 
| bsw@1071 | 437     lua_pushinteger(L, ldap_error); | 
| bsw@1071 | 438     return 3; | 
| bsw@1071 | 439   } | 
| bsw@1071 | 440 | 
| bsw@1071 | 441   // clear Lua stack: | 
| bsw@1071 | 442   lua_settop(L, 0); | 
| bsw@1071 | 443 | 
| bsw@1071 | 444   // create result table for all entries at stack position 1: | 
| bsw@1071 | 445   lua_newtable(L); | 
| bsw@1071 | 446 | 
| bsw@1071 | 447   // use ldap_first_entry() and ldap_next_entry() functions | 
| bsw@1071 | 448   // to iterate through all entries in the result 'res' | 
| bsw@1071 | 449   // and count 'i' from 1 up: | 
| bsw@1071 | 450   for ( | 
| bsw@1071 | 451     ent=ldap_first_entry(*ldp_ptr, res), i=1; | 
| bsw@1071 | 452     ent; | 
| bsw@1071 | 453     ent=ldap_next_entry(*ldp_ptr, ent), i++ | 
| bsw@1071 | 454   ) { | 
| bsw@1071 | 455 | 
| bsw@1071 | 456     char *attr;       // name of attribute | 
| bsw@1071 | 457     BerElement *ber;  // structure to iterate through attributes | 
| bsw@1071 | 458     char *dn;         // LDAP entry name (distinguished name) | 
| bsw@1071 | 459 | 
| bsw@1071 | 460     // create result table for one entry at stack position 2: | 
| bsw@1071 | 461     lua_newtable(L); | 
| bsw@1071 | 462 | 
| bsw@1071 | 463     // use ldap_first_attribute() and ldap_next_attribute() | 
| bsw@1071 | 464     // as well as 'BerElement *ber' to iterate through all attributes | 
| bsw@1071 | 465     // ('ber' must be free'd with ber_free(ber, 0) call later): | 
| bsw@1071 | 466     for ( | 
| bsw@1071 | 467       attr=ldap_first_attribute(*ldp_ptr, ent, &ber); | 
| bsw@1071 | 468       attr; | 
| bsw@1071 | 469       attr=ldap_next_attribute(*ldp_ptr, ent, ber) | 
| bsw@1071 | 470     ) { | 
| bsw@1071 | 471 | 
| bsw@1071 | 472       struct berval **vals;  // pointer to (first element of) array of values | 
| bsw@1071 | 473       struct berval **val;   // pointer to a value represented as 'struct berval' structure | 
| bsw@1071 | 474       int j;                 // integer to fill a Lua table | 
| bsw@1071 | 475 | 
| bsw@1071 | 476       // push the attribute name to Lua stack position 3: | 
| bsw@1071 | 477       lua_pushstring(L, attr); | 
| bsw@1071 | 478 | 
| bsw@1071 | 479       // create a new table for the attribute's values on Lua stack position 4: | 
| bsw@1071 | 480       lua_newtable(L); | 
| bsw@1071 | 481 | 
| bsw@1071 | 482       // call ldap_get_values_len() to obtain the values | 
| bsw@1071 | 483       // (required to be free'd later with ldap_value_free_len()): | 
| bsw@1071 | 484       vals = ldap_get_values_len(*ldp_ptr, ent, attr); | 
| bsw@1071 | 485 | 
| bsw@1071 | 486       // iterate through values and count 'j' from 1 up: | 
| bsw@1071 | 487       for (val=vals, j=1; *val; val++, j++) { | 
| bsw@1071 | 488 | 
| bsw@1071 | 489         // push value to Lua stack position 5: | 
| bsw@1071 | 490         lua_pushlstring(L, (*val)->bv_val, (*val)->bv_len); | 
| bsw@1071 | 491 | 
| bsw@1071 | 492         // pop value from Lua stack position 5 | 
| bsw@1071 | 493         // and store it in table on Lua stack position 4: | 
| bsw@1071 | 494         lua_rawseti(L, 4, j); | 
| bsw@1071 | 495 | 
| bsw@1071 | 496       } | 
| bsw@1071 | 497 | 
| bsw@1071 | 498       // free data structure of values: | 
| bsw@1071 | 499       ldap_value_free_len(vals); | 
| bsw@1071 | 500 | 
| bsw@1071 | 501       // pop attribute name from Lua stack position 3 | 
| bsw@1071 | 502       // and pop value table from Lua stack position 4 | 
| bsw@1071 | 503       // and store them in result table for entry at Lua stack position 2: | 
| bsw@1071 | 504       lua_settable(L, 2); | 
| bsw@1071 | 505 | 
| bsw@1071 | 506     } | 
| bsw@1071 | 507 | 
| bsw@1071 | 508     // free 'BerElement *ber' stucture that has been used to iterate through all attributes | 
| bsw@1071 | 509     // (second argument is zero due to manpage of ldap_first_attribute()): | 
| bsw@1071 | 510     ber_free(ber, 0); | 
| bsw@1071 | 511 | 
| bsw@1071 | 512     // store distinguished name (DN) on Lua stack position 3 | 
| bsw@1071 | 513     // (aquired memory is free'd with ldap_memfree()): | 
| bsw@1071 | 514     dn = ldap_get_dn(*ldp_ptr, ent); | 
| bsw@1071 | 515     lua_pushstring(L, dn); | 
| bsw@1071 | 516     ldap_memfree(dn); | 
| bsw@1071 | 517 | 
| bsw@1071 | 518     // pop distinguished name (DN) from Lua stack position 3 | 
| bsw@1071 | 519     // and store it in field "dn" of entry result table at stack position 2 | 
| bsw@1071 | 520     lua_setfield(L, 2, "dn"); | 
| bsw@1071 | 521 | 
| bsw@1071 | 522     // pop entry result table from Lua stack position 2 | 
| bsw@1071 | 523     // and store it in table at stack position 1: | 
| bsw@1071 | 524     lua_rawseti(L, 1, i); | 
| bsw@1071 | 525 | 
| bsw@1071 | 526   } | 
| bsw@1071 | 527 | 
| bsw@1071 | 528   // return result table from top of Lua stack (stack position 1): | 
| bsw@1071 | 529   return 1; | 
| bsw@1071 | 530 | 
| bsw@1071 | 531 } | 
| bsw@1071 | 532 | 
| bsw@1071 | 533 static int mldap_unbind(lua_State *L) { | 
| bsw@1071 | 534   // Lua C function used as "unbind" function of module and "unbind" method of Lua userdata object | 
| bsw@1071 | 535   // closing the LDAP connection (if still open) | 
| bsw@1071 | 536   // returning nothing | 
| bsw@1071 | 537 | 
| bsw@1071 | 538   LDAP **ldp_ptr;  // pointer to a pointer to the OpenLDAP structure representing the connection | 
| bsw@1071 | 539 | 
| bsw@1071 | 540   // check if the first argument is a Lua userdata object with the correct metatable | 
| bsw@1071 | 541   // and get a C pointer to that userdata object: | 
| bsw@1071 | 542   ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable"); | 
| bsw@1071 | 543 | 
| bsw@1071 | 544   // check if the Lua userdata object still contains a pointer: | 
| bsw@1071 | 545   if (*ldp_ptr) { | 
| bsw@1071 | 546 | 
| bsw@1071 | 547     // if it does, then call ldap_unbind_ext_s(): | 
| bsw@1071 | 548     ldap_unbind_ext_s( | 
| bsw@1071 | 549       *ldp_ptr,  // pointer to the opaque OpenLDAP structure representing the connection | 
| bsw@1071 | 550       NULL,      // no server controls | 
| bsw@1071 | 551       NULL       // no client controls | 
| bsw@1071 | 552     ); | 
| bsw@1071 | 553 | 
| bsw@1071 | 554     // store NULL pointer in Lua userdata to mark connection as closed | 
| bsw@1071 | 555     *ldp_ptr = NULL; | 
| bsw@1071 | 556   } | 
| bsw@1071 | 557 | 
| bsw@1071 | 558   // returning nothing: | 
| bsw@1071 | 559   return 0; | 
| bsw@1071 | 560 | 
| bsw@1071 | 561 } | 
| bsw@1071 | 562 | 
| bsw@1071 | 563 | 
| bsw@1071 | 564 // registration information for library functions: | 
| bsw@1071 | 565 static const struct luaL_Reg mldap_module_functions[] = { | 
| bsw@1071 | 566   {"bind", mldap_bind}, | 
| bsw@1071 | 567   {"unbind", mldap_unbind}, | 
| bsw@1071 | 568   {NULL, NULL} | 
| bsw@1071 | 569 }; | 
| bsw@1071 | 570 | 
| bsw@1071 | 571 | 
| bsw@1071 | 572 // registration information for methods of connection object: | 
| bsw@1071 | 573 static const struct luaL_Reg mldap_connection_methods[] = { | 
| bsw@1071 | 574   {"search", mldap_search}, | 
| bsw@1071 | 575   {"unbind", mldap_unbind}, | 
| bsw@1071 | 576   {NULL, NULL} | 
| bsw@1071 | 577 }; | 
| bsw@1071 | 578 | 
| bsw@1071 | 579 | 
| bsw@1071 | 580 // registration information for connection metatable: | 
| bsw@1071 | 581 static const struct luaL_Reg mldap_connection_metamethods[] = { | 
| bsw@1071 | 582   {"__gc", mldap_unbind}, | 
| bsw@1071 | 583   {NULL, NULL} | 
| bsw@1071 | 584 }; | 
| bsw@1071 | 585 | 
| bsw@1071 | 586 | 
| bsw@1071 | 587 // luaopen function to initialize/register library: | 
| bsw@1071 | 588 int luaopen_mldap(lua_State *L) { | 
| bsw@1071 | 589 | 
| bsw@1071 | 590   // clear Lua stack: | 
| bsw@1071 | 591   lua_settop(L, 0); | 
| bsw@1071 | 592 | 
| bsw@1071 | 593   // create table with library functions on Lua stack position 1: | 
| bsw@1071 | 594   luaL_newlib(L, mldap_module_functions); | 
| bsw@1071 | 595 | 
| bsw@1071 | 596   // create metatable for connection objects on Lua stack position 2: | 
| bsw@1071 | 597   luaL_newlib(L, mldap_connection_metamethods); | 
| bsw@1071 | 598 | 
| bsw@1071 | 599   // create table with methods for connection object on Lua stack position 3: | 
| bsw@1071 | 600   luaL_newlib(L, mldap_connection_methods); | 
| bsw@1071 | 601 | 
| bsw@1071 | 602   // pop table with methods for connection object from Lua stack position 3 | 
| bsw@1071 | 603   // and store it as "__index" in metatable: | 
| bsw@1071 | 604   lua_setfield(L, 2, "__index"); | 
| bsw@1071 | 605 | 
| bsw@1071 | 606   // pop table with metatable for connection objects from Lua stack position 2 | 
| bsw@1071 | 607   // and store it in the Lua registry: | 
| bsw@1071 | 608   lua_setfield(L, LUA_REGISTRYINDEX, MLDAP_REGKEY "connection_metatable"); | 
| bsw@1071 | 609 | 
| bsw@1071 | 610   // create table for error code mappings on Lua stack position 2: | 
| bsw@1071 | 611   lua_newtable(L); | 
| bsw@1071 | 612 | 
| bsw@1071 | 613   // fill table for error code mappings | 
| bsw@1071 | 614   // that maps integer error codes to error code strings | 
| bsw@1071 | 615   // and vice versa: | 
| bsw@1071 | 616   mldap_set_errorcodes(L); | 
| bsw@1071 | 617 | 
| bsw@1071 | 618   // pop table for error code mappings from Lua stack position 2 | 
| bsw@1071 | 619   // and store it as "errorcodes" in table with library functions: | 
| bsw@1071 | 620   lua_setfield(L, 1, "errorcodes"); | 
| bsw@1071 | 621 | 
| bsw@1071 | 622   // return table with library functions from top of Lua stack: | 
| bsw@1071 | 623   return 1; | 
| bsw@1071 | 624 | 
| bsw@1071 | 625 } |