r23792: convert Samba4 to GPLv3
[sfrench/samba-autobuild/.git] / source4 / 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 = 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(), 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         char *pf_name;
579         char *df_name;
580         const char *pf;
581         const char *df;
582
583         if (argc != 2) {
584                 ejsSetErrorMsg(eid, "ldb.attach_dsdb_schema_from_ldif invalid arguments");
585                 return -1;
586         }
587
588         ldb = ejs_get_ldb_context(eid);
589         if (ldb == NULL) {
590                 return -1;
591         }
592
593         pf = argv[0];
594         df = argv[1];
595
596         status = dsdb_attach_schema_from_ldif_file(ldb, pf, df);
597
598         mpr_Return(eid, mprWERROR(status));
599         return 0;
600 }
601
602 /*
603   commit a ldb attach a dsdb_schema from ldif files
604   usage:
605    ok = ldb.set_ntds_invocationId("7729aa4b-f990-41ad-b81a-8b6a14090f41");
606 */
607 static int ejs_ldb_set_ntds_invocationId(MprVarHandle eid, int argc, char **argv)
608 {
609         struct ldb_context *ldb;
610         NTSTATUS status;
611         struct GUID guid;
612         char *guid_str;
613         bool ok;
614
615         if (argc != 1) {
616                 ejsSetErrorMsg(eid, "ldb.set_ntds_invocationId invalid arguments");
617                 return -1;
618         }
619
620         ldb = ejs_get_ldb_context(eid);
621         if (ldb == NULL) {
622                 return -1;
623         }
624
625         guid_str = argv[0];
626
627         status = GUID_from_string(guid_str, &guid);
628         if (!NT_STATUS_IS_OK(status)) {
629                 ejsSetErrorMsg(eid, "ldb.set_ntds_invocationId - failed to parse GUID '%s' %s\n",
630                                 guid_str, nt_errstr(status));
631                 return -1;
632         }
633
634         ok = samdb_set_ntds_invocation_id(ldb, &guid);
635         if (!ok) {
636                 ejsSetErrorMsg(eid, "ldb.set_ntds_invocationId - failed to set cached ntds invocationId\n");
637                 return -1;
638         }
639
640         mpr_Return(eid, mprCreateBoolVar(ok));
641         return 0;
642 }
643
644 /*
645   commit a ldb attach a dsdb_schema from ldif files
646   usage:
647    ok = ldb.get_ntds_objectGUID("7729aa4b-f990-41ad-b81a-8b6a14090f41");
648 */
649 static int ejs_ldb_set_ntds_objectGUID(MprVarHandle eid, int argc, char **argv)
650 {
651         struct ldb_context *ldb;
652         NTSTATUS status;
653         struct GUID guid;
654         char *guid_str;
655         bool ok;
656
657         if (argc != 1) {
658                 ejsSetErrorMsg(eid, "ldb.set_ntds_objectGUID invalid arguments");
659                 return -1;
660         }
661
662         ldb = ejs_get_ldb_context(eid);
663         if (ldb == NULL) {
664                 return -1;
665         }
666
667         guid_str = argv[0];
668
669         status = GUID_from_string(guid_str, &guid);
670         if (!NT_STATUS_IS_OK(status)) {
671                 ejsSetErrorMsg(eid, "ldb.set_ntds_objectGUID - failed to parse GUID '%s' %s\n",
672                                 guid_str, nt_errstr(status));
673                 return -1;
674         }
675
676         ok = samdb_set_ntds_invocation_id(ldb, &guid);
677         if (!ok) {
678                 ejsSetErrorMsg(eid, "ldb.set_ntds_objectGUID - failed to set cached ntds invocationId\n");
679                 return -1;
680         }
681
682         mpr_Return(eid, mprCreateBoolVar(ok));
683         return 0;
684 }
685
686 /*
687   initialise ldb ejs subsystem
688 */
689 static int ejs_ldb_init(MprVarHandle eid, int argc, struct MprVar **argv)
690 {
691         struct MprVar *ldb = mprInitObject(eid, "ldb", argc, argv);
692
693         mprSetStringCFunction(ldb, "connect", ejs_ldbConnect);
694         mprSetCFunction(ldb, "search", ejs_ldbSearch);
695         mprSetCFunction(ldb, "add", ejs_ldbAdd);
696         mprSetCFunction(ldb, "modify", ejs_ldbModify);
697         mprSetCFunction(ldb, "del", ejs_ldbDelete);
698         mprSetCFunction(ldb, "rename", ejs_ldbRename);
699         mprSetCFunction(ldb, "errstring", ejs_ldbErrstring);
700         mprSetCFunction(ldb, "encode", ejs_base64encode);
701         mprSetCFunction(ldb, "decode", ejs_base64decode);
702         mprSetCFunction(ldb, "dn_escape", ejs_dn_escape);
703         mprSetCFunction(ldb, "close", ejs_ldbClose);
704         mprSetCFunction(ldb, "transaction_start", ejs_ldbTransactionStart);
705         mprSetCFunction(ldb, "transaction_cancel", ejs_ldbTransactionCancel);
706         mprSetCFunction(ldb, "transaction_commit", ejs_ldbTransactionCommit);
707         mprSetStringCFunction(ldb, "attach_dsdb_schema_from_ldif",
708                               ejs_ldb_attach_dsdb_schema_from_ldif);
709         mprSetStringCFunction(ldb, "set_ntds_invocationId",
710                               ejs_ldb_set_ntds_invocationId);
711         mprSetStringCFunction(ldb, "set_ntds_objectGUID",
712                               ejs_ldb_set_ntds_objectGUID);
713         mprSetVar(ldb, "SCOPE_BASE", mprCreateNumberVar(LDB_SCOPE_BASE));
714         mprSetVar(ldb, "SCOPE_ONE", mprCreateNumberVar(LDB_SCOPE_ONELEVEL));
715         mprSetVar(ldb, "SCOPE_SUBTREE", mprCreateNumberVar(LDB_SCOPE_SUBTREE));
716         mprSetVar(ldb, "SCOPE_DEFAULT", mprCreateNumberVar(LDB_SCOPE_DEFAULT));
717
718         return 0;
719 }
720
721
722 /*
723   setup C functions that be called from ejs
724 */
725 NTSTATUS smb_setup_ejs_ldb(void)
726 {
727         ejsDefineCFunction(-1, "ldb_init", ejs_ldb_init, NULL, MPR_VAR_SCRIPT_HANDLE);
728         return NT_STATUS_OK;
729 }