ldaplist.c revision 7777:dc7662044c94
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <libintl.h>
30#include <strings.h>
31#include <locale.h>
32#include <syslog.h>
33
34#include "standalone.h"
35
36extern char *set_filter(char **, char *, char **);
37extern char *set_filter_publickey(char **, char *, int, char **);
38extern void _printResult(ns_ldap_result_t *);
39extern void printMapping();
40
41int listflag = 0;
42
43void
44usage(char *msg) {
45	if (msg)
46		(void) fprintf(stderr, "%s\n", msg);
47
48	(void) fprintf(stderr,
49	gettext(
50	"\n"
51	"usage: ldaplist [-dlv] [-h LDAP_server[:serverPort] [-M domainName]\n"
52	"[-N  profileName] [-a  authenticationMethod] [-P certifPath]\n"
53	"[-D  bindDN] [-w bindPassword] [-j passwdFile]]\n"
54	"[<database> [<key>] ...]\n\n"
55	"usage: ldaplist -h\n"
56	"\n"
57	"usage: ldaplist -g\n\n"
58	"\tOptions:\n"
59	"\t    -l list all the attributes found in entry.\n"
60	"\t       By default, it lists only the DNs.\n"
61	"\t    -d list attributes for the database instead of its entries\n"
62	"\t    -v print out the LDAP search filter.\n"
63	"\t    -g list the database mappings.\n"
64	"\t    -h An address (or a name) and a port of the LDAP server in\n"
65	"\t       which the entries will be stored. The default value for\n"
66	"\t       the port is 389 (or 636 for TLS connections).\n"
67	"\t    -M The name of a domain served by the specified server.\n"
68	"\t       If not specified, the default domain name will be used.\n"
69	"\t    -N Specifies a DUAProfile name.\n"
70	"\t       The default value is \"default\".\n"
71	"\t    -a Specifies an authentication method.\n"
72	"\t    -P The certificate path for the location of the certificate\n"
73	"\t       database.\n"
74	"\t    -D Specifies an entry which has read permission to\n"
75	"\t       the requested database.\n"
76	"\t    -w Password to be used for authenticating the bindDN.\n"
77	"\t    -j File containing the password for bindDN or SSL key db.\n"
78	"\t<database> is the database to be searched in.  Standard system\n"
79	"\tdatabases are:\n"
80	"\t\tpassword, printers, group, hosts, ethers, networks, netmasks,\n"
81	"\t\trpc, bootparams, protocols, services, netgroup, auto_*.\n"
82	"\tNon-standard system databases can be specified as follows:\n"
83	"\t\tby specific container: ou=<dbname> or\n"
84	"\t\tby default container: <dbname>.  In this case, 'nismapname'\n"
85	"\t\twill be used, thus mapping this to nismapname=<dbname>.\n"
86	"\t<key> is the key to search in the database.  For the standard\n"
87	"\tdatabases, the search type for the key is predefined.  You can\n"
88	"\toverride this by specifying <type>=<key>.\n"
89	"\nNOTE: The old -h option printing the mapping information is "
90	"deprecated.\nFor backward compatibility the following mode is "
91	"available:\nldaplist -h\n"));
92	exit(1);
93}
94
95/*
96 * This is a generic filter call back function for
97 * merging the filter from service search descriptor with
98 * an existing search filter. This routine expects userdata
99 * contain a format string with a single %s in it, and will
100 * use the format string with sprintf() to insert the SSD filter.
101 *
102 * This routine is passed to the __ns_ldap_list() or
103 * __ns_ldap_firstEntry() APIs as the filter call back
104 * together with the userdata. For example,
105 * the "ldaplist hosts sys1" processing may call __ns_ldap_list()
106 * with "(&(objectClass=ipHost)(cn=sys1))" as filter, this function
107 * as the filter call back, and "(&(%s)(cn=sys1))" as the
108 * userdata, this routine will in turn gets call to produce
109 * "(&(department=sds)(cn=sys1))" as the real search
110 * filter, if the input SSD contains a filter "department=sds".
111 */
112static int
113merge_SSD_filter(const ns_ldap_search_desc_t *desc,
114			char **realfilter,
115			const void *userdata)
116{
117	int	len;
118
119	/* sanity check */
120	if (realfilter == NULL)
121		return (NS_LDAP_INVALID_PARAM);
122	*realfilter = NULL;
123
124	if (desc == NULL || desc->filter == NULL ||
125	    userdata == NULL)
126		return (NS_LDAP_INVALID_PARAM);
127
128	len = strlen(userdata) + strlen(desc->filter) + 1;
129
130	*realfilter = (char *)malloc(len);
131	if (*realfilter == NULL)
132		return (NS_LDAP_MEMORY);
133
134	(void) sprintf(*realfilter, (char *)userdata,
135	    desc->filter);
136
137	return (NS_LDAP_SUCCESS);
138}
139
140/* returns 0=success, 1=error */
141int
142list(char *database, char *ldapfilter, char **ldapattribute,
143char **err, char *userdata)
144{
145	ns_ldap_result_t	*result;
146	ns_ldap_error_t	*errorp;
147	int		rc;
148	char		buf[500];
149
150	*err = NULL;
151	buf[0] = '\0';
152	rc = __ns_ldap_list(database, (const char *)ldapfilter,
153	    merge_SSD_filter, (const char **)ldapattribute, NULL,
154	    listflag, &result, &errorp, NULL, userdata);
155	if (rc != NS_LDAP_SUCCESS) {
156		char *p;
157		(void) __ns_ldap_err2str(rc, &p);
158		if (errorp && errorp->message) {
159			(void) snprintf(buf, sizeof (buf), "%s (%s)",
160			    p, errorp->message);
161			(void) __ns_ldap_freeError(&errorp);
162		} else
163			(void) snprintf(buf, sizeof (buf), "%s\n", p);
164		*err = strdup(buf);
165		return (rc);
166	}
167
168	_printResult(result);
169	(void) __ns_ldap_freeResult(&result);
170	return (0);
171}
172
173
174int
175switch_err(int rc)
176{
177	switch (rc) {
178	case NS_LDAP_SUCCESS:
179		return (0);
180	case NS_LDAP_NOTFOUND:
181		return (1);
182	}
183	return (2);
184}
185
186int
187main(int argc, char **argv)
188{
189
190	extern int		optind;
191	char			*database = NULL;
192	char			*ldapfilter = NULL;
193	char			*attribute = "dn";
194	char			**key = NULL;
195	char			**ldapattribute = NULL;
196	char 			*buffer[100];
197	char			*err = NULL;
198	char			*p;
199	int			index = 1;
200	int			c;
201	int			rc;
202	int			verbose = 0;
203	char			*udata = NULL;
204
205	ns_standalone_conf_t	standalone_cfg = standaloneDefaults;
206	ns_ldap_error_t		*errorp = NULL;
207	char			*authmech = NULL;
208	ns_auth_t		auth = {NS_LDAP_AUTH_NONE,
209					NS_LDAP_TLS_NONE,
210					NS_LDAP_SASL_NONE,
211					NS_LDAP_SASLOPT_NONE};
212
213	(void) setlocale(LC_ALL, "");
214	(void) textdomain(TEXT_DOMAIN);
215
216	openlog("ldaplist", LOG_PID, LOG_USER);
217
218	if (argc == 2 &&
219	    strlen(argv[1]) == 2 && strncmp(argv[1], "-h", 2) == 0) {
220		/* preserve backwards compatability, support old -h option */
221		(void) printMapping();
222		exit(0);
223	}
224
225	while ((c = getopt(argc, argv, "h:M:N:P:r:a:D:w:j:dgvl")) != EOF) {
226		switch (c) {
227		case 'd':
228			listflag |= NS_LDAP_SCOPE_BASE;
229			break;
230		case 'g':
231			(void) printMapping();
232			exit(0);
233			break; /* Never reached */
234		case 'l':
235			attribute = "NULL";
236			break;
237		case 'v':
238			verbose = 1;
239			break;
240		case 'M':
241			standalone_cfg.type = NS_LDAP_SERVER;
242			standalone_cfg.SA_DOMAIN = optarg;
243			break;
244		case 'h':
245			standalone_cfg.type = NS_LDAP_SERVER;
246			if (separatePort(optarg,
247			    &standalone_cfg.SA_SERVER,
248			    &standalone_cfg.SA_PORT) > 0) {
249				exit(1);
250			}
251			break;
252		case 'P':
253			standalone_cfg.type = NS_LDAP_SERVER;
254			standalone_cfg.SA_CERT_PATH = optarg;
255			break;
256		case 'N':
257			standalone_cfg.type = NS_LDAP_SERVER;
258			standalone_cfg.SA_PROFILE_NAME = optarg;
259			break;
260		case 'D':
261			standalone_cfg.type = NS_LDAP_SERVER;
262			standalone_cfg.SA_BIND_DN = strdup(optarg);
263			break;
264		case 'w':
265			if (standalone_cfg.SA_BIND_PWD != NULL) {
266				(void) fprintf(stderr,
267				    gettext("The -w option is mutually "
268				    "exclusive of -j. -w is ignored.\n"));
269				break;
270			}
271
272			if (optarg != NULL &&
273			    optarg[0] == '-' && optarg[1] == '\0') {
274				/* Ask for a password later */
275				break;
276			}
277
278			standalone_cfg.type = NS_LDAP_SERVER;
279			standalone_cfg.SA_BIND_PWD = strdup(optarg);
280			break;
281		case 'j':
282			if (standalone_cfg.SA_BIND_PWD != NULL) {
283				(void) fprintf(stderr,
284				    gettext("The -w option is mutually "
285				    "exclusive of -j. -w is ignored.\n"));
286				free(standalone_cfg.SA_BIND_PWD);
287			}
288			standalone_cfg.type = NS_LDAP_SERVER;
289			standalone_cfg.SA_BIND_PWD = readPwd(optarg);
290			if (standalone_cfg.SA_BIND_PWD == NULL) {
291				exit(1);
292			}
293			break;
294		case 'a':
295			authmech = optarg;
296			break;
297		default:
298			usage(gettext("Invalid option"));
299		}
300	}
301
302	if (standalone_cfg.type == NS_LDAP_SERVER &&
303	    standalone_cfg.SA_SERVER == NULL) {
304		(void) fprintf(stderr,
305		    gettext("Please specify an LDAP server you want "
306		    "to connect to. \n"));
307		exit(1);
308	}
309
310	if ((c = argc - optind) > 0)
311		database = argv[optind++];
312	if ((--c) > 0)
313		key = &argv[optind];
314
315	if (authmech != NULL) {
316		if (__ns_ldap_initAuth(authmech,
317		    &auth,
318		    &errorp) != NS_LDAP_SUCCESS) {
319			if (errorp) {
320				(void) fprintf(stderr, "%s", errorp->message);
321				(void) __ns_ldap_freeError(&errorp);
322			}
323			exit(1);
324		}
325	}
326
327	if (auth.saslmech != NS_LDAP_SASL_GSSAPI &&
328	    standalone_cfg.SA_BIND_DN != NULL &&
329	    standalone_cfg.SA_BIND_PWD == NULL) {
330		/* If password is not specified, then prompt user for it. */
331		standalone_cfg.SA_BIND_PWD =
332		    strdup(getpassphrase("Enter password:"));
333	}
334
335	standalone_cfg.SA_AUTH = (authmech == NULL) ? NULL : &auth;
336
337	if (__ns_ldap_initStandalone(&standalone_cfg,
338	    &errorp) != NS_LDAP_SUCCESS) {
339		if (errorp) {
340			(void) fprintf(stderr, "%s\n", errorp->message);
341			(void) __ns_ldap_freeError(&errorp);
342		}
343		exit(1);
344	}
345
346	if (authmech != NULL) {
347		if (__ns_ldap_setParam(NS_LDAP_AUTH_P,
348		    authmech, &errorp) != NS_LDAP_SUCCESS) {
349			__ns_ldap_cancelStandalone();
350			if (errorp != NULL) {
351				(void) fprintf(stderr, "%s", errorp->message);
352				(void) __ns_ldap_freeError(&errorp);
353			}
354			exit(1);
355		}
356	}
357	if (standalone_cfg.SA_CRED != NULL) {
358		if (__ns_ldap_setParam(NS_LDAP_CREDENTIAL_LEVEL_P,
359		    standalone_cfg.SA_CRED, &errorp) != NS_LDAP_SUCCESS) {
360			__ns_ldap_cancelStandalone();
361			if (errorp != NULL) {
362				(void) fprintf(stderr, "%s", errorp->message);
363				(void) __ns_ldap_freeError(&errorp);
364			}
365			exit(1);
366		}
367	}
368
369	if (standalone_cfg.type != NS_CACHEMGR &&
370	    standalone_cfg.SA_BIND_DN != NULL) {
371		ns_auth_t **authpp = NULL, **authp = NULL;
372
373		if (__ns_ldap_getParam(NS_LDAP_AUTH_P,
374		    (void ***)&authpp,
375		    &errorp) != NS_LDAP_SUCCESS || authpp == NULL) {
376			__ns_ldap_cancelStandalone();
377			(void) __ns_ldap_freeParam((void ***)&authpp);
378			if (errorp) {
379				(void) fprintf(stderr,
380				    gettext(errorp->message));
381				(void) __ns_ldap_freeError(&errorp);
382			}
383			exit(1);
384		}
385		for (authp = authpp; *authp; authp++) {
386			if ((*authp)->saslmech == NS_LDAP_SASL_GSSAPI) {
387				/*
388				 * For now we have no use for bindDN and
389				 * bindPassword when using SASL/GSSAPI.
390				 */
391				(void) fprintf(stderr,
392				    gettext("Warning: SASL/GSSAPI will be "
393				    "used as an authentication method"
394				    "The bind DN and password will "
395				    "be ignored.\n"));
396				break;
397			}
398		}
399	}
400
401	/*
402	 * If dumpping a database,
403	 * or all the containers,
404	 * use page control just
405	 * in case there are too many entries
406	 */
407	if (!key && !(listflag & NS_LDAP_SCOPE_BASE))
408		listflag |= NS_LDAP_PAGE_CTRL;
409
410	/* build the attribute array */
411	if (strncasecmp(attribute, "NULL", 4) == 0)
412		ldapattribute = NULL;
413	else {
414		buffer[0] = strdup(attribute);
415		while ((p = strchr(attribute, ',')) != NULL) {
416			buffer[index++] = attribute = p + 1;
417			*p = '\0';
418		}
419		buffer[index] = NULL;
420		ldapattribute = buffer;
421	}
422
423	/* build the filter */
424	if (database && (strcasecmp(database, "publickey") == NULL)) {
425		/* user publickey lookup */
426		char *err1 = NULL;
427		int  rc1;
428
429		rc = rc1 = -1;
430		ldapfilter = set_filter_publickey(key, database, 0, &udata);
431		if (ldapfilter) {
432			if (verbose) {
433				(void) fprintf(stdout,
434				    gettext("+++ database=%s\n"),
435				    (database ? database : "NULL"));
436				(void) fprintf(stdout,
437				    gettext("+++ filter=%s\n"),
438				    (ldapfilter ? ldapfilter : "NULL"));
439				(void) fprintf(stdout,
440				gettext("+++ template for merging"
441				    "SSD filter=%s\n"),
442				    (udata ? udata : "NULL"));
443			}
444			rc = list("passwd", ldapfilter, ldapattribute,
445			    &err, udata);
446			free(ldapfilter);
447			free(udata);
448		}
449		/* hosts publickey lookup */
450		ldapfilter = set_filter_publickey(key, database, 1, &udata);
451		if (ldapfilter) {
452			if (verbose) {
453				(void) fprintf(stdout,
454				    gettext("+++ database=%s\n"),
455				    (database ? database : "NULL"));
456				(void) fprintf(stdout,
457				    gettext("+++ filter=%s\n"),
458				    (ldapfilter ? ldapfilter : "NULL"));
459				(void) fprintf(stdout,
460				gettext("+++ template for merging"
461				    "SSD filter=%s\n"),
462				    (udata ? udata : "NULL"));
463			}
464			rc1 = list("hosts", ldapfilter, ldapattribute,
465			    &err1, udata);
466			free(ldapfilter);
467			free(udata);
468		}
469		if (rc == -1 && rc1 == -1) {
470			/* this should never happen */
471			(void) fprintf(stderr,
472			    gettext("ldaplist: invalid publickey lookup\n"));
473			rc = 2;
474		} else if (rc != 0 && rc1 != 0) {
475			(void) fprintf(stderr,
476			gettext("ldaplist: %s\n"), (err ? err : err1));
477			if (rc == -1)
478				rc = rc1;
479		} else
480			rc = 0;
481		exit(switch_err(rc));
482	}
483
484	/*
485	 * we set the search filter to (objectclass=*) when we want
486	 * to list the directory attribute instead of the entries
487	 * (the -d option).
488	 */
489	if (((ldapfilter = set_filter(key, database, &udata)) == NULL) ||
490	    (listflag == NS_LDAP_SCOPE_BASE)) {
491		ldapfilter = strdup("objectclass=*");
492		udata = strdup("%s");
493	}
494
495	if (verbose) {
496		(void) fprintf(stdout, gettext("+++ database=%s\n"),
497		    (database ? database : "NULL"));
498		(void) fprintf(stdout, gettext("+++ filter=%s\n"),
499		    (ldapfilter ? ldapfilter : "NULL"));
500		(void) fprintf(stdout,
501		    gettext("+++ template for merging SSD filter=%s\n"),
502		    (udata ? udata : "NULL"));
503	}
504	if (rc = list(database, ldapfilter, ldapattribute, &err, udata))
505		(void) fprintf(stderr, gettext("ldaplist: %s\n"), err);
506
507	__ns_ldap_cancelStandalone();
508
509	if (ldapfilter)
510		free(ldapfilter);
511	if (udata)
512		free(udata);
513	exit(switch_err(rc));
514	return (0); /* Never reached */
515}
516