xref: /illumos-gate/usr/src/cmd/ldap/ns_ldap/ldaplist.c (revision c59d9dff)
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
usage(char * msg)78 usage(char *msg)
79 {
80 	if (msg)
81 		(void) fprintf(stderr, "%s\n", msg);
82 
83 	(void) fprintf(stderr, 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
merge_SSD_filter(const ns_ldap_search_desc_t * desc,char ** realfilter,const void * userdata)147 merge_SSD_filter(const ns_ldap_search_desc_t *desc, char **realfilter,
148     const void *userdata)
149 {
150 	int	len;
151 	char *checker;
152 
153 	/* sanity check */
154 	if (realfilter == NULL)
155 		return (NS_LDAP_INVALID_PARAM);
156 	*realfilter = NULL;
157 
158 	if (desc == NULL || desc->filter == NULL ||
159 	    userdata == NULL)
160 		return (NS_LDAP_INVALID_PARAM);
161 
162 	/* Parameter check.  We only want one %s here, otherwise bail. */
163 	len = 0;	/* Reuse 'len' as "Number of %s hits"... */
164 	checker = (char *)userdata;
165 	do {
166 		checker = strchr(checker, '%');
167 		if (checker != NULL) {
168 			if (len > 0 || *(checker + 1) != 's')
169 				return (NS_LDAP_INVALID_PARAM);
170 			len++;	/* Got our %s. */
171 			checker += 2;
172 		} else if (len != 1)
173 			return (NS_LDAP_INVALID_PARAM);
174 	} while (checker != NULL);
175 
176 	len = strlen(userdata) + strlen(desc->filter) + 1;
177 
178 	*realfilter = (char *)malloc(len);
179 	if (*realfilter == NULL)
180 		return (NS_LDAP_MEMORY);
181 
182 	(void) sprintf(*realfilter, (char *)userdata,
183 	    desc->filter);
184 
185 	return (NS_LDAP_SUCCESS);
186 }
187 
188 /* returns 0=success, 1=error */
189 int
list(char * database,char * ldapfilter,char ** ldapattribute,char ** err,char * userdata)190 list(char *database, char *ldapfilter, char **ldapattribute,
191     char **err, char *userdata)
192 {
193 	ns_ldap_result_t	*result;
194 	ns_ldap_error_t	*errorp;
195 	int		rc;
196 	char		buf[500];
197 	const char	*sort = NULL;
198 	int		i;
199 
200 	if (database) {
201 		for (i = 0; databaselist[i].database; i++) {
202 			if (strcmp(databaselist[i].database, database) == 0) {
203 				sort = databaselist[i].sortattr;
204 				break;
205 			}
206 			if (strcmp(databaselist[i].database,
207 			    NS_LDAP_TYPE_AUTOMOUNT) == 0 &&
208 			    strncmp(database, NS_LDAP_TYPE_AUTOMOUNT,
209 			    sizeof (NS_LDAP_TYPE_AUTOMOUNT) - 1) == 0) {
210 				sort = databaselist[i].sortattr;
211 				break;
212 			}
213 		}
214 	}
215 
216 	*err = NULL;
217 	buf[0] = '\0';
218 	rc = __ns_ldap_list_sort(database, (const char *)ldapfilter,
219 	    sort, merge_SSD_filter, (const char **)ldapattribute, NULL,
220 	    listflag, &result, &errorp, NULL, userdata);
221 	if (rc != NS_LDAP_SUCCESS) {
222 		char *p;
223 		(void) __ns_ldap_err2str(rc, &p);
224 		if (errorp && errorp->message) {
225 			(void) snprintf(buf, sizeof (buf), "%s (%s)",
226 			    p, errorp->message);
227 			(void) __ns_ldap_freeError(&errorp);
228 		} else
229 			(void) snprintf(buf, sizeof (buf), "%s\n", p);
230 		*err = strdup(buf);
231 		return (rc);
232 	}
233 
234 	_printResult(result);
235 	(void) __ns_ldap_freeResult(&result);
236 	return (0);
237 }
238 
239 
240 int
switch_err(int rc)241 switch_err(int rc)
242 {
243 	switch (rc) {
244 	case NS_LDAP_SUCCESS:
245 		return (0);
246 	case NS_LDAP_NOTFOUND:
247 		return (1);
248 	}
249 	return (2);
250 }
251 
252 int
main(int argc,char ** argv)253 main(int argc, char **argv)
254 {
255 
256 	extern int		optind;
257 	char			*database = NULL;
258 	char			*ldapfilter = NULL;
259 	char			*attribute = "dn";
260 	char			**key = NULL;
261 	char			**ldapattribute = NULL;
262 	char			*buffer[100];
263 	char			*err = NULL;
264 	char			*p;
265 	int			index = 1;
266 	int			c;
267 	int			rc;
268 	int			verbose = 0;
269 	char			*udata = NULL;
270 
271 	ns_standalone_conf_t	standalone_cfg = standaloneDefaults;
272 	ns_ldap_error_t		*errorp = NULL;
273 	char			*authmech = NULL;
274 	ns_auth_t		auth = {NS_LDAP_AUTH_NONE,
275 					NS_LDAP_TLS_NONE,
276 					NS_LDAP_SASL_NONE,
277 					NS_LDAP_SASLOPT_NONE};
278 
279 	(void) setlocale(LC_ALL, "");
280 	(void) textdomain(TEXT_DOMAIN);
281 
282 	openlog("ldaplist", LOG_PID, LOG_USER);
283 
284 	if (argc == 2 &&
285 	    strlen(argv[1]) == 2 && strncmp(argv[1], "-h", 2) == 0) {
286 		/* preserve backwards compatability, support old -h option */
287 		(void) printMapping();
288 		exit(0);
289 	}
290 
291 	while ((c = getopt(argc, argv, "h:M:N:P:r:a:D:w:j:dgvl")) != EOF) {
292 		switch (c) {
293 		case 'd':
294 			listflag |= NS_LDAP_SCOPE_BASE;
295 			break;
296 		case 'g':
297 			(void) printMapping();
298 			exit(0);
299 			break; /* Never reached */
300 		case 'l':
301 			attribute = "NULL";
302 			break;
303 		case 'v':
304 			verbose = 1;
305 			break;
306 		case 'M':
307 			standalone_cfg.type = NS_LDAP_SERVER;
308 			standalone_cfg.SA_DOMAIN = optarg;
309 			break;
310 		case 'h':
311 			standalone_cfg.type = NS_LDAP_SERVER;
312 			if (separatePort(optarg,
313 			    &standalone_cfg.SA_SERVER,
314 			    &standalone_cfg.SA_PORT) > 0) {
315 				exit(1);
316 			}
317 			break;
318 		case 'P':
319 			standalone_cfg.type = NS_LDAP_SERVER;
320 			standalone_cfg.SA_CERT_PATH = optarg;
321 			break;
322 		case 'N':
323 			standalone_cfg.type = NS_LDAP_SERVER;
324 			standalone_cfg.SA_PROFILE_NAME = optarg;
325 			break;
326 		case 'D':
327 			standalone_cfg.type = NS_LDAP_SERVER;
328 			standalone_cfg.SA_BIND_DN = strdup(optarg);
329 			break;
330 		case 'w':
331 			if (standalone_cfg.SA_BIND_PWD != NULL) {
332 				(void) fprintf(stderr,
333 				    gettext("The -w option is mutually "
334 				    "exclusive of -j. -w is ignored.\n"));
335 				break;
336 			}
337 
338 			if (optarg != NULL &&
339 			    optarg[0] == '-' && optarg[1] == '\0') {
340 				/* Ask for a password later */
341 				break;
342 			}
343 
344 			standalone_cfg.type = NS_LDAP_SERVER;
345 			standalone_cfg.SA_BIND_PWD = strdup(optarg);
346 			break;
347 		case 'j':
348 			if (standalone_cfg.SA_BIND_PWD != NULL) {
349 				(void) fprintf(stderr,
350 				    gettext("The -w option is mutually "
351 				    "exclusive of -j. -w is ignored.\n"));
352 				free(standalone_cfg.SA_BIND_PWD);
353 			}
354 			standalone_cfg.type = NS_LDAP_SERVER;
355 			standalone_cfg.SA_BIND_PWD = readPwd(optarg);
356 			if (standalone_cfg.SA_BIND_PWD == NULL) {
357 				exit(1);
358 			}
359 			break;
360 		case 'a':
361 			authmech = optarg;
362 			break;
363 		default:
364 			usage(gettext("Invalid option"));
365 		}
366 	}
367 
368 	if (standalone_cfg.type == NS_LDAP_SERVER &&
369 	    standalone_cfg.SA_SERVER == NULL) {
370 		(void) fprintf(stderr,
371 		    gettext("Please specify an LDAP server you want "
372 		    "to connect to. \n"));
373 		exit(1);
374 	}
375 
376 	if ((c = argc - optind) > 0)
377 		database = argv[optind++];
378 	if ((--c) > 0)
379 		key = &argv[optind];
380 
381 	if (authmech != NULL) {
382 		if (__ns_ldap_initAuth(authmech,
383 		    &auth,
384 		    &errorp) != NS_LDAP_SUCCESS) {
385 			if (errorp) {
386 				(void) fprintf(stderr, "%s", errorp->message);
387 				(void) __ns_ldap_freeError(&errorp);
388 			}
389 			exit(1);
390 		}
391 	}
392 
393 	if (auth.saslmech != NS_LDAP_SASL_GSSAPI &&
394 	    standalone_cfg.SA_BIND_DN != NULL &&
395 	    standalone_cfg.SA_BIND_PWD == NULL) {
396 		/* If password is not specified, then prompt user for it. */
397 		standalone_cfg.SA_BIND_PWD =
398 		    strdup(getpassphrase("Enter password:"));
399 	}
400 
401 	standalone_cfg.SA_AUTH = (authmech == NULL) ? NULL : &auth;
402 
403 	if (__ns_ldap_initStandalone(&standalone_cfg,
404 	    &errorp) != NS_LDAP_SUCCESS) {
405 		if (errorp) {
406 			(void) fprintf(stderr, "%s\n", errorp->message);
407 			(void) __ns_ldap_freeError(&errorp);
408 		}
409 		exit(1);
410 	}
411 
412 	if (authmech != NULL) {
413 		if (__ns_ldap_setParam(NS_LDAP_AUTH_P,
414 		    authmech, &errorp) != NS_LDAP_SUCCESS) {
415 			__ns_ldap_cancelStandalone();
416 			if (errorp != NULL) {
417 				(void) fprintf(stderr, "%s", errorp->message);
418 				(void) __ns_ldap_freeError(&errorp);
419 			}
420 			exit(1);
421 		}
422 	}
423 	if (standalone_cfg.SA_CRED != NULL) {
424 		if (__ns_ldap_setParam(NS_LDAP_CREDENTIAL_LEVEL_P,
425 		    standalone_cfg.SA_CRED, &errorp) != NS_LDAP_SUCCESS) {
426 			__ns_ldap_cancelStandalone();
427 			if (errorp != NULL) {
428 				(void) fprintf(stderr, "%s", errorp->message);
429 				(void) __ns_ldap_freeError(&errorp);
430 			}
431 			exit(1);
432 		}
433 	}
434 
435 	if (standalone_cfg.type != NS_CACHEMGR &&
436 	    standalone_cfg.SA_BIND_DN != NULL) {
437 		ns_auth_t **authpp = NULL, **authp = NULL;
438 
439 		if (__ns_ldap_getParam(NS_LDAP_AUTH_P,
440 		    (void ***)&authpp,
441 		    &errorp) != NS_LDAP_SUCCESS || authpp == NULL) {
442 			__ns_ldap_cancelStandalone();
443 			(void) __ns_ldap_freeParam((void ***)&authpp);
444 			if (errorp) {
445 				(void) fprintf(stderr,
446 				    gettext(errorp->message));
447 				(void) __ns_ldap_freeError(&errorp);
448 			}
449 			exit(1);
450 		}
451 		for (authp = authpp; *authp; authp++) {
452 			if ((*authp)->saslmech == NS_LDAP_SASL_GSSAPI) {
453 				/*
454 				 * For now we have no use for bindDN and
455 				 * bindPassword when using SASL/GSSAPI.
456 				 */
457 				(void) fprintf(stderr,
458 				    gettext("Warning: SASL/GSSAPI will be "
459 				    "used as an authentication method"
460 				    "The bind DN and password will "
461 				    "be ignored.\n"));
462 				break;
463 			}
464 		}
465 	}
466 
467 	/*
468 	 * If dumpping a database,
469 	 * or all the containers,
470 	 * use page control just
471 	 * in case there are too many entries
472 	 */
473 	if (!key && !(listflag & NS_LDAP_SCOPE_BASE))
474 		listflag |= NS_LDAP_PAGE_CTRL;
475 
476 	/* build the attribute array */
477 	if (strncasecmp(attribute, "NULL", 4) == 0)
478 		ldapattribute = NULL;
479 	else {
480 		buffer[0] = strdup(attribute);
481 		while ((p = strchr(attribute, ',')) != NULL) {
482 			buffer[index++] = attribute = p + 1;
483 			*p = '\0';
484 		}
485 		buffer[index] = NULL;
486 		ldapattribute = buffer;
487 	}
488 
489 	/* build the filter */
490 	if (database && (strcasecmp(database, "publickey") == 0)) {
491 		/* user publickey lookup */
492 		char *err1 = NULL;
493 		int  rc1;
494 
495 		rc = rc1 = -1;
496 		ldapfilter = set_filter_publickey(key, database, 0, &udata);
497 		if (ldapfilter) {
498 			if (verbose) {
499 				(void) fprintf(stdout,
500 				    gettext("+++ database=%s\n"),
501 				    (database ? database : "NULL"));
502 				(void) fprintf(stdout,
503 				    gettext("+++ filter=%s\n"),
504 				    (ldapfilter ? ldapfilter : "NULL"));
505 				(void) fprintf(stdout,
506 				gettext("+++ template for merging"
507 				    "SSD filter=%s\n"),
508 				    (udata ? udata : "NULL"));
509 			}
510 			rc = list("passwd", ldapfilter, ldapattribute,
511 			    &err, udata);
512 			free(ldapfilter);
513 			free(udata);
514 		}
515 		/* hosts publickey lookup */
516 		ldapfilter = set_filter_publickey(key, database, 1, &udata);
517 		if (ldapfilter) {
518 			if (verbose) {
519 				(void) fprintf(stdout,
520 				    gettext("+++ database=%s\n"),
521 				    (database ? database : "NULL"));
522 				(void) fprintf(stdout,
523 				    gettext("+++ filter=%s\n"),
524 				    (ldapfilter ? ldapfilter : "NULL"));
525 				(void) fprintf(stdout,
526 				gettext("+++ template for merging"
527 				    "SSD filter=%s\n"),
528 				    (udata ? udata : "NULL"));
529 			}
530 			rc1 = list("hosts", ldapfilter, ldapattribute,
531 			    &err1, udata);
532 			free(ldapfilter);
533 			free(udata);
534 		}
535 		if (rc == -1 && rc1 == -1) {
536 			/* this should never happen */
537 			(void) fprintf(stderr,
538 			    gettext("ldaplist: invalid publickey lookup\n"));
539 			rc = 2;
540 		} else if (rc != 0 && rc1 != 0) {
541 			(void) fprintf(stderr,
542 			gettext("ldaplist: %s\n"), (err ? err : err1));
543 			if (rc == -1)
544 				rc = rc1;
545 		} else
546 			rc = 0;
547 		exit(switch_err(rc));
548 	}
549 
550 	/*
551 	 * we set the search filter to (objectclass=*) when we want
552 	 * to list the directory attribute instead of the entries
553 	 * (the -d option).
554 	 */
555 	if (((ldapfilter = set_filter(key, database, &udata)) == NULL) ||
556 	    (listflag == NS_LDAP_SCOPE_BASE)) {
557 		ldapfilter = strdup("objectclass=*");
558 		udata = strdup("%s");
559 	}
560 
561 	if (verbose) {
562 		(void) fprintf(stdout, gettext("+++ database=%s\n"),
563 		    (database ? database : "NULL"));
564 		(void) fprintf(stdout, gettext("+++ filter=%s\n"),
565 		    (ldapfilter ? ldapfilter : "NULL"));
566 		(void) fprintf(stdout,
567 		    gettext("+++ template for merging SSD filter=%s\n"),
568 		    (udata ? udata : "NULL"));
569 	}
570 	if (rc = list(database, ldapfilter, ldapattribute, &err, udata))
571 		(void) fprintf(stderr, gettext("ldaplist: %s\n"), err);
572 
573 	__ns_ldap_cancelStandalone();
574 
575 	if (ldapfilter)
576 		free(ldapfilter);
577 	if (udata)
578 		free(udata);
579 	exit(switch_err(rc));
580 	return (0); /* Never reached */
581 }
582