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 }
|