liquid_feedback_frontend

view lib/mldap/mldap.c @ 1071:58f48a8a202a

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

Impressum / About Us