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