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