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