1 /* LDAP client interface for SXEmacs.
2 Copyright (C) 1998 Free Software Foundation, Inc.
4 This file is part of SXEmacs
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Synched up with: Not in FSF. */
22 /* Author: Oscar Figueiredo with lots of support from Hrvoje Niksic */
24 /* This file provides lisp primitives for access to an LDAP library
25 conforming to the API defined in RFC 1823.
26 It has been tested with:
27 - UMich LDAP 3.3 (http://www.umich.edu/~dirsvcs/ldap/)
28 - OpenLDAP 1.2 (http://www.openldap.org/)
29 - Netscape's LDAP SDK (http://developer.netscape.com/) */
41 static Fixnum ldap_default_port;
42 static Lisp_Object Vldap_default_base;
44 /* Needed by the lrecord definition */
47 /* ldap-open plist keywords */
48 static Lisp_Object Qport, Qauth, Qbinddn, Qpasswd, Qderef, Qtimelimit,
50 /* Search scope limits */
51 static Lisp_Object Qbase, Qonelevel, Qsubtree;
52 /* Authentication methods */
53 static Lisp_Object Qkrbv41, Qkrbv42;
55 static Lisp_Object Qnever, Qalways, Qfind;
56 /* Modification types (Qdelete is defined in general.c) */
57 static Lisp_Object Qadd, Qreplace;
59 /************************************************************************/
60 /* Utility Functions */
61 /************************************************************************/
63 static void signal_ldap_error(LDAP * ld, LDAPMessage * res, int ldap_err)
66 #if defined HAVE_LDAP_PARSE_RESULT
68 ldap_err = ldap_parse_result(ld, res,
69 &err, NULL, NULL, NULL, NULL, 0);
70 if (ldap_err == LDAP_SUCCESS)
72 #elif defined HAVE_LDAP_GET_LDERRNO
73 ldap_err = ldap_get_lderrno(ld, NULL, NULL);
74 #elif defined HAVE_LDAP_RESULT2ERROR
75 ldap_err = ldap_result2error(ld, res, 0);
77 ldap_err = ld->ld_errno;
80 signal_simple_error("LDAP error",
81 build_string(ldap_err2string(ldap_err)));
84 /************************************************************************/
85 /* ldap lrecord basic functions */
86 /************************************************************************/
88 static Lisp_Object make_ldap(Lisp_LDAP * ldap)
90 Lisp_Object lisp_ldap;
91 XSETLDAP(lisp_ldap, ldap);
95 static Lisp_Object mark_ldap(Lisp_Object obj)
97 return XLDAP(obj)->host;
101 print_ldap(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
105 Lisp_LDAP *ldap = XLDAP(obj);
108 error("printing unreadable object #<ldap %s>",
109 XSTRING_DATA(ldap->host));
111 write_c_string("#<ldap ", printcharfun);
112 print_internal(ldap->host, printcharfun, 1);
114 write_c_string("(dead) ", printcharfun);
115 write_fmt_string(printcharfun, " 0x%lx>", (long)ldap);
118 static Lisp_LDAP *allocate_ldap(void)
120 Lisp_LDAP *ldap = alloc_lcrecord_type(Lisp_LDAP, &lrecord_ldap);
127 static void finalize_ldap(void *header, int for_disksave)
129 Lisp_LDAP *ldap = (Lisp_LDAP *) header;
133 ("Can't dump an emacs containing LDAP objects",
137 ldap_unbind(ldap->ld);
141 DEFINE_LRECORD_IMPLEMENTATION("ldap", ldap,
142 mark_ldap, print_ldap, finalize_ldap,
143 NULL, NULL, 0, Lisp_LDAP);
145 /************************************************************************/
146 /* Basic ldap accessors */
147 /************************************************************************/
149 DEFUN("ldapp", Fldapp, 1, 1, 0, /*
150 Return t if OBJECT is a LDAP connection.
154 return LDAPP(object) ? Qt : Qnil;
157 DEFUN("ldap-host", Fldap_host, 1, 1, 0, /*
158 Return the server host of the connection LDAP, as a string.
163 return (XLDAP(ldap))->host;
166 DEFUN("ldap-live-p", Fldap_status, 1, 1, 0, /*
167 Return t if LDAP is an active LDAP connection.
172 return (XLDAP(ldap))->ld ? Qt : Qnil;
175 /************************************************************************/
176 /* Opening/Closing a LDAP connection */
177 /************************************************************************/
179 DEFUN("ldap-open", Fldap_open, 1, 2, 0, /*
180 Open a LDAP connection to HOST.
181 PLIST is a plist containing additional parameters for the connection.
182 Valid keys in that list are:
183 `port' the TCP port to use for the connection if different from
185 `auth' is the authentication method to use, possible values depend on
186 the LDAP library SXEmacs was compiled with: `simple', `krbv41' and `krbv42'.
187 `binddn' is the distinguished name of the user to bind as (in RFC 1779 syntax).
188 `passwd' is the password to use for simple authentication.
189 `deref' is one of the symbols `never', `always', `search' or `find'.
190 `timelimit' is the timeout limit for the connection in seconds.
191 `sizelimit' is the maximum number of matches to return.
195 /* This function can GC */
199 int ldap_auth = LDAP_AUTH_SIMPLE;
200 char *ldap_binddn = NULL;
201 char *ldap_passwd = NULL;
202 int ldap_deref = LDAP_DEREF_NEVER;
203 int ldap_timelimit = 0;
204 int ldap_sizelimit = 0;
210 EXTERNAL_PROPERTY_LIST_LOOP_3(keyword, value, plist) {
212 if (EQ(keyword, Qport)) {
214 ldap_port = XINT(value);
216 /* Authentication method */
217 if (EQ(keyword, Qauth)) {
218 if (EQ(value, Qsimple))
219 ldap_auth = LDAP_AUTH_SIMPLE;
220 #ifdef LDAP_AUTH_KRBV41
221 else if (EQ(value, Qkrbv41))
222 ldap_auth = LDAP_AUTH_KRBV41;
224 #ifdef LDAP_AUTH_KRBV42
225 else if (EQ(value, Qkrbv42))
226 ldap_auth = LDAP_AUTH_KRBV42;
230 ("Invalid authentication method",
234 else if (EQ(keyword, Qbinddn)) {
236 LISP_STRING_TO_EXTERNAL(value, ldap_binddn,
240 else if (EQ(keyword, Qpasswd)) {
242 LISP_STRING_TO_EXTERNAL(value, ldap_passwd,
246 else if (EQ(keyword, Qderef)) {
247 if (EQ(value, Qnever))
248 ldap_deref = LDAP_DEREF_NEVER;
249 else if (EQ(value, Qsearch))
250 ldap_deref = LDAP_DEREF_SEARCHING;
251 else if (EQ(value, Qfind))
252 ldap_deref = LDAP_DEREF_FINDING;
253 else if (EQ(value, Qalways))
254 ldap_deref = LDAP_DEREF_ALWAYS;
257 ("Invalid deref value", value);
260 else if (EQ(keyword, Qtimelimit)) {
262 ldap_timelimit = XINT(value);
265 else if (EQ(keyword, Qsizelimit)) {
267 ldap_sizelimit = XINT(value);
272 if (ldap_port == 0) {
273 ldap_port = ldap_default_port;
276 /* Connect to the server and bind */
277 slow_down_interrupts();
278 ld = ldap_open((char *)XSTRING_DATA(host), ldap_port);
279 speed_up_interrupts();
282 signal_simple_error_2("Failed connecting to host",
283 host, lisp_strerror(errno));
285 #ifdef HAVE_LDAP_SET_OPTION
286 if ((err = ldap_set_option(ld, LDAP_OPT_DEREF,
287 (void *)&ldap_deref)) != LDAP_SUCCESS)
288 signal_ldap_error(ld, NULL, err);
289 if ((err = ldap_set_option(ld, LDAP_OPT_TIMELIMIT,
290 (void *)&ldap_timelimit)) != LDAP_SUCCESS)
291 signal_ldap_error(ld, NULL, err);
292 if ((err = ldap_set_option(ld, LDAP_OPT_SIZELIMIT,
293 (void *)&ldap_sizelimit)) != LDAP_SUCCESS)
294 signal_ldap_error(ld, NULL, err);
295 if ((err = ldap_set_option(ld, LDAP_OPT_REFERRALS,
296 LDAP_OPT_ON)) != LDAP_SUCCESS)
297 signal_ldap_error(ld, NULL, err);
298 if ((err = ldap_set_option(ld, LDAP_OPT_RESTART,
299 LDAP_OPT_ON)) != LDAP_SUCCESS)
300 signal_ldap_error(ld, NULL, err);
301 #else /* not HAVE_LDAP_SET_OPTION */
302 ld->ld_deref = ldap_deref;
303 ld->ld_timelimit = ldap_timelimit;
304 ld->ld_sizelimit = ldap_sizelimit;
305 #ifdef LDAP_REFERRALS
306 ld->ld_options = LDAP_OPT_REFERRALS;
307 #else /* not LDAP_REFERRALS */
309 #endif /* not LDAP_REFERRALS */
310 /* SXEmacs uses interrupts (SIGIO,SIGALRM), LDAP calls need to ignore them */
311 ld->ld_options |= LDAP_OPT_RESTART;
312 #endif /* not HAVE_LDAP_SET_OPTION */
314 err = ldap_bind_s(ld, ldap_binddn, ldap_passwd, ldap_auth);
315 if (err != LDAP_SUCCESS)
316 signal_simple_error("Failed binding to the server",
317 build_string(ldap_err2string(err)));
319 ldap = allocate_ldap();
323 return make_ldap(ldap);
326 DEFUN("ldap-close", Fldap_close, 1, 1, 0, /*
327 Close an LDAP connection.
332 CHECK_LIVE_LDAP(ldap);
334 ldap_unbind(lldap->ld);
339 /************************************************************************/
340 /* Working on a LDAP connection */
341 /************************************************************************/
342 struct ldap_unwind_struct {
344 struct berval **vals;
347 static Lisp_Object ldap_search_unwind(Lisp_Object unwind_obj)
349 struct ldap_unwind_struct *unwind =
350 (struct ldap_unwind_struct *)get_opaque_ptr(unwind_obj);
352 ldap_msgfree(unwind->res);
354 ldap_value_free_len(unwind->vals);
358 /* The following function is called `ldap-search-basic' instead of */
359 /* plain `ldap-search' to maintain compatibility with the XEmacs 21.1 */
360 /* API where `ldap-search' was the name of the high-level search */
363 DEFUN("ldap-search-basic", Fldap_search_basic, 2, 8, 0, /*
364 Perform a search on an open LDAP connection.
365 LDAP is an LDAP connection object created with `ldap-open'.
366 FILTER is a filter string for the search as described in RFC 1558.
367 BASE is the distinguished name at which to start the search.
368 SCOPE is one of the symbols `base', `onelevel' or `subtree' indicating
369 the scope of the search.
370 ATTRS is a list of strings indicating which attributes to retrieve
371 for each matching entry. If nil return all available attributes.
372 If ATTRSONLY is non-nil then only the attributes are retrieved, not
373 the associated values.
374 If WITHDN is non-nil each entry in the result will be prepended with
375 its distinguished name DN.
376 If VERBOSE is non-nil progress messages will be echoed.
377 The function returns a list of matching entries. Each entry is itself
378 an alist of attribute/value pairs optionally preceded by the DN of the
379 entry according to the value of WITHDN.
381 (ldap, filter, base, scope, attrs, attrsonly, withdn, verbose))
383 /* This function can GC */
392 struct ldap_unwind_struct unwind;
394 int ldap_scope = LDAP_SCOPE_SUBTREE;
395 char **ldap_attributes = NULL;
397 int speccount = specpdl_depth();
399 Lisp_Object list = Qnil;
400 Lisp_Object entry = Qnil;
401 Lisp_Object result = Qnil;
402 struct gcpro gcpro1, gcpro2, gcpro3;
404 GCPRO3(list, entry, result);
409 /* Do all the parameter checking */
410 CHECK_LIVE_LDAP(ldap);
411 ld = XLDAP(ldap)->ld;
414 CHECK_STRING(filter);
418 base = Vldap_default_base;
426 if (EQ(scope, Qbase))
427 ldap_scope = LDAP_SCOPE_BASE;
428 else if (EQ(scope, Qonelevel))
429 ldap_scope = LDAP_SCOPE_ONELEVEL;
430 else if (EQ(scope, Qsubtree))
431 ldap_scope = LDAP_SCOPE_SUBTREE;
433 signal_simple_error("Invalid scope", scope);
436 /* Attributes to search */
440 alloca_array(char *, 1 + XINT(Flength(attrs)));
443 EXTERNAL_LIST_LOOP(attrs, attrs) {
444 Lisp_Object current = XCAR(attrs);
445 CHECK_STRING(current);
446 LISP_STRING_TO_EXTERNAL(current, ldap_attributes[i],
450 ldap_attributes[i] = NULL;
453 /* Attributes only ? */
454 CHECK_SYMBOL(attrsonly);
456 /* Perform the search */
458 NILP(base) ? (char *)"" : (char *)XSTRING_DATA(base),
460 NILP(filter) ? (char *)"" : (char *)
461 XSTRING_DATA(filter), ldap_attributes,
462 NILP(attrsonly) ? 0 : 1)
464 signal_ldap_error(ld, NULL, 0);
467 /* Ensure we don't exit without cleaning up */
468 record_unwind_protect(ldap_search_unwind, make_opaque_ptr(&unwind));
470 /* Build the results list */
473 rc = ldap_result(ld, LDAP_RES_ANY, 0, NULL, &unwind.res);
475 while (rc == LDAP_RES_SEARCH_ENTRY) {
478 e = ldap_first_entry(ld, unwind.res);
479 /* #### This call to message() is pretty fascist, because it
480 destroys the current echo area contents, even when invoked
481 from Lisp. It should use echo_area_message() instead, and
482 restore the old echo area contents later. */
484 message("Parsing ldap results... %d", matches);
486 /* Get the DN if required */
488 dn = ldap_get_dn(ld, e);
490 signal_ldap_error(ld, e, 0);
491 entry = Fcons(build_ext_string(dn, Qnative), Qnil);
493 for (a = ldap_first_attribute(ld, e, &ptr);
494 a != NULL; a = ldap_next_attribute(ld, e, ptr)) {
495 list = Fcons(build_ext_string(a, Qnative), Qnil);
496 unwind.vals = ldap_get_values_len(ld, e, a);
497 if (unwind.vals != NULL) {
498 for (i = 0; unwind.vals[i] != NULL; i++) {
500 Fcons(make_ext_string
501 ((Extbyte *) unwind.vals[i]->
503 unwind.vals[i]->bv_len,
507 entry = Fcons(Fnreverse(list), entry);
508 ldap_value_free_len(unwind.vals);
511 result = Fcons(Fnreverse(entry), result);
512 ldap_msgfree(unwind.res);
515 rc = ldap_result(ld, LDAP_RES_ANY, 0, NULL, &(unwind.res));
518 #if defined HAVE_LDAP_PARSE_RESULT
520 int rc2 = ldap_parse_result(ld, unwind.res,
522 NULL, NULL, NULL, NULL, 0);
523 if (rc2 != LDAP_SUCCESS)
528 signal_ldap_error(ld, NULL, LDAP_TIMELIMIT_EXCEEDED);
531 signal_ldap_error(ld, unwind.res,
532 (unwind.res == NULL) ? ld->ld_errno : 0);
534 #if defined HAVE_LDAP_RESULT2ERROR
535 rc = ldap_result2error(ld, unwind.res, 0);
539 if (rc != LDAP_SUCCESS)
540 signal_ldap_error(ld, NULL, rc);
542 ldap_msgfree(unwind.res);
543 unwind.res = (LDAPMessage *) NULL;
545 /* #### See above for calling message(). */
547 message("Parsing ldap results... done");
549 unbind_to(speccount, Qnil);
551 return Fnreverse(result);
554 DEFUN("ldap-add", Fldap_add, 3, 3, 0, /*
555 Add an entry to an LDAP directory.
556 LDAP is an LDAP connection object created with `ldap-open'.
557 DN is the distinguished name of the entry to add.
558 ENTRY is an entry specification, i.e., a list of cons cells
559 containing attribute/value string pairs.
564 LDAPMod *ldap_mods, **ldap_mods_ptrs;
565 struct berval *bervals;
570 Lisp_Object current = Qnil;
571 Lisp_Object values = Qnil;
572 struct gcpro gcpro1, gcpro2;
574 GCPRO2(current, values);
576 /* Do all the parameter checking */
577 CHECK_LIVE_LDAP(ldap);
578 ld = XLDAP(ldap)->ld;
583 /* Check the entry */
586 signal_simple_error("Cannot add void entry", entry);
588 /* Build the ldap_mods array */
589 len = XINT(Flength(entry));
590 ldap_mods = alloca_array(LDAPMod, len);
591 ldap_mods_ptrs = alloca_array(LDAPMod *, 1 + len);
593 EXTERNAL_LIST_LOOP(entry, entry) {
594 current = XCAR(entry);
596 CHECK_STRING(XCAR(current));
597 ldap_mods_ptrs[i] = &(ldap_mods[i]);
598 LISP_STRING_TO_EXTERNAL(XCAR(current), ldap_mods[i].mod_type,
600 ldap_mods[i].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
601 values = XCDR(current);
603 len = XINT(Flength(values));
604 bervals = alloca_array(struct berval, len);
605 ldap_mods[i].mod_vals.modv_bvals =
606 alloca_array(struct berval *, 1 + len);
608 EXTERNAL_LIST_LOOP(values, values) {
609 current = XCAR(values);
610 CHECK_STRING(current);
611 ldap_mods[i].mod_vals.modv_bvals[j] =
613 TO_EXTERNAL_FORMAT(LISP_STRING, current, ALLOCA,
619 ldap_mods[i].mod_vals.modv_bvals[j] = NULL;
621 CHECK_STRING(values);
622 bervals = alloca_array(struct berval, 1);
623 ldap_mods[i].mod_vals.modv_bvals =
624 alloca_array(struct berval *, 2);
625 ldap_mods[i].mod_vals.modv_bvals[0] = &(bervals[0]);
626 TO_EXTERNAL_FORMAT(LISP_STRING, values,
627 ALLOCA, (bervals[0].bv_val,
630 ldap_mods[i].mod_vals.modv_bvals[1] = NULL;
634 ldap_mods_ptrs[i] = NULL;
635 rc = ldap_add_s(ld, (char *)XSTRING_DATA(dn), ldap_mods_ptrs);
636 if (rc != LDAP_SUCCESS)
637 signal_ldap_error(ld, NULL, rc);
643 DEFUN("ldap-modify", Fldap_modify, 3, 3, 0, /*
644 Add an entry to an LDAP directory.
645 LDAP is an LDAP connection object created with `ldap-open'.
646 DN is the distinguished name of the entry to modify.
647 MODS is a list of modifications to apply.
648 A modification is a list of the form (MOD-OP ATTR VALUE1 VALUE2 ...)
649 MOD-OP and ATTR are mandatory, VALUEs are optional depending on MOD-OP.
650 MOD-OP is the type of modification, one of the symbols `add', `delete'
651 or `replace'. ATTR is the LDAP attribute type to modify.
656 LDAPMod *ldap_mods, **ldap_mods_ptrs;
657 struct berval *bervals;
662 Lisp_Object current = Qnil;
663 Lisp_Object values = Qnil;
664 struct gcpro gcpro1, gcpro2;
666 /* Do all the parameter checking */
667 CHECK_LIVE_LDAP(ldap);
668 ld = XLDAP(ldap)->ld;
673 /* Check the entry */
678 /* Build the ldap_mods array */
679 len = XINT(Flength(mods));
680 ldap_mods = alloca_array(LDAPMod, len);
681 ldap_mods_ptrs = alloca_array(LDAPMod *, 1 + len);
684 GCPRO2(current, values);
685 EXTERNAL_LIST_LOOP(mods, mods) {
686 current = XCAR(mods);
688 CHECK_SYMBOL(XCAR(current));
689 mod_op = XCAR(current);
690 ldap_mods_ptrs[i] = &(ldap_mods[i]);
691 ldap_mods[i].mod_op = LDAP_MOD_BVALUES;
692 if (EQ(mod_op, Qadd))
693 ldap_mods[i].mod_op |= LDAP_MOD_ADD;
694 else if (EQ(mod_op, Qdelete))
695 ldap_mods[i].mod_op |= LDAP_MOD_DELETE;
696 else if (EQ(mod_op, Qreplace))
697 ldap_mods[i].mod_op |= LDAP_MOD_REPLACE;
699 signal_simple_error("Invalid LDAP modification type",
701 current = XCDR(current);
702 CHECK_STRING(XCAR(current));
703 LISP_STRING_TO_EXTERNAL(XCAR(current), ldap_mods[i].mod_type,
705 values = XCDR(current);
706 len = XINT(Flength(values));
707 bervals = alloca_array(struct berval, len);
708 ldap_mods[i].mod_vals.modv_bvals =
709 alloca_array(struct berval *, 1 + len);
711 EXTERNAL_LIST_LOOP(values, values) {
712 current = XCAR(values);
713 CHECK_STRING(current);
714 ldap_mods[i].mod_vals.modv_bvals[j] = &(bervals[j]);
715 TO_EXTERNAL_FORMAT(LISP_STRING, current,
716 ALLOCA, (bervals[j].bv_val,
721 ldap_mods[i].mod_vals.modv_bvals[j] = NULL;
724 ldap_mods_ptrs[i] = NULL;
725 rc = ldap_modify_s(ld, (char *)XSTRING_DATA(dn), ldap_mods_ptrs);
726 if (rc != LDAP_SUCCESS)
727 signal_ldap_error(ld, NULL, rc);
733 DEFUN("ldap-delete", Fldap_delete, 2, 2, 0, /*
734 Delete an entry to an LDAP directory.
735 LDAP is an LDAP connection object created with `ldap-open'.
736 DN is the distinguished name of the entry to delete.
743 /* Check parameters */
744 CHECK_LIVE_LDAP(ldap);
745 ld = XLDAP(ldap)->ld;
748 rc = ldap_delete_s(ld, (char *)XSTRING_DATA(dn));
749 if (rc != LDAP_SUCCESS)
750 signal_ldap_error(ld, NULL, rc);
755 void syms_of_eldap(void)
757 INIT_LRECORD_IMPLEMENTATION(ldap);
759 defsymbol(&Qldapp, "ldapp");
760 defsymbol(&Qport, "port");
761 defsymbol(&Qauth, "auth");
762 defsymbol(&Qbinddn, "binddn");
763 defsymbol(&Qpasswd, "passwd");
764 defsymbol(&Qderef, "deref");
765 defsymbol(&Qtimelimit, "timelimit");
766 defsymbol(&Qsizelimit, "sizelimit");
767 defsymbol(&Qbase, "base");
768 defsymbol(&Qonelevel, "onelevel");
769 defsymbol(&Qsubtree, "subtree");
770 defsymbol(&Qkrbv41, "krbv41");
771 defsymbol(&Qkrbv42, "krbv42");
772 defsymbol(&Qnever, "never");
773 defsymbol(&Qalways, "always");
774 defsymbol(&Qfind, "find");
775 defsymbol(&Qadd, "add");
776 defsymbol(&Qreplace, "replace");
780 DEFSUBR(Fldap_status);
782 DEFSUBR(Fldap_close);
783 DEFSUBR(Fldap_search_basic);
785 DEFSUBR(Fldap_modify);
786 DEFSUBR(Fldap_delete);
789 void vars_of_eldap(void)
792 ldap_default_port = LDAP_PORT;
793 Vldap_default_base = Qnil;
795 DEFVAR_INT("ldap-default-port", &ldap_default_port /*
796 Default TCP port for LDAP connections.
797 Initialized from the LDAP library. Default value is 389.
800 DEFVAR_LISP("ldap-default-base", &Vldap_default_base /*
801 Default base for LDAP searches.
802 This is a string using the syntax of RFC 1779.
803 For instance, "o=ACME, c=US" limits the search to the
804 Acme organization in the United States.