13. Appendix B: Writing a SHELL backend

This section provides information for system administrators wanting to use the SHELL backend to slapd. It explains the input format, output format, and calling conventions a SHELL backend program must follow to communicate with slapd.

13.1 Overview

When slapd receives an operation to a SHELL backend, the backend consults the information given in the slapd configuration file to determine what program to invoke to handle the operation. For example, if the SHELL database definition in the configuration file contained a line like this
search /usr/local/bin/search.sh
The indicated command would be invoked in response to a search request.

Slapd feeds a text representation of the request to the command on the command's standard input. Slapd then reads a text representation of the results or errors produced by the command from the command's standard output. These text results are converted to LDAP format and returned to the client. Slapd pays attention to the exit status of the command in some situations (i.e., to determine if a BIND request has succeeded or not).

The next sections discuss these input, output, and exit conventions in more detail.

13.2 Input Format

The input to your SHELL backend program is a simple text-based, newline separated sequence of <option>: <value> pairs conveying the type and information in a request. The exact format for each request is given below. All requests start with a key word indicating the type of request, a msgid: line indicating the unique message ID of the operation, and one or more suffix: lines indicating the database suffix(es) the backend is configured for.

13.2.1 Bind

The input format for a BIND request is as follows.
BIND
msgid: <integer>[suffix: <dn>]+
dn: <binddn>
method: <integer>
credlen: <integer>
cred: <credentials>
The msgid parameter is a unique identifier for the operation. The method parameter will be one of the LDAP authentication methods listed in <ldap.h>. The only credential currently supported is a clear-text password (used with a simple bind).

13.2.2 Unbind

The input format for an UNBIND request is as follows.
UBBIND
msgid: <integer>
[suffix: <dn>]+
dn: <binddn>
This routine is provided so the backend can do any clean-up necessary.

13.2.3 Search

The input format for a SEARCH request is as follows.
SEARCH
msgid: <integer>
[suffix: <dn>]+
base: <baseobjectdn>
scope: 0 | 1 | 2
deref: 0 | 1 | 2 | 3
sizelimit: <integer>
timelimit: <integer>
filter: <ldapfilter>
attrsonly: 0 | 1
attrs: all | <attrlist>
The values of the scope parameter correspond to the various LDAP scopes listed in <ldap.h>.

The values of the deref parameter correspond to the various LDAP dereference options listed in <ldap.h>.

The filter parameter is a string representation of the LDAP search filter, as described in RFC 1588.

The <attrlist> is a space-separated list of attributes to retrieve.

13.2.4 Compare

The input format for a COMPARE request is as follows.
COMPARE
msgid: <integer>
[suffix: <dn>]+
dn: <entrydn>
<attrtype>: <attrvalue>
The AVA (attribute value assertion) to compare to the entry is given in the <attrtype>: <attrvalue> line.

13.2.5 Modify

The input format for a MODIFY request is as follows.
MODIFY
msgid: <integer>
[suffix: <dn>]+
dn: <entrydn>
[add: <attrtype>
[<attrtype>: <attrvalue]+]*
[delete: <attrtype>
[<attrtype>: <attrvalue]*]*
[replace: <attrtype>
[<attrtype>: <attrvalue]+]*
The add, delete and replace constructs indicate the modifications to make.

13.2.6 Modify RDN

The input format for a MODIFY RDN request is as follows.
MODRDN
msgid: <integer>
[suffix: <dn>]+
dn: <entrydn>
newrdn: <rdn>
deleteoldrdn: 0 | 1
The deleteoldrdn parameter is a Boolean parameter (where 0 means false and 1 means true).

13.2.7 Add

The input format for an ADD request is as follows.
ADD
msgid: <integer>
[suffix: <dn>]+
<entry>
The <entry> parameter is a text representation of the entry to add in LDIF format, as described in Section 6.3.

13.2.8 Delete

The input format for a DELETE request is as follows.
ADD
msgid: <integer>
[suffix: <dn>]+
dn: <entrydn>

13.2.9 Abandon

The input format for an abandon operation is as follows.
ABANDON
msgid: <integer>
[suffix: <dn>]+
For the abandon operation, the msgid parameter gives the message ID of the operation to abandon.

13.3 Output Format

There are two possible results a SHELL backend command can produce: search entries, and results. The format of each is described below.

13.3.1 Search Entry

The format of a search entry is the LDIF format described in Section 6.3. Multiple entries can be given by separating the entries by blank lines. The ldif program described in Section 8.2.6 can be very helpful in producing the LDIF format required by the SHELL backend.

13.3.2 Result

The format of a result is as follows.
RESULT
code: <integer>
matched: <partialdn>
info: <string>
All of the parameters are optional and will be given default values if omitted. If search entries have been returned, the RESULT follows the last one, with a blank line preceding the RESULT.

13.3.3 Debugging

A SHELL backend command may produce debugging statements which may be logged but otherwise ignored by slapd. Any output line beginning with the characters "DEBUG:" will be treated as a debugging statement by slapd.

This feature can be useful when trying to debug a problem with your SHELL backend. If you turn on SHELL debugging in slapd (level 1024), it will log anything it reads from a SHELL backend, allowing you to see your backend's debugging statements easily.

13.4 Exit Status

SHELL backend commands should be mindful of their exit status. This status is examined by the invoking slapd to determine whether the command succeeded or not. This can be important for a number of reasons.
1. For modify operations, the exit status determines whether the modification should be logged and sent out to replicas or not.

2. For bind operations, the exit status determines whether the bind was successful, and therefore whether the DN given should be trusted on future access control decisions.

An exit status of zero indicates the command was successful. A non-zero exit status indicates the command was not successful.

Note that on a bind operation, a zero exit status indicates that the DN given in the bind should be trusted on future access control decisions. This means that if, for example, a NOAUTH bind (no password provided) succeeds, you should be sure not to return an exit status of zero.

13.5 Example

The following example illustrates a simple use of the SHELL backend to provide LDAP access to the /etc/passwd file on a machine.

13.5.1 Configuration file

Our example makes use of the following simple configuration file.
referral ldap://ldap.itd.umich.edu
database shell
suffix "o=university of michigan, c=us"
search /usr/local/bin/searchexample.sh
This configuration defines a single SHELL backend, for entries in the "o=University of Michigan, c=US" subtree. Requests involving any other subtree will be sent to the LDAP server running on the host ldap.itd.umich.edu. A search operation will cause the command /usr/local/bin/searchexample.sh to be executed. Any other operation will result in an "unwilling to perform" error being returned to the client.

13.5.2 Search command shell script

The search command in our example is implemented by the following bourne shell script. It assumes a very simple filter of the form (uid=login) where login is a user's UNIX login. It extracts the login from the filter, does a simple grep for it in the /etc/passwd file, and parses the resulting line (if any) using awk to pull out the gecos field.

Note that our simple example does no error checking, handles only very simple filters, ignores the scope, sizelimit, timelimit and other parameters, and is meant for illustrative purposes only. A real example should do more error checking and handle more situations.

1. #!/bin/sh
2. while [ 1 ]; do
3. read TAG VALUE
4. if [ $? -ne 0 ]; then
5. break
6. fi
7. case "$TAG" in
8. base:)
9. BASE=$VALUE
10. ;;
11. filter:)
12. FILTER=$VALUE
13. ;;
14. esac
15. done
16. LOGIN=`echo $FILTER | sed -e 's/.*=\(.*\))/\1/'`
17. PWLINE=`grep -i "^$LOGIN" /etc/passwd`
18. if [ $? = 0 ]; then
19. echo "DEBUG: passwd line is $PWLINE"
20. echo $PWLINE | awk -F: '{
21. printf("dn: cn=%s,%s\n", $1, base);
22. printf("cn: %s\n", $1);
23. printf("cn: %s\n", $5);
24. printf("sn: %s\n", $1);
25. printf("uid: %s\n", $1);
26. }' base="$BASE"
27. echo ""
28. fi
29. echo "RESULT"
30. echo "code: 0"
31. exit 0
The line numbers are for illustrative purposes only and do not appear in the actual file.

Note the debugging statement on line 19. The output from this statement is ignored by slapd because of the DEBUG: prefix, unless debugging is turned on, in which case it may be logged (depending on the debugging level) but will otherwise not affect the search results sent.


[View Next Section] [View Previous Section] [Return to Table of Contents]

Send comments about this page to: ldap-support@umich.edu