xref: /illumos-gate/usr/src/cmd/ldap/ns_ldap/ldaplist.c (revision 5aa2fb58)
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 
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 void
44 usage(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  */
112 static int
113 merge_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 */
141 int
142 list(char *database, char *ldapfilter, char **ldapattribute,
143 char **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 
174 int
175 switch_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 
186 int
187 main(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