r25446: Merge some changes I made on the way home from SFO:
[jelmer/samba4-debian.git] / source / scripting / ejs / smbcalls_ldb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    provide hooks into smbd C calls from ejs scripts
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Jelmer Vernooij 2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "scripting/ejs/smbcalls.h"
25 #include "lib/appweb/ejs/ejs.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "lib/ldb/include/ldb_errors.h"
28 #include "db_wrap.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "librpc/ndr/libndr.h"
31
32 /*
33   get the connected db
34  */
35 static struct ldb_context *ejs_get_ldb_context(int eid)
36 {
37         struct ldb_context *ldb = (struct ldb_context *)mprGetThisPtr(eid, "db");
38         if (ldb == NULL) {
39                 ejsSetErrorMsg(eid, "invalid ldb connection");
40         }
41         return ldb;
42 }
43
44 /*
45   perform an ldb search, returning an array of results
46
47   syntax:
48      res = ldb.search("expression");
49      var attrs = new Array("attr1", "attr2", "attr3");
50      ldb.search("expression", attrs);
51      var basedn = "cn=this,dc=is,dc=a,dc=test";
52      ldb.search("expression", basedn, ldb.SCOPE_SUBTREE, attrs);
53      ldb.search("expression", basedn, ldb.SCOPE_SUBTREE, attrs, controls);
54 */
55 static int ejs_ldbSearch(MprVarHandle eid, int argc, struct MprVar **argv)
56 {
57         const char **attrs = NULL;
58         const char *expression;
59         const char *base = NULL;
60         struct ldb_dn *basedn = NULL;
61         int scope = LDB_SCOPE_DEFAULT;
62         TALLOC_CTX *tmp_ctx = talloc_new(mprMemCtx());
63         struct ldb_context *ldb;
64         int ret;
65         struct ldb_control **parsed_controls = NULL;
66         struct ldb_result *res=NULL;
67         struct ldb_request *req;
68
69         /* validate arguments */
70         if (argc < 1 || argc > 5) {
71                 ejsSetErrorMsg(eid, "ldb.search invalid number of arguments");
72                 goto failed;
73         }
74         if (argc > 3 && argv[3]->type != MPR_TYPE_OBJECT) {
75                 ejsSetErrorMsg(eid, "ldb.search attributes must be an object");
76                 goto failed;
77         }
78
79         ldb = ejs_get_ldb_context(eid);
80         if (ldb == NULL) {
81                 return -1;
82         }
83         
84         expression = mprToString(argv[0]);
85         if (argc > 1) {
86                 base = mprToString(argv[1]);
87                 /* a null basedn is valid */
88         }
89         if (base != NULL) {
90                 basedn = ldb_dn_new(tmp_ctx, ldb, base);
91                 if ( ! ldb_dn_validate(basedn)) {
92                         ejsSetErrorMsg(eid, "ldb.search malformed base dn");
93                         goto failed;
94                 }
95         } else {
96                 basedn = ldb_get_default_basedn(ldb);
97         }
98         if (argc > 2) {
99                 scope = mprToInt(argv[2]);
100                 switch (scope) {
101                         case LDB_SCOPE_DEFAULT:
102                         case LDB_SCOPE_BASE:
103                         case LDB_SCOPE_ONELEVEL:
104                         case LDB_SCOPE_SUBTREE:
105                                 break; /* ok */
106                         default:
107                                 ejsSetErrorMsg(eid, "ldb.search invalid scope");
108                                 goto failed;
109                 }
110         }
111         if (argc > 3) {
112                 attrs = mprToList(tmp_ctx, argv[3]);
113         }
114         if (argc > 4) {
115                 const char **controls;
116                 controls = mprToList(tmp_ctx, argv[4]);
117                 if (controls) {
118                         parsed_controls = ldb_parse_control_strings(ldb, tmp_ctx, controls);
119                         if (!parsed_controls) {
120                                 ejsSetErrorMsg(eid, "ldb.search cannot parse controls: %s", 
121                                                ldb_errstring(ldb));
122                                 goto failed;
123                         }
124                 }
125         }
126
127         res = talloc_zero(tmp_ctx, struct ldb_result);
128         if (!res) {
129                 return LDB_ERR_OPERATIONS_ERROR;
130         }
131
132         ret = ldb_build_search_req(&req, ldb, tmp_ctx,
133                                    basedn,
134                                    scope,
135                                    expression,
136                                    attrs,
137                                    parsed_controls,
138                                    res,
139                                    ldb_search_default_callback);
140
141         if (ret == LDB_SUCCESS) {
142
143                 ldb_set_timeout(ldb, req, 0); /* use default timeout */
144                 
145                 ret = ldb_request(ldb, req);
146                 
147                 if (ret == LDB_SUCCESS) {
148                         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
149                 }
150         }
151
152         if (ret != LDB_SUCCESS) {
153                 ejsSetErrorMsg(eid, "ldb.search failed - %s", ldb_errstring(ldb));
154                 mpr_Return(eid, mprLdbResult(ldb, ret, NULL));
155         } else {
156                 mpr_Return(eid, mprLdbResult(ldb, ret, res));
157         }
158
159         talloc_free(tmp_ctx);
160         return 0;
161
162 failed:
163         talloc_free(tmp_ctx);
164         return -1;
165 }
166
167
168 /*
169   perform an ldb add or modify
170 */
171 static int ejs_ldbAddModify(MprVarHandle eid, int argc, struct MprVar **argv,
172                             int fn(struct ldb_context *, const struct ldb_message *))
173 {
174         const char *ldifstring;
175         struct ldb_context *ldb;
176         struct ldb_ldif *ldif;
177         int ret = 0, count=0;
178
179         if (argc != 1) {
180                 ejsSetErrorMsg(eid, "ldb.add/modify invalid arguments");
181                 return -1;
182         }
183
184         ldifstring = mprToString(argv[0]);
185         if (ldifstring == NULL) {
186                 ejsSetErrorMsg(eid, "ldb.add/modify invalid arguments");
187                 return -1;
188         }
189
190         ldb = ejs_get_ldb_context(eid);
191         if (ldb == NULL) {
192                 return -1;
193         }
194
195         while ((ldif = ldb_ldif_read_string(ldb, &ldifstring))) {
196                 count++;
197                 ret = fn(ldb, ldif->msg);
198                 talloc_free(ldif);
199                 if (ret != 0) break;
200         }
201
202         if (count == 0) {
203                 ejsSetErrorMsg(eid, "ldb.add/modify invalid ldif");
204                 return -1;
205         }
206
207         mpr_Return(eid, mprLdbResult(ldb, ret, NULL));
208         return 0;
209 }
210
211
212 /*
213   perform an ldb delete
214   usage:
215    ok = ldb.delete(dn);
216 */
217 static int ejs_ldbDelete(MprVarHandle eid, int argc, struct MprVar **argv)
218 {
219         struct ldb_dn *dn;
220         struct ldb_context *ldb;
221         int ret;
222
223         if (argc != 1) {
224                 ejsSetErrorMsg(eid, "ldb.delete invalid arguments");
225                 return -1;
226         }
227
228         ldb = ejs_get_ldb_context(eid);
229         if (ldb == NULL) {
230                 return -1;
231         }
232
233         dn = ldb_dn_new(ldb, ldb, mprToString(argv[0]));
234         if ( ! ldb_dn_validate(dn)) {
235                 ejsSetErrorMsg(eid, "ldb.delete malformed dn");
236                 return -1;
237         }
238
239         ret = ldb_delete(ldb, dn);
240
241         talloc_free(dn);
242
243         mpr_Return(eid, mprLdbResult(ldb, ret, NULL));
244         return 0;
245 }
246
247 /*
248   perform an ldb rename
249   usage:
250    ok = ldb.rename(dn1, dn2);
251 */
252 static int ejs_ldbRename(MprVarHandle eid, int argc, struct MprVar **argv)
253 {
254         struct ldb_dn *dn1, *dn2;
255         struct ldb_context *ldb;
256         int ret;
257
258         if (argc != 2) {
259                 ejsSetErrorMsg(eid, "ldb.rename invalid arguments");
260                 return -1;
261         }
262
263         ldb = ejs_get_ldb_context(eid);
264         if (ldb == NULL) {
265                 return -1;
266         }
267
268         dn1 = ldb_dn_new(ldb, ldb, mprToString(argv[0]));
269         dn2 = ldb_dn_new(ldb, ldb, mprToString(argv[1]));
270         if ( ! ldb_dn_validate(dn1) ||  ! ldb_dn_validate(dn2)) {
271                 ejsSetErrorMsg(eid, "ldb.rename invalid or malformed arguments");
272                 return -1;
273         }
274
275         ret = ldb_rename(ldb, dn1, dn2);
276
277         talloc_free(dn1);
278         talloc_free(dn2);
279
280         mpr_Return(eid, mprLdbResult(ldb, ret, NULL));
281         return 0;
282 }
283
284 /*
285   get last error message
286   usage:
287    ok = ldb.errstring();
288 */
289 static int ejs_ldbErrstring(MprVarHandle eid, int argc, struct MprVar **argv)
290 {
291         struct ldb_context *ldb;
292
293         ldb = ejs_get_ldb_context(eid);
294         if (ldb == NULL) {
295                 return -1;
296         }
297
298         mpr_Return(eid, mprString(ldb_errstring(ldb)));
299         return 0;
300 }
301
302 /* 
303    base64 encode 
304    usage: 
305     dataout = ldb.encode(datain)
306  */
307 static int ejs_base64encode(MprVarHandle eid, int argc, struct MprVar **argv)
308 {
309         char *ret;
310
311         if (argc != 1) {
312                 ejsSetErrorMsg(eid, "ldb.base64encode invalid argument count");
313                 return -1;
314         }
315
316         if (argv[0]->type == MPR_TYPE_STRING) {
317                 const char *orig = mprToString(argv[0]);
318                 ret = ldb_base64_encode(mprMemCtx(), orig, strlen(orig));
319         } else {
320                 DATA_BLOB *blob;
321
322                 blob = mprToDataBlob(argv[0]);
323                 mprAssert(blob);
324                 ret = ldb_base64_encode(mprMemCtx(), (char *)blob->data, blob->length);
325         }
326                 
327         if (!ret) {
328                 mpr_Return(eid, mprCreateUndefinedVar());
329         } else {
330                 mpr_Return(eid, mprString(ret));
331         }
332
333         talloc_free(ret);
334
335         return 0;
336 }
337
338 /* 
339    base64 decode
340    usage:
341      dataout = ldb.decode(datain)
342  */
343 static int ejs_base64decode(MprVarHandle eid, int argc, struct MprVar **argv)
344 {
345         char *tmp;
346         int ret;
347         
348         if (argc != 1) {
349                 ejsSetErrorMsg(eid, "ldb.base64encode invalid argument count");
350                 return -1;
351         }
352
353         tmp = talloc_strdup(mprMemCtx(), mprToString(argv[0]));
354         ret = ldb_base64_decode(tmp);
355         if (ret == -1) {
356                 mpr_Return(eid, mprCreateUndefinedVar());
357         } else {
358                 DATA_BLOB blob;
359                 blob.data = (uint8_t *)tmp;
360                 blob.length = ret;
361                 mpr_Return(eid, mprDataBlob(blob));
362         }
363
364         talloc_free(tmp);
365
366         return 0;
367 }
368
369 /* 
370    escape a DN
371    usage:
372      dataout = ldb.dn_escape(datain)
373  */
374 static int ejs_dn_escape(MprVarHandle eid, int argc, struct MprVar **argv)
375 {
376         char *ret;
377         struct ldb_val val;
378         
379         if (argc != 1) {
380                 ejsSetErrorMsg(eid, "ldb.dn_escape invalid argument count");
381                 return -1;
382         }
383
384         val = data_blob_string_const(mprToString(argv[0]));
385
386         ret = ldb_dn_escape_value(mprMemCtx(), val);
387         if (ret == NULL) {
388                 mpr_Return(eid, mprCreateUndefinedVar());
389         } else {
390                 mpr_Return(eid, mprString(ret));
391                 talloc_free(ret);
392         }
393
394         return 0;
395 }
396
397 /*
398   perform an ldb add 
399
400   syntax:
401     ok = ldb.add(ldifstring);
402 */
403 static int ejs_ldbAdd(MprVarHandle eid, int argc, struct MprVar **argv)
404 {
405         return ejs_ldbAddModify(eid, argc, argv, ldb_add);
406 }
407
408 /*
409   perform an ldb modify
410
411   syntax:
412     ok = ldb.modify(ldifstring);
413 */
414 static int ejs_ldbModify(MprVarHandle eid, int argc, struct MprVar **argv)
415 {
416         return ejs_ldbAddModify(eid, argc, argv, ldb_modify);
417 }
418
419 /*
420   connect to a database
421   usage:
422    ok = ldb.connect(dbfile);
423    ok = ldb.connect(dbfile, "modules:modlist");
424
425   ldb.credentials or ldb.session_info may be setup first
426
427 */
428 static int ejs_ldbConnect(MprVarHandle eid, int argc, char **argv)
429 {
430         struct ldb_context *ldb;
431         struct auth_session_info *session_info = NULL;
432         struct cli_credentials *creds = NULL;
433         struct MprVar *credentials, *session;
434         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
435
436         const char *dbfile;
437
438         if (argc < 1) {
439                 ejsSetErrorMsg(eid, "ldb.connect invalid arguments");
440                 return -1;
441         }
442
443         credentials = mprGetProperty(this, "credentials", NULL);
444         if (credentials) {
445                 creds = talloc_get_type(mprGetPtr(credentials, "creds"), struct cli_credentials);
446         }
447
448         session = mprGetProperty(this, "session_info", NULL);
449         if (session) {
450                 session_info = talloc_get_type(mprGetPtr(session, "session_info"), struct auth_session_info);
451         }
452
453         dbfile = argv[0];
454
455         ldb = ldb_wrap_connect(mprMemCtx(), global_loadparm, dbfile, 
456                                session_info, creds,
457                                0, (const char **)(argv+1));
458         if (ldb == NULL) {
459                 ejsSetErrorMsg(eid, "ldb.connect failed to open %s", dbfile);
460         }
461
462         mprSetThisPtr(eid, "db", ldb);
463         mpr_Return(eid, mprCreateBoolVar(ldb != NULL));
464         return 0;
465 }
466
467
468 /*
469   close a db connection
470 */
471 static int ejs_ldbClose(MprVarHandle eid, int argc, struct MprVar **argv)
472 {
473         struct ldb_context *ldb;
474
475         if (argc != 0) {
476                 ejsSetErrorMsg(eid, "ldb.close invalid arguments");
477                 return -1;
478         }
479
480         ldb = ejs_get_ldb_context(eid);
481         if (ldb == NULL) {
482                 return -1;
483         }
484
485         mprSetThisPtr(eid, "db", NULL);
486         mpr_Return(eid, mprCreateBoolVar(True));
487         return 0;
488 }
489
490
491 /*
492   start a ldb transaction
493   usage:
494    ok = ldb.transaction_start();
495 */
496 static int ejs_ldbTransactionStart(MprVarHandle eid, int argc, struct MprVar **argv)
497 {
498         struct ldb_context *ldb;
499         int ret;
500
501         if (argc != 0) {
502                 ejsSetErrorMsg(eid, "ldb.transaction_start invalid arguments");
503                 return -1;
504         }
505
506         ldb = ejs_get_ldb_context(eid);
507         if (ldb == NULL) {
508                 return -1;
509         }
510
511         ret = ldb_transaction_start(ldb);
512
513         mpr_Return(eid, mprCreateBoolVar(ret == 0));
514         return 0;
515 }
516
517 /*
518   cancel a ldb transaction
519   usage:
520    ok = ldb.transaction_cancel();
521 */
522 static int ejs_ldbTransactionCancel(MprVarHandle eid, int argc, struct MprVar **argv)
523 {
524         struct ldb_context *ldb;
525         int ret;
526
527         if (argc != 0) {
528                 ejsSetErrorMsg(eid, "ldb.transaction_cancel invalid arguments");
529                 return -1;
530         }
531
532         ldb = ejs_get_ldb_context(eid);
533         if (ldb == NULL) {
534                 return -1;
535         }
536
537         ret = ldb_transaction_cancel(ldb);
538
539         mpr_Return(eid, mprCreateBoolVar(ret == 0));
540         return 0;
541 }
542
543 /*
544   commit a ldb transaction
545   usage:
546    ok = ldb.transaction_commit();
547 */
548 static int ejs_ldbTransactionCommit(MprVarHandle eid, int argc, struct MprVar **argv)
549 {
550         struct ldb_context *ldb;
551         int ret;
552
553         if (argc != 0) {
554                 ejsSetErrorMsg(eid, "ldb.transaction_commit invalid arguments");
555                 return -1;
556         }
557
558         ldb = ejs_get_ldb_context(eid);
559         if (ldb == NULL) {
560                 return -1;
561         }
562
563         ret = ldb_transaction_commit(ldb);
564
565         mpr_Return(eid, mprCreateBoolVar(ret == 0));
566         return 0;
567 }
568
569 /*
570   commit a ldb attach a dsdb_schema from ldif files
571   usage:
572    ok = ldb.attach_dsdb_schema_from_ldif("prefixMap ldif content", "definition ldif content")
573 */
574 static int ejs_ldb_attach_dsdb_schema_from_ldif(MprVarHandle eid, int argc, char **argv)
575 {
576         struct ldb_context *ldb;
577         WERROR status;
578         const char *pf;
579         const char *df;
580
581         if (argc != 2) {
582                 ejsSetErrorMsg(eid, "ldb.attach_dsdb_schema_from_ldif invalid arguments");
583                 return -1;
584         }
585
586         ldb = ejs_get_ldb_context(eid);
587         if (ldb == NULL) {
588                 return -1;
589         }
590
591         pf = argv[0];
592         df = argv[1];
593
594         status = dsdb_attach_schema_from_ldif_file(ldb, pf, df);
595
596         mpr_Return(eid, mprWERROR(status));
597         return 0;
598 }
599
600 /*
601   commit a ldb attach a dsdb_schema from ldif files
602   usage:
603    ok = ldb.set_ntds_invocationId("7729aa4b-f990-41ad-b81a-8b6a14090f41");
604 */
605 static int ejs_ldb_set_ntds_invocationId(MprVarHandle eid, int argc, char **argv)
606 {
607         struct ldb_context *ldb;
608         NTSTATUS status;
609         struct GUID guid;
610         char *guid_str;
611         bool ok;
612
613         if (argc != 1) {
614                 ejsSetErrorMsg(eid, "ldb.set_ntds_invocationId invalid arguments");
615                 return -1;
616         }
617
618         ldb = ejs_get_ldb_context(eid);
619         if (ldb == NULL) {
620                 return -1;
621         }
622
623         guid_str = argv[0];
624
625         status = GUID_from_string(guid_str, &guid);
626         if (!NT_STATUS_IS_OK(status)) {
627                 ejsSetErrorMsg(eid, "ldb.set_ntds_invocationId - failed to parse GUID '%s' %s\n",
628                                 guid_str, nt_errstr(status));
629                 return -1;
630         }
631
632         ok = samdb_set_ntds_invocation_id(ldb, &guid);
633         if (!ok) {
634                 ejsSetErrorMsg(eid, "ldb.set_ntds_invocationId - failed to set cached ntds invocationId\n");
635                 return -1;
636         }
637
638         mpr_Return(eid, mprCreateBoolVar(ok));
639         return 0;
640 }
641
642 /*
643   commit a ldb attach a dsdb_schema from ldif files
644   usage:
645    ok = ldb.get_ntds_objectGUID("7729aa4b-f990-41ad-b81a-8b6a14090f41");
646 */
647 static int ejs_ldb_set_ntds_objectGUID(MprVarHandle eid, int argc, char **argv)
648 {
649         struct ldb_context *ldb;
650         NTSTATUS status;
651         struct GUID guid;
652         char *guid_str;
653         bool ok;
654
655         if (argc != 1) {
656                 ejsSetErrorMsg(eid, "ldb.set_ntds_objectGUID invalid arguments");
657                 return -1;
658         }
659
660         ldb = ejs_get_ldb_context(eid);
661         if (ldb == NULL) {
662                 return -1;
663         }
664
665         guid_str = argv[0];
666
667         status = GUID_from_string(guid_str, &guid);
668         if (!NT_STATUS_IS_OK(status)) {
669                 ejsSetErrorMsg(eid, "ldb.set_ntds_objectGUID - failed to parse GUID '%s' %s\n",
670                                 guid_str, nt_errstr(status));
671                 return -1;
672         }
673
674         ok = samdb_set_ntds_invocation_id(ldb, &guid);
675         if (!ok) {
676                 ejsSetErrorMsg(eid, "ldb.set_ntds_objectGUID - failed to set cached ntds invocationId\n");
677                 return -1;
678         }
679
680         mpr_Return(eid, mprCreateBoolVar(ok));
681         return 0;
682 }
683
684 /*
685   initialise ldb ejs subsystem
686 */
687 static int ejs_ldb_init(MprVarHandle eid, int argc, struct MprVar **argv)
688 {
689         struct MprVar *ldb = mprInitObject(eid, "ldb", argc, argv);
690
691         mprSetStringCFunction(ldb, "connect", ejs_ldbConnect);
692         mprSetCFunction(ldb, "search", ejs_ldbSearch);
693         mprSetCFunction(ldb, "add", ejs_ldbAdd);
694         mprSetCFunction(ldb, "modify", ejs_ldbModify);
695         mprSetCFunction(ldb, "del", ejs_ldbDelete);
696         mprSetCFunction(ldb, "rename", ejs_ldbRename);
697         mprSetCFunction(ldb, "errstring", ejs_ldbErrstring);
698         mprSetCFunction(ldb, "encode", ejs_base64encode);
699         mprSetCFunction(ldb, "decode", ejs_base64decode);
700         mprSetCFunction(ldb, "dn_escape", ejs_dn_escape);
701         mprSetCFunction(ldb, "close", ejs_ldbClose);
702         mprSetCFunction(ldb, "transaction_start", ejs_ldbTransactionStart);
703         mprSetCFunction(ldb, "transaction_cancel", ejs_ldbTransactionCancel);
704         mprSetCFunction(ldb, "transaction_commit", ejs_ldbTransactionCommit);
705         mprSetStringCFunction(ldb, "attach_dsdb_schema_from_ldif",
706                               ejs_ldb_attach_dsdb_schema_from_ldif);
707         mprSetStringCFunction(ldb, "set_ntds_invocationId",
708                               ejs_ldb_set_ntds_invocationId);
709         mprSetStringCFunction(ldb, "set_ntds_objectGUID",
710                               ejs_ldb_set_ntds_objectGUID);
711         mprSetVar(ldb, "SCOPE_BASE", mprCreateNumberVar(LDB_SCOPE_BASE));
712         mprSetVar(ldb, "SCOPE_ONE", mprCreateNumberVar(LDB_SCOPE_ONELEVEL));
713         mprSetVar(ldb, "SCOPE_SUBTREE", mprCreateNumberVar(LDB_SCOPE_SUBTREE));
714         mprSetVar(ldb, "SCOPE_DEFAULT", mprCreateNumberVar(LDB_SCOPE_DEFAULT));
715
716         return 0;
717 }
718
719
720 /*
721   setup C functions that be called from ejs
722 */
723 NTSTATUS smb_setup_ejs_ldb(void)
724 {
725         ejsDefineCFunction(-1, "ldb_init", ejs_ldb_init, NULL, MPR_VAR_SCRIPT_HANDLE);
726         return NT_STATUS_OK;
727 }