| 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
 | 
| 
bsw@1071
 | 
    82   int 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
 | 
| 
bsw@1071
 | 
   117   int mandatory,            // if not 0, then the argument is mandatory and an error is raised if it isn't found
 | 
| 
bsw@1071
 | 
   118   lua_Number default_value  // default value to return, if the argument is not mandatory and nil or false
 | 
| 
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):
 | 
| 
bsw@1071
 | 
   148   return value;
 | 
| 
bsw@1071
 | 
   149 
 | 
| 
bsw@1071
 | 
   150   // leaves no new elements on the stack
 | 
| 
bsw@1071
 | 
   151 }
 | 
| 
bsw@1071
 | 
   152 
 | 
| 
bsw@1071
 | 
   153 
 | 
| 
bsw@1071
 | 
   154 static int mldap_scope(
 | 
| 
bsw@1071
 | 
   155   // converts a string ("base", "onelevel", "subtree", "children") to an integer representing the LDAP scope
 | 
| 
bsw@1071
 | 
   156   // and throws an error for any unknown string
 | 
| 
bsw@1071
 | 
   157 
 | 
| 
bsw@1071
 | 
   158   lua_State *L,             // pointer to lua_State variable (needed to throw errors)
 | 
| 
bsw@1071
 | 
   159   const char *scope_string  // string that is either ("base", "onelevel", "subtree", "children")
 | 
| 
bsw@1071
 | 
   160 
 | 
| 
bsw@1071
 | 
   161   // does not affect or use the Lua stack at all
 | 
| 
bsw@1071
 | 
   162 ) {
 | 
| 
bsw@1071
 | 
   163 
 | 
| 
bsw@1071
 | 
   164   // return integer according to string value:
 | 
| 
bsw@1071
 | 
   165   if (!strcmp(scope_string, "base"))     return LDAP_SCOPE_BASE;
 | 
| 
bsw@1071
 | 
   166   if (!strcmp(scope_string, "onelevel")) return LDAP_SCOPE_ONELEVEL;
 | 
| 
bsw@1071
 | 
   167   if (!strcmp(scope_string, "subtree"))  return LDAP_SCOPE_SUBTREE;
 | 
| 
bsw@1071
 | 
   168   if (!strcmp(scope_string, "children")) return LDAP_SCOPE_CHILDREN;
 | 
| 
bsw@1071
 | 
   169 
 | 
| 
bsw@1071
 | 
   170   // throw error for unknown string values:
 | 
| 
bsw@1071
 | 
   171   return luaL_error(L, "Invalid LDAP scope: '%s'", scope_string), 0;
 | 
| 
bsw@1071
 | 
   172 
 | 
| 
bsw@1071
 | 
   173 }
 | 
| 
bsw@1071
 | 
   174 
 | 
| 
bsw@1071
 | 
   175 
 | 
| 
bsw@1071
 | 
   176 static int mldap_bind(lua_State *L) {
 | 
| 
bsw@1071
 | 
   177   // Lua C function that takes named arguments as a table
 | 
| 
bsw@1071
 | 
   178   // and returns a userdata object, representing the LDAP connection
 | 
| 
bsw@1071
 | 
   179   // or returns nil, an error string, and an error code (integer) on error
 | 
| 
bsw@1071
 | 
   180 
 | 
| 
bsw@1071
 | 
   181   // named arguments:
 | 
| 
bsw@1071
 | 
   182   // "uri"      (string)  server URI to connect to
 | 
| 
bsw@1071
 | 
   183   // "who"      (string)  DN to bind as
 | 
| 
bsw@1071
 | 
   184   // "password" (string)  password for DN to bind as
 | 
| 
bsw@1071
 | 
   185   // "timeout"  (number)  timeout in seconds
 | 
| 
bsw@1071
 | 
   186 
 | 
| 
bsw@1071
 | 
   187   static const int ldap_version = LDAP_VERSION3;  // providing a pointer (&ldap_version) to set LDAP protocol version 3
 | 
| 
bsw@1071
 | 
   188   const char *uri;           // C string for "uri" argument
 | 
| 
bsw@1071
 | 
   189   const char *who;           // C string for "who" argument
 | 
| 
bsw@1071
 | 
   190   struct berval cred;        // credentials ("password") are stored as struct berval
 | 
| 
bsw@1071
 | 
   191   lua_Number timeout_float;  // float (lua_Number) for timeout
 | 
| 
bsw@1071
 | 
   192   struct timeval timeout;    // timeout is stored as struct timeval
 | 
| 
bsw@1071
 | 
   193   int ldap_error;            // LDAP error code (as returned by libldap calls)
 | 
| 
bsw@1071
 | 
   194   LDAP *ldp;                 // pointer to an opaque OpenLDAP structure representing the connection
 | 
| 
bsw@1071
 | 
   195   LDAP **ldp_ptr;            // pointer to a Lua userdata structure (that only contains the 'ldp' pointer)
 | 
| 
bsw@1071
 | 
   196 
 | 
| 
bsw@1071
 | 
   197   // throw error if first argument is not a table:
 | 
| 
bsw@1071
 | 
   198   if (lua_type(L, 1) != LUA_TTABLE) {
 | 
| 
bsw@1071
 | 
   199     luaL_error(L, "Argument to function 'bind' is not a table.");
 | 
| 
bsw@1071
 | 
   200   }
 | 
| 
bsw@1071
 | 
   201 
 | 
| 
bsw@1071
 | 
   202   // extract arguments:
 | 
| 
bsw@1071
 | 
   203   uri = mldap_get_named_string_arg(L, 1, "uri", true);
 | 
| 
bsw@1071
 | 
   204   who = mldap_get_named_string_arg(L, 1, "who", false);
 | 
| 
bsw@1071
 | 
   205   cred.bv_val = mldap_get_named_string_arg(L, 1, "password", false);
 | 
| 
bsw@1071
 | 
   206   if (cred.bv_val) cred.bv_len = strlen(cred.bv_val);
 | 
| 
bsw@1071
 | 
   207   else cred.bv_len = 0;
 | 
| 
bsw@1071
 | 
   208   timeout_float = mldap_get_named_number_arg(L, 1, "timeout", false, -1);
 | 
| 
bsw@1071
 | 
   209   timeout.tv_sec = timeout_float;
 | 
| 
bsw@1071
 | 
   210   timeout.tv_usec = (timeout_float - timeout.tv_sec) * 1000000;
 | 
| 
bsw@1071
 | 
   211 
 | 
| 
bsw@1071
 | 
   212   // initialize OpenLDAP structure and provide URI for connection:
 | 
| 
bsw@1071
 | 
   213   ldap_error = ldap_initialize(&ldp, uri);
 | 
| 
bsw@1071
 | 
   214   // on error, jump to label "mldap_queryconn_error1", as no ldap_unbind_ext_s() is needed:
 | 
| 
bsw@1071
 | 
   215   if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error1;
 | 
| 
bsw@1071
 | 
   216 
 | 
| 
bsw@1071
 | 
   217   // set LDAP protocol version 3:
 | 
| 
bsw@1071
 | 
   218   ldap_error = ldap_set_option(ldp, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
 | 
| 
bsw@1071
 | 
   219   // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called:
 | 
| 
bsw@1071
 | 
   220   if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2;
 | 
| 
bsw@1071
 | 
   221 
 | 
| 
bsw@1071
 | 
   222   // set timeout for asynchronous OpenLDAP library calls:
 | 
| 
bsw@1071
 | 
   223   ldap_error = ldap_set_option(ldp, LDAP_OPT_TIMEOUT, &timeout);
 | 
| 
bsw@1071
 | 
   224   // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called:
 | 
| 
bsw@1071
 | 
   225   if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2;
 | 
| 
bsw@1071
 | 
   226 
 | 
| 
bsw@1071
 | 
   227   // connect to LDAP server:
 | 
| 
bsw@1071
 | 
   228   ldap_error = ldap_sasl_bind_s(
 | 
| 
bsw@1071
 | 
   229     ldp,               // pointer to opaque OpenLDAP structure representing the connection
 | 
| 
bsw@1071
 | 
   230     who,               // DN to bind as
 | 
| 
bsw@1071
 | 
   231     LDAP_SASL_SIMPLE,  // SASL mechanism "simple" for password authentication
 | 
| 
bsw@1071
 | 
   232     &cred,             // password as struct berval
 | 
| 
bsw@1071
 | 
   233     NULL,              // no server controls
 | 
| 
bsw@1071
 | 
   234     NULL,              // no client controls
 | 
| 
bsw@1071
 | 
   235     NULL               // do not store server credentials
 | 
| 
bsw@1071
 | 
   236   );
 | 
| 
bsw@1071
 | 
   237 
 | 
| 
bsw@1071
 | 
   238   // error handling:
 | 
| 
bsw@1071
 | 
   239   if (ldap_error != LDAP_SUCCESS) {
 | 
| 
bsw@1071
 | 
   240 
 | 
| 
bsw@1071
 | 
   241     // error label to jump to, if a call of ldap_unbind_ext_s() is required:
 | 
| 
bsw@1071
 | 
   242     mldap_queryconn_error2:
 | 
| 
bsw@1071
 | 
   243 
 | 
| 
bsw@1071
 | 
   244     // close connection and free resources:
 | 
| 
bsw@1071
 | 
   245     ldap_unbind_ext_s(ldp, NULL, NULL);
 | 
| 
bsw@1071
 | 
   246 
 | 
| 
bsw@1071
 | 
   247     // error label to jump to, if no call of ldap_unbind_ext_s() is required:
 | 
| 
bsw@1071
 | 
   248     mldap_queryconn_error1:
 | 
| 
bsw@1071
 | 
   249 
 | 
| 
bsw@1071
 | 
   250     // return three values:
 | 
| 
bsw@1071
 | 
   251     lua_pushnil(L);                                  // return nil as first value
 | 
| 
bsw@1071
 | 
   252     lua_pushstring(L, ldap_err2string(ldap_error));  // return error string as second value
 | 
| 
bsw@1071
 | 
   253     lua_pushinteger(L, ldap_error);                  // return error code (integer) as third value
 | 
| 
bsw@1071
 | 
   254     return 3;
 | 
| 
bsw@1071
 | 
   255 
 | 
| 
bsw@1071
 | 
   256   }
 | 
| 
bsw@1071
 | 
   257 
 | 
| 
bsw@1071
 | 
   258   // create new Lua userdata object (that will contain the 'ldp' pointer) on top of stack:
 | 
| 
bsw@1071
 | 
   259   ldp_ptr = lua_newuserdata(L, sizeof(LDAP *));
 | 
| 
bsw@1071
 | 
   260 
 | 
| 
bsw@1071
 | 
   261   // set metatable of Lua userdata object:
 | 
| 
bsw@1071
 | 
   262   luaL_setmetatable(L, MLDAP_REGKEY "connection_metatable");
 | 
| 
bsw@1071
 | 
   263 
 | 
| 
bsw@1071
 | 
   264   // write contents of Lua userdata object (the 'ldp' pointer):
 | 
| 
bsw@1071
 | 
   265   *ldp_ptr = ldp;
 | 
| 
bsw@1071
 | 
   266 
 | 
| 
bsw@1071
 | 
   267   // return Lua userdata object from top of stack:
 | 
| 
bsw@1071
 | 
   268   return 1;
 | 
| 
bsw@1071
 | 
   269 
 | 
| 
bsw@1071
 | 
   270 }
 | 
| 
bsw@1071
 | 
   271 
 | 
| 
bsw@1071
 | 
   272 
 | 
| 
bsw@1071
 | 
   273 static int mldap_search(lua_State *L) {
 | 
| 
bsw@1071
 | 
   274   // Lua C function used as "search" method of Lua userdata object representing the LDAP connection
 | 
| 
bsw@1071
 | 
   275   // that takes a Lua userdata object (the LDAP connection) as first argument,
 | 
| 
bsw@1071
 | 
   276   // a table with named arguments as second argument,
 | 
| 
bsw@1071
 | 
   277   // and returns a result table on success (see below)
 | 
| 
bsw@1071
 | 
   278   // or returns nil, an error string, and an error code (integer) on error
 | 
| 
bsw@1071
 | 
   279 
 | 
| 
bsw@1071
 | 
   280   // named arguments:
 | 
| 
bsw@1071
 | 
   281   // "base"  (string)   DN of the entry at which to start the search
 | 
| 
bsw@1071
 | 
   282   // "scope" (string)   scope of the search, one of:
 | 
| 
bsw@1071
 | 
   283   //                      "base" to search the object itself
 | 
| 
bsw@1071
 | 
   284   //                      "onelevel" to search the object's immediate children
 | 
| 
bsw@1071
 | 
   285   //                      "subtree" to search the object and all its descendants
 | 
| 
bsw@1071
 | 
   286   //                      "children" to search all of the descendants
 | 
| 
bsw@1071
 | 
   287   // "filter" (string)  string representation of the filter to apply in the search
 | 
| 
bsw@1071
 | 
   288   //                    (conforming to RFC 4515 as extended in RFC 4526)
 | 
| 
bsw@1071
 | 
   289   // "attrs"  (table)   list of attribute descriptions (each a string) to return from matching entries
 | 
| 
bsw@1071
 | 
   290 
 | 
| 
bsw@1071
 | 
   291   // structure of result table:
 | 
| 
bsw@1071
 | 
   292   // {
 | 
| 
bsw@1071
 | 
   293   //   { dn      = <distinguished name (DN)>,
 | 
| 
bsw@1071
 | 
   294   //     <attr1> = { <value1>, <value2>, ... },
 | 
| 
bsw@1071
 | 
   295   //     <attr2> = { <value1>, <value2>, ... },
 | 
| 
bsw@1071
 | 
   296   //     ...
 | 
| 
bsw@1071
 | 
   297   //   },
 | 
| 
bsw@1071
 | 
   298   //   { dn      = <distinguished name (DN)>,
 | 
| 
bsw@1071
 | 
   299   //     <attr1> = { <value1>, <value2>, ... },
 | 
| 
bsw@1071
 | 
   300   //     <attr2> = { <value1>, <value2>, ... },
 | 
| 
bsw@1071
 | 
   301   //     ...
 | 
| 
bsw@1071
 | 
   302   //   },
 | 
| 
bsw@1071
 | 
   303   //   ...
 | 
| 
bsw@1071
 | 
   304   // }
 | 
| 
bsw@1071
 | 
   305 
 | 
| 
bsw@1071
 | 
   306   const char *base;          // C string for "base" argument
 | 
| 
bsw@1071
 | 
   307   const char *scope_string;  // C string for "scope" argument
 | 
| 
bsw@1071
 | 
   308   int scope;                 // integer representing the scope
 | 
| 
bsw@1071
 | 
   309   const char *filter;        // C string for "filter" argument
 | 
| 
bsw@1071
 | 
   310   size_t nattrs;             // number of attributes in "attrs" argument
 | 
| 
bsw@1071
 | 
   311   char **attrs;              // C string array of "attrs" argument
 | 
| 
bsw@1071
 | 
   312   size_t attr_idx;           // index variable for building the C string array of "attrs"
 | 
| 
bsw@1071
 | 
   313   int ldap_error;            // LDAP error code (as returned by libldap calls)
 | 
| 
bsw@1071
 | 
   314   LDAP **ldp_ptr;            // pointer to a pointer to the OpenLDAP structure representing the connection
 | 
| 
bsw@1071
 | 
   315   LDAPMessage *res;          // pointer to the result of ldap_search_ext_s() call
 | 
| 
bsw@1071
 | 
   316   LDAPMessage *ent;          // pointer to an entry in the result of ldap_search_ext_s() call
 | 
| 
bsw@1071
 | 
   317   int i;                     // integer to fill the Lua table returned as result
 | 
| 
bsw@1071
 | 
   318 
 | 
| 
bsw@1071
 | 
   319   // truncate the Lua stack to 2 elements:
 | 
| 
bsw@1071
 | 
   320   lua_settop(L, 2);
 | 
| 
bsw@1071
 | 
   321 
 | 
| 
bsw@1071
 | 
   322   // check if the first argument is a Lua userdata object with the correct metatable
 | 
| 
bsw@1071
 | 
   323   // and get a C pointer to that userdata object:
 | 
| 
bsw@1071
 | 
   324   ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable");
 | 
| 
bsw@1071
 | 
   325 
 | 
| 
bsw@1071
 | 
   326   // throw an error, if the connection has already been closed:
 | 
| 
bsw@1071
 | 
   327   if (!*ldp_ptr) {
 | 
| 
bsw@1071
 | 
   328     return luaL_error(L, "LDAP connection has already been closed");
 | 
| 
bsw@1071
 | 
   329   }
 | 
| 
bsw@1071
 | 
   330 
 | 
| 
bsw@1071
 | 
   331   // check if the second argument is a table, and throw an error if it's not a table:
 | 
| 
bsw@1071
 | 
   332   if (lua_type(L, 2) != LUA_TTABLE) {
 | 
| 
bsw@1071
 | 
   333     luaL_error(L, "Argument to function 'bind' is not a table.");
 | 
| 
bsw@1071
 | 
   334   }
 | 
| 
bsw@1071
 | 
   335 
 | 
| 
bsw@1071
 | 
   336   // extract named arguments (requires memory allocation for 'attrs'):
 | 
| 
bsw@1071
 | 
   337   base = mldap_get_named_string_arg(L, 2, "base", true);  // pushed to 3
 | 
| 
bsw@1071
 | 
   338   scope_string = mldap_get_named_string_arg(L, 2, "scope", true);  // pushed to 4
 | 
| 
bsw@1071
 | 
   339   scope = mldap_scope(L, scope_string);
 | 
| 
bsw@1071
 | 
   340   lua_pop(L, 1);  // removes stack element 4
 | 
| 
bsw@1071
 | 
   341   filter = mldap_get_named_string_arg(L, 2, "filter", false);  // pushed to 4
 | 
| 
bsw@1071
 | 
   342   lua_getfield(L, 2, "attrs");  // pushed to 5
 | 
| 
bsw@1071
 | 
   343   nattrs = lua_len(L, -1);
 | 
| 
bsw@1071
 | 
   344   attrs = calloc(nattrs + 1, sizeof(char *));  // memory allocation, +1 for terminating NULL
 | 
| 
bsw@1071
 | 
   345   if (!attrs) return luaL_error(L, "Memory allocation error in C function 'mldap_queryconn'");
 | 
| 
bsw@1071
 | 
   346   for (attr_idx=0; attr_idx<nattrs; attr_idx++) {
 | 
| 
bsw@1071
 | 
   347     lua_pushinteger(L, attr_idx+1);
 | 
| 
bsw@1071
 | 
   348     lua_gettable(L, 5);  // pushed onto variable stack position
 | 
| 
bsw@1071
 | 
   349     if (lua_type(L, -1) != LUA_TSTRING) {
 | 
| 
bsw@1071
 | 
   350       free(attrs);
 | 
| 
bsw@1071
 | 
   351       return luaL_error(L, "Element in attribute list is not a string");
 | 
| 
bsw@1071
 | 
   352     }
 | 
| 
bsw@1071
 | 
   353     attrs[i] = lua_tostring(L, -1);
 | 
| 
bsw@1071
 | 
   354   }
 | 
| 
bsw@1071
 | 
   355   // attrs[nattrs] = NULL;  // unnecessary due to calloc
 | 
| 
bsw@1071
 | 
   356 
 | 
| 
bsw@1071
 | 
   357   // execute LDAP search and store pointer to the result in 'res':
 | 
| 
bsw@1071
 | 
   358   ldap_error = ldap_search_ext_s(
 | 
| 
bsw@1071
 | 
   359     *ldp_ptr,  // pointer to the opaque OpenLDAP structure representing the connection
 | 
| 
bsw@1071
 | 
   360     base,      // DN of the entry at which to start the search
 | 
| 
bsw@1071
 | 
   361     scope,     // scope of the search
 | 
| 
bsw@1071
 | 
   362     filter,    // string representation of the filter to apply in the search
 | 
| 
bsw@1071
 | 
   363     attrs,     // array of attribute descriptions (array of strings)
 | 
| 
bsw@1071
 | 
   364     0,         // do not only request attribute descriptions but also values
 | 
| 
bsw@1071
 | 
   365     NULL,      // no server controls
 | 
| 
bsw@1071
 | 
   366     NULL,      // no client controls
 | 
| 
bsw@1071
 | 
   367     NULL,      // do not set another timeout
 | 
| 
bsw@1071
 | 
   368     0,         // no sizelimit
 | 
| 
bsw@1071
 | 
   369     &res       // store result in 'res'
 | 
| 
bsw@1071
 | 
   370   );
 | 
| 
bsw@1071
 | 
   371 
 | 
| 
bsw@1071
 | 
   372   // free data structures that have been allocated for the 'attrs' array:
 | 
| 
bsw@1071
 | 
   373   free(attrs);
 | 
| 
bsw@1071
 | 
   374 
 | 
| 
bsw@1071
 | 
   375   // return nil, an error string, and an error code (integer) in case of error:
 | 
| 
bsw@1071
 | 
   376   if (ldap_error != LDAP_SUCCESS) {
 | 
| 
bsw@1071
 | 
   377     lua_pushnil(L);
 | 
| 
bsw@1071
 | 
   378     lua_pushstring(L, ldap_err2string(ldap_error));
 | 
| 
bsw@1071
 | 
   379     lua_pushinteger(L, ldap_error);
 | 
| 
bsw@1071
 | 
   380     return 3;
 | 
| 
bsw@1071
 | 
   381   }
 | 
| 
bsw@1071
 | 
   382 
 | 
| 
bsw@1071
 | 
   383   // clear Lua stack:
 | 
| 
bsw@1071
 | 
   384   lua_settop(L, 0);
 | 
| 
bsw@1071
 | 
   385 
 | 
| 
bsw@1071
 | 
   386   // create result table for all entries at stack position 1:
 | 
| 
bsw@1071
 | 
   387   lua_newtable(L);
 | 
| 
bsw@1071
 | 
   388 
 | 
| 
bsw@1071
 | 
   389   // use ldap_first_entry() and ldap_next_entry() functions
 | 
| 
bsw@1071
 | 
   390   // to iterate through all entries in the result 'res'
 | 
| 
bsw@1071
 | 
   391   // and count 'i' from 1 up:
 | 
| 
bsw@1071
 | 
   392   for (
 | 
| 
bsw@1071
 | 
   393     ent=ldap_first_entry(*ldp_ptr, res), i=1;
 | 
| 
bsw@1071
 | 
   394     ent;
 | 
| 
bsw@1071
 | 
   395     ent=ldap_next_entry(*ldp_ptr, ent), i++
 | 
| 
bsw@1071
 | 
   396   ) {
 | 
| 
bsw@1071
 | 
   397 
 | 
| 
bsw@1071
 | 
   398     char *attr;       // name of attribute
 | 
| 
bsw@1071
 | 
   399     BerElement *ber;  // structure to iterate through attributes
 | 
| 
bsw@1071
 | 
   400     char *dn;         // LDAP entry name (distinguished name)
 | 
| 
bsw@1071
 | 
   401 
 | 
| 
bsw@1071
 | 
   402     // create result table for one entry at stack position 2:
 | 
| 
bsw@1071
 | 
   403     lua_newtable(L);
 | 
| 
bsw@1071
 | 
   404 
 | 
| 
bsw@1071
 | 
   405     // use ldap_first_attribute() and ldap_next_attribute()
 | 
| 
bsw@1071
 | 
   406     // as well as 'BerElement *ber' to iterate through all attributes
 | 
| 
bsw@1071
 | 
   407     // ('ber' must be free'd with ber_free(ber, 0) call later):
 | 
| 
bsw@1071
 | 
   408     for (
 | 
| 
bsw@1071
 | 
   409       attr=ldap_first_attribute(*ldp_ptr, ent, &ber);
 | 
| 
bsw@1071
 | 
   410       attr;
 | 
| 
bsw@1071
 | 
   411       attr=ldap_next_attribute(*ldp_ptr, ent, ber)
 | 
| 
bsw@1071
 | 
   412     ) {
 | 
| 
bsw@1071
 | 
   413 
 | 
| 
bsw@1071
 | 
   414       struct berval **vals;  // pointer to (first element of) array of values
 | 
| 
bsw@1071
 | 
   415       struct berval **val;   // pointer to a value represented as 'struct berval' structure
 | 
| 
bsw@1071
 | 
   416       int j;                 // integer to fill a Lua table
 | 
| 
bsw@1071
 | 
   417 
 | 
| 
bsw@1071
 | 
   418       // push the attribute name to Lua stack position 3:
 | 
| 
bsw@1071
 | 
   419       lua_pushstring(L, attr);
 | 
| 
bsw@1071
 | 
   420 
 | 
| 
bsw@1071
 | 
   421       // create a new table for the attribute's values on Lua stack position 4:
 | 
| 
bsw@1071
 | 
   422       lua_newtable(L);
 | 
| 
bsw@1071
 | 
   423 
 | 
| 
bsw@1071
 | 
   424       // call ldap_get_values_len() to obtain the values
 | 
| 
bsw@1071
 | 
   425       // (required to be free'd later with ldap_value_free_len()):
 | 
| 
bsw@1071
 | 
   426       vals = ldap_get_values_len(*ldp_ptr, ent, attr);
 | 
| 
bsw@1071
 | 
   427 
 | 
| 
bsw@1071
 | 
   428       // iterate through values and count 'j' from 1 up:
 | 
| 
bsw@1071
 | 
   429       for (val=vals, j=1; *val; val++, j++) {
 | 
| 
bsw@1071
 | 
   430 
 | 
| 
bsw@1071
 | 
   431         // push value to Lua stack position 5:
 | 
| 
bsw@1071
 | 
   432         lua_pushlstring(L, (*val)->bv_val, (*val)->bv_len);
 | 
| 
bsw@1071
 | 
   433 
 | 
| 
bsw@1071
 | 
   434         // pop value from Lua stack position 5
 | 
| 
bsw@1071
 | 
   435         // and store it in table on Lua stack position 4:
 | 
| 
bsw@1071
 | 
   436         lua_rawseti(L, 4, j);
 | 
| 
bsw@1071
 | 
   437 
 | 
| 
bsw@1071
 | 
   438       }
 | 
| 
bsw@1071
 | 
   439 
 | 
| 
bsw@1071
 | 
   440       // free data structure of values:
 | 
| 
bsw@1071
 | 
   441       ldap_value_free_len(vals);
 | 
| 
bsw@1071
 | 
   442 
 | 
| 
bsw@1071
 | 
   443       // pop attribute name from Lua stack position 3
 | 
| 
bsw@1071
 | 
   444       // and pop value table from Lua stack position 4
 | 
| 
bsw@1071
 | 
   445       // and store them in result table for entry at Lua stack position 2:
 | 
| 
bsw@1071
 | 
   446       lua_settable(L, 2);
 | 
| 
bsw@1071
 | 
   447 
 | 
| 
bsw@1071
 | 
   448     }
 | 
| 
bsw@1071
 | 
   449 
 | 
| 
bsw@1071
 | 
   450     // free 'BerElement *ber' stucture that has been used to iterate through all attributes
 | 
| 
bsw@1071
 | 
   451     // (second argument is zero due to manpage of ldap_first_attribute()):
 | 
| 
bsw@1071
 | 
   452     ber_free(ber, 0);
 | 
| 
bsw@1071
 | 
   453 
 | 
| 
bsw@1071
 | 
   454     // store distinguished name (DN) on Lua stack position 3
 | 
| 
bsw@1071
 | 
   455     // (aquired memory is free'd with ldap_memfree()):
 | 
| 
bsw@1071
 | 
   456     dn = ldap_get_dn(*ldp_ptr, ent);
 | 
| 
bsw@1071
 | 
   457     lua_pushstring(L, dn);
 | 
| 
bsw@1071
 | 
   458     ldap_memfree(dn);
 | 
| 
bsw@1071
 | 
   459 
 | 
| 
bsw@1071
 | 
   460     // pop distinguished name (DN) from Lua stack position 3
 | 
| 
bsw@1071
 | 
   461     // and store it in field "dn" of entry result table at stack position 2
 | 
| 
bsw@1071
 | 
   462     lua_setfield(L, 2, "dn");
 | 
| 
bsw@1071
 | 
   463 
 | 
| 
bsw@1071
 | 
   464     // pop entry result table from Lua stack position 2
 | 
| 
bsw@1071
 | 
   465     // and store it in table at stack position 1:
 | 
| 
bsw@1071
 | 
   466     lua_rawseti(L, 1, i);
 | 
| 
bsw@1071
 | 
   467     
 | 
| 
bsw@1071
 | 
   468   }
 | 
| 
bsw@1071
 | 
   469 
 | 
| 
bsw@1071
 | 
   470   // return result table from top of Lua stack (stack position 1):
 | 
| 
bsw@1071
 | 
   471   return 1;
 | 
| 
bsw@1071
 | 
   472 
 | 
| 
bsw@1071
 | 
   473 }
 | 
| 
bsw@1071
 | 
   474 
 | 
| 
bsw@1071
 | 
   475 static int mldap_unbind(lua_State *L) {
 | 
| 
bsw@1071
 | 
   476   // Lua C function used as "unbind" function of module and "unbind" method of Lua userdata object
 | 
| 
bsw@1071
 | 
   477   // closing the LDAP connection (if still open)
 | 
| 
bsw@1071
 | 
   478   // returning nothing
 | 
| 
bsw@1071
 | 
   479 
 | 
| 
bsw@1071
 | 
   480   LDAP **ldp_ptr;  // pointer to a pointer to the OpenLDAP structure representing the connection
 | 
| 
bsw@1071
 | 
   481 
 | 
| 
bsw@1071
 | 
   482   // check if the first argument is a Lua userdata object with the correct metatable
 | 
| 
bsw@1071
 | 
   483   // and get a C pointer to that userdata object:
 | 
| 
bsw@1071
 | 
   484   ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable");
 | 
| 
bsw@1071
 | 
   485 
 | 
| 
bsw@1071
 | 
   486   // check if the Lua userdata object still contains a pointer:
 | 
| 
bsw@1071
 | 
   487   if (*ldp_ptr) {
 | 
| 
bsw@1071
 | 
   488 
 | 
| 
bsw@1071
 | 
   489     // if it does, then call ldap_unbind_ext_s():
 | 
| 
bsw@1071
 | 
   490     ldap_unbind_ext_s(
 | 
| 
bsw@1071
 | 
   491       *ldp_ptr,  // pointer to the opaque OpenLDAP structure representing the connection
 | 
| 
bsw@1071
 | 
   492       NULL,      // no server controls
 | 
| 
bsw@1071
 | 
   493       NULL       // no client controls
 | 
| 
bsw@1071
 | 
   494     );
 | 
| 
bsw@1071
 | 
   495 
 | 
| 
bsw@1071
 | 
   496     // store NULL pointer in Lua userdata to mark connection as closed
 | 
| 
bsw@1071
 | 
   497     *ldp_ptr = NULL;
 | 
| 
bsw@1071
 | 
   498   }
 | 
| 
bsw@1071
 | 
   499 
 | 
| 
bsw@1071
 | 
   500   // returning nothing:
 | 
| 
bsw@1071
 | 
   501   return 0;
 | 
| 
bsw@1071
 | 
   502 
 | 
| 
bsw@1071
 | 
   503 }
 | 
| 
bsw@1071
 | 
   504 
 | 
| 
bsw@1071
 | 
   505 
 | 
| 
bsw@1071
 | 
   506 // registration information for library functions:
 | 
| 
bsw@1071
 | 
   507 static const struct luaL_Reg mldap_module_functions[] = {
 | 
| 
bsw@1071
 | 
   508   {"bind", mldap_bind},
 | 
| 
bsw@1071
 | 
   509   {"unbind", mldap_unbind},
 | 
| 
bsw@1071
 | 
   510   {NULL, NULL}
 | 
| 
bsw@1071
 | 
   511 };
 | 
| 
bsw@1071
 | 
   512 
 | 
| 
bsw@1071
 | 
   513 
 | 
| 
bsw@1071
 | 
   514 // registration information for methods of connection object: 
 | 
| 
bsw@1071
 | 
   515 static const struct luaL_Reg mldap_connection_methods[] = {
 | 
| 
bsw@1071
 | 
   516   {"search", mldap_search},
 | 
| 
bsw@1071
 | 
   517   {"unbind", mldap_unbind},
 | 
| 
bsw@1071
 | 
   518   {NULL, NULL}
 | 
| 
bsw@1071
 | 
   519 };
 | 
| 
bsw@1071
 | 
   520 
 | 
| 
bsw@1071
 | 
   521 
 | 
| 
bsw@1071
 | 
   522 // registration information for connection metatable: 
 | 
| 
bsw@1071
 | 
   523 static const struct luaL_Reg mldap_connection_metamethods[] = {
 | 
| 
bsw@1071
 | 
   524   {"__gc", mldap_unbind},
 | 
| 
bsw@1071
 | 
   525   {NULL, NULL}
 | 
| 
bsw@1071
 | 
   526 };
 | 
| 
bsw@1071
 | 
   527 
 | 
| 
bsw@1071
 | 
   528 
 | 
| 
bsw@1071
 | 
   529 // luaopen function to initialize/register library:
 | 
| 
bsw@1071
 | 
   530 int luaopen_mldap(lua_State *L) {
 | 
| 
bsw@1071
 | 
   531 
 | 
| 
bsw@1071
 | 
   532   // clear Lua stack:
 | 
| 
bsw@1071
 | 
   533   lua_settop(L, 0);
 | 
| 
bsw@1071
 | 
   534 
 | 
| 
bsw@1071
 | 
   535   // create table with library functions on Lua stack position 1:
 | 
| 
bsw@1071
 | 
   536   luaL_newlib(L, mldap_module_functions);
 | 
| 
bsw@1071
 | 
   537 
 | 
| 
bsw@1071
 | 
   538   // create metatable for connection objects on Lua stack position 2:
 | 
| 
bsw@1071
 | 
   539   luaL_newlib(L, mldap_connection_metamethods);
 | 
| 
bsw@1071
 | 
   540 
 | 
| 
bsw@1071
 | 
   541   // create table with methods for connection object on Lua stack position 3:
 | 
| 
bsw@1071
 | 
   542   luaL_newlib(L, mldap_connection_methods);
 | 
| 
bsw@1071
 | 
   543 
 | 
| 
bsw@1071
 | 
   544   // pop table with methods for connection object from Lua stack position 3
 | 
| 
bsw@1071
 | 
   545   // and store it as "__index" in metatable:
 | 
| 
bsw@1071
 | 
   546   lua_setfield(L, 2, "__index");
 | 
| 
bsw@1071
 | 
   547 
 | 
| 
bsw@1071
 | 
   548   // pop table with metatable for connection objects from Lua stack position 2
 | 
| 
bsw@1071
 | 
   549   // and store it in the Lua registry:
 | 
| 
bsw@1071
 | 
   550   lua_setfield(L, LUA_REGISTRYINDEX, MLDAP_REGKEY "connection_metatable");
 | 
| 
bsw@1071
 | 
   551 
 | 
| 
bsw@1071
 | 
   552   // create table for error code mappings on Lua stack position 2:
 | 
| 
bsw@1071
 | 
   553   lua_newtable(L);
 | 
| 
bsw@1071
 | 
   554 
 | 
| 
bsw@1071
 | 
   555   // fill table for error code mappings
 | 
| 
bsw@1071
 | 
   556   // that maps integer error codes to error code strings
 | 
| 
bsw@1071
 | 
   557   // and vice versa:
 | 
| 
bsw@1071
 | 
   558   mldap_set_errorcodes(L);
 | 
| 
bsw@1071
 | 
   559 
 | 
| 
bsw@1071
 | 
   560   // pop table for error code mappings from Lua stack position 2
 | 
| 
bsw@1071
 | 
   561   // and store it as "errorcodes" in table with library functions:
 | 
| 
bsw@1071
 | 
   562   lua_setfield(L, 1, "errorcodes");
 | 
| 
bsw@1071
 | 
   563 
 | 
| 
bsw@1071
 | 
   564   // return table with library functions from top of Lua stack:
 | 
| 
bsw@1071
 | 
   565   return 1;
 | 
| 
bsw@1071
 | 
   566 
 | 
| 
bsw@1071
 | 
   567 }
 |