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