xref: /illumos-gate/usr/src/lib/print/libpapi-dynamic/common/nss.c (revision f808c858fa61e7769218966759510a8b1190dfcf)
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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* Id: nss.c 180 2006-07-20 17:33:02Z njacobs $ */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <syslog.h>
39 #include <papi.h>
40 #include <uri.h>
41 #include <papi_impl.h>
42 #ifdef NSS_EMULATION
43 #include <nss-emulation.h>
44 #elif NSS_SOLARIS
45 #include <nss_dbdefs.h>
46 #endif
47 #include <config-site.h>
48 #if defined(__sun) && defined(__SVR4)
49 #include <sys/systeminfo.h>
50 #endif
51 
52 
53 static char *
54 bsdaddr_to_uri(papi_attribute_t **list, char *bsdaddr)
55 {
56 	char *result = NULL;
57 
58 	if (bsdaddr != NULL) {
59 		char *bsd[3], *tmp, *iter = NULL;
60 		char buf[512];
61 
62 		tmp = strdup(bsdaddr);
63 
64 		bsd[0] = strtok_r(tmp, ":,", &iter);
65 		if ((bsd[1] = strtok_r(NULL, ":,", &iter)) == NULL)
66 			papiAttributeListGetString(list, NULL,
67 					"printer-name", &bsd[1]);
68 		bsd[2] = strtok_r(NULL, ":,", &iter);
69 
70 		snprintf(buf, sizeof (buf), "lpd://%s/printers/%s%s%s", bsd[0],
71 			(bsd[1] != NULL) ? bsd[1] : "",
72 			(bsd[2] != NULL) ? "#" : "",
73 			(bsd[2] != NULL) ? bsd[2] : "");
74 
75 		free(tmp);
76 
77 		result = strdup(buf);
78 	}
79 
80 	return (result);
81 }
82 
83 #if defined(__sun) && defined(__SVR4)
84 #include <sys/socket.h>
85 #include <sys/ioctl.h>
86 #include <sys/sockio.h>
87 #include <net/if.h>
88 #include <netinet/in.h>
89 #include <arpa/inet.h>
90 #include <netdb.h>
91 
92 static struct in6_addr **
93 local_interfaces()
94 {
95 	struct in6_addr **result = NULL;
96 	int s;
97 	struct lifnum n;
98 	struct lifconf c;
99 	struct lifreq *r;
100 	int count;
101 
102 	/* we need a socket to get the interfaces */
103 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
104 		return (0);
105 
106 	/* get the number of interfaces */
107 	memset(&n, 0, sizeof (n));
108 	n.lifn_family = AF_UNSPEC;
109 	if (ioctl(s, SIOCGLIFNUM, (char *)&n) < 0) {
110 		close(s);
111 		return (0);	/* no interfaces */
112 	}
113 
114 	/* get the interface(s) configuration */
115 	memset(&c, 0, sizeof (c));
116 	c.lifc_family = AF_UNSPEC;
117 	c.lifc_buf = calloc(n.lifn_count, sizeof (struct lifreq));
118 	c.lifc_len = (n.lifn_count * sizeof (struct lifreq));
119 	if (ioctl(s, SIOCGLIFCONF, (char *)&c) < 0) {
120 		free(c.lifc_buf);
121 		close(s);
122 		return (0);	/* can't get interface(s) configuration */
123 	}
124 	close(s);
125 
126 	r = c.lifc_req;
127 	for (count = c.lifc_len / sizeof (struct lifreq);
128 	     count > 0; count--, r++) {
129 		struct in6_addr v6[1], *addr = NULL;
130 
131 		switch (r->lifr_addr.ss_family) {
132 		case AF_INET: {
133 			struct sockaddr_in *s =
134 				(struct sockaddr_in *)&r->lifr_addr;
135 			IN6_INADDR_TO_V4MAPPED(&s->sin_addr, v6);
136 			addr = v6;
137 			}
138 			break;
139 		case AF_INET6: {
140 			struct sockaddr_in6 *s =
141 				(struct sockaddr_in6 *)&r->lifr_addr;
142 			addr = &s->sin6_addr;
143 			}
144 			break;
145 		}
146 
147 		if (addr != NULL) {
148 			struct in6_addr *a = malloc(sizeof (*a));
149 
150 			memcpy(a, addr, sizeof (*a));
151 			list_append(&result, a);
152 		}
153 	}
154 	free(c.lifc_buf);
155 
156 	return (result);
157 }
158 
159 static int
160 match_interfaces(char *host)
161 {
162 	struct in6_addr **lif = local_interfaces();
163 	struct hostent *hp;
164 	int rc = 0;
165 	int errnum;
166 
167 	/* are there any local interfaces */
168 	if (lif == NULL)
169 		return (0);
170 
171 	/* cycle through the host db addresses */
172 	hp = getipnodebyname(host, AF_INET6, AI_ALL|AI_V4MAPPED, &errnum);
173 	if (hp != NULL) {
174 		struct in6_addr **tmp = (struct in6_addr **)hp->h_addr_list;
175 		int i;
176 
177 		for (i = 0; ((rc == 0) && (tmp[i] != NULL)); i++) {
178 			int j;
179 
180 			for (j = 0; ((rc == 0) && (lif[j] != NULL)); j++)
181 				if (memcmp(tmp[i], lif[j],
182 						sizeof (struct in6_addr)) == 0)
183 					rc = 1;
184 		}
185 	}
186 	free(lif);
187 
188 	return (rc);
189 }
190 
191 static int
192 is_localhost(char *host)
193 {
194 	char hostname[BUFSIZ];
195 
196 	/* is it "localhost" */
197 	if (strncasecmp(host, "localhost", 10) == 0)
198 		return (1);
199 
200 	/* is it the {nodename} */
201 	sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
202 	if (strncasecmp(host, hostname, strlen(hostname)) == 0)
203 		return (1);
204 
205 	/* does it match one of the host's configured interfaces */
206 	if (match_interfaces(host) != 0)
207 		return (1);
208 
209 	return (0);
210 }
211 
212 /*
213  * This is an awful HACK to force the dynamic PAPI library to use the
214  * lpsched support when the destination apears to be a local lpsched
215  * queue on Solaris.
216  */
217 static void
218 solaris_lpsched_shortcircuit_hack(papi_attribute_t ***list)
219 {
220 	papi_attribute_t *attribute;
221 	uri_t *uri = NULL;
222 	char *printer = NULL;
223 	char buf[128], buf2[128];
224 
225 	/* setting this in the calling env can be useful for debugging */
226 	if (getenv("DISABLE_LPSCHED_SHORTCIRCUIT") != NULL)
227 		return;
228 
229 	papiAttributeListGetString(*list, NULL,
230 				"printer-uri-supported", &printer);
231 	if (uri_from_string(printer, &uri) < 0)
232 		return;
233 
234 	/* already an lpsched URI ? */
235 	if (strcasecmp(uri->scheme, "lpsched") == 0)
236 		return;
237 
238 	if ((printer = strrchr(uri->path, '/')) == NULL)
239 		printer = uri->path;
240 	else
241 		printer++;
242 
243 	/* is there an lpsched queue (printer/class) */
244 	snprintf(buf, sizeof (buf), "/etc/lp/interfaces/%s", printer);
245 	snprintf(buf2, sizeof (buf2), "/etc/lp/classes/%s", printer);
246 	if ((access(buf, F_OK) < 0) && (access(buf2, F_OK) < 0))
247 		return;
248 
249 	/* is this the "local" host */
250 	if ((uri->host != NULL) && (is_localhost(uri->host) == 0))
251 		return;
252 
253 	snprintf(buf, sizeof (buf), "lpsched://%s/printers/%s",
254 			(uri->host ? uri->host : "localhost"), printer);
255 	papiAttributeListAddString(list, PAPI_ATTR_REPLACE,
256 			"printer-uri-supported", buf);
257 }
258 #endif
259 
260 static void
261 fill_printer_uri_supported(papi_attribute_t ***list)
262 {
263 	papi_attribute_t *attribute;
264 	char *string = NULL;
265 
266 	/* do we have a printer-uri-supported */
267 	attribute = papiAttributeListFind(*list, "printer-uri-supported");
268 	if (attribute != NULL) /* we have what we need, return */
269 		return;
270 
271 	/* do we have a printer-uri to rename */
272 	attribute = papiAttributeListFind(*list, "printer-uri");
273 	if (attribute != NULL) { /* rename it in place and return */
274 		free(attribute->name);
275 		attribute->name = strdup("printer-uri-supported");
276 		return;
277 	}
278 
279 	/* do we have a printers.conf(4) "bsdaddr" to convert */
280 	papiAttributeListGetString(*list, NULL, "bsdaddr", &string);
281 	if (string != NULL) { /* parse it, convert it, add it */
282 		char *uri = bsdaddr_to_uri(*list, string);
283 
284 		if (uri != NULL) {
285 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
286 					"printer-uri-supported", uri);
287 			papiAttributeListDelete(list, "bsdaddr");
288 			free(uri);
289 			return;
290 		}
291 	}
292 
293 	/* do we have a printers.conf(4) "rm" (and "rp") to convert */
294 	papiAttributeListGetString(*list, NULL, "rm", &string);
295 	if (string != NULL) {
296 		char *rp = NULL;
297 
298 		/* default to "printer-name", but use "rp" if we have it */
299 		papiAttributeListGetString(*list, NULL, "printer-name", &rp);
300 		papiAttributeListGetString(*list, NULL, "rp", &rp);
301 
302 		if (rp != NULL) { /* fill in the uri if we have the data */
303 			char buf[BUFSIZ];
304 
305 			snprintf(buf, sizeof (buf), "lpd://%s/printers/%s",
306 				string, rp);
307 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
308 					"printer-uri-supported", strdup(buf));
309 			return;
310 		}
311 	}
312 
313 	/* if were are here, we don't have a printer-uri-supported */
314 }
315 
316 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
317 static void
318 fill_printer_uri(papi_attribute_t ***list)
319 {
320 	papi_attribute_t *attribute;
321 	char *uri = NULL;
322 
323 	if ((list == NULL) || (*list == NULL))
324 		return;
325 
326 	/* do we have a printer-uri */
327 	attribute = papiAttributeListFind(*list, "printer-uri");
328 	if (attribute != NULL) /* we have what we need, return */
329 		return;
330 
331 	/*
332 	 * this is sufficient to fool libgnomeprintpapi, but not promote it's
333 	 * use in the future.
334 	 */
335 	papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri",
336 			"broken printer-uri semantic");
337 }
338 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
339 
340 static void
341 cvt_all_to_member_names(papi_attribute_t ***list)
342 {
343 	papi_status_t status;
344 	void *iter = NULL;
345 	char *string = NULL;
346 
347 	papiAttributeListGetString(*list, NULL, "member-names", &string);
348 	if (string != NULL) /* already have a member-names */
349 		return;
350 
351 	for (status = papiAttributeListGetString(*list, &iter, "all", &string);
352 	     status == PAPI_OK;
353 	     status = papiAttributeListGetString(*list, &iter, NULL, &string)) {
354 		char *s_iter = NULL, *value, *tmp = strdup(string);
355 
356 		for (value = strtok_r(tmp, ", \t", &s_iter);
357 		     value != NULL;
358 		     value = strtok_r(NULL, ", \t", &s_iter))
359 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
360 					"member-names", value);
361 		free(tmp);
362 	}
363 }
364 
365 static papi_attribute_t **
366 _cvt_nss_entry_to_printer(char *entry)
367 {
368 	char    *key = NULL,
369 		*cp,
370 		buf[BUFSIZ];
371 	int in_namelist = 1, buf_pos = 0;
372 	papi_attribute_t **list = NULL;
373 
374 	if (entry == NULL)
375 		return (NULL);
376 
377 	memset(buf, 0, sizeof (buf));
378 	for (cp = entry; *cp != '\0'; cp++) {
379 		switch (*cp) {
380 		case ':':	/* end of kvp */
381 			if (in_namelist != 0) {
382 				papiAttributeListAddString(&list,
383 					PAPI_ATTR_APPEND, "printer-name", buf);
384 				in_namelist = 0;
385 			} else if (key != NULL)
386 				papiAttributeListAddString(&list,
387 					PAPI_ATTR_APPEND, key, buf);
388 			memset(buf, 0, sizeof (buf));
389 			buf_pos = 0;
390 			key = NULL;
391 			break;
392 		case '=':	/* kvp seperator */
393 			if (key == NULL) {
394 				key = strdup(buf);
395 				memset(buf, 0, sizeof (buf));
396 				buf_pos = 0;
397 			} else
398 				buf[buf_pos++] = *cp;
399 			break;
400 		case '|':	/* namelist seperator */
401 			if (in_namelist != 0) {
402 				papiAttributeListAddString(&list,
403 					PAPI_ATTR_APPEND, "printer-name", buf);
404 				memset(buf, 0, sizeof (buf));
405 				buf_pos = 0;
406 			} else	/* add it to the buffer */
407 				buf[buf_pos++] = *cp;
408 			break;
409 		case '\\':	/* escape char */
410 			buf[buf_pos++] = *(++cp);
411 			break;
412 		default:
413 			buf[buf_pos++] = *cp;
414 		}
415 
416 	}
417 
418 	if (key != NULL)
419 		papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf);
420 
421 	/* resolve any "use" references in the configuration DB */
422 	key = NULL;
423 	papiAttributeListGetString(list, NULL, "use", &key);
424 	if (key != NULL) {
425 		papi_attribute_t **use_attrs = getprinterbyname(key, NULL);
426 
427 		list_concatenate(&list, use_attrs);
428 	}
429 
430 	fill_printer_uri_supported(&list);
431 	cvt_all_to_member_names(&list); /* convert "all" to "member-names" */
432 
433 	return (list);
434 }
435 
436 #if defined(NSS_SOLARIS) && !defined(NSS_EMULATION)
437 
438 #ifndef	NSS_DBNAM__PRINTERS	/* not in nss_dbdefs.h because it's private */
439 #define	NSS_DBNAM__PRINTERS	"_printers"
440 #endif
441 
442 static DEFINE_NSS_DB_ROOT(db_root);
443 static DEFINE_NSS_GETENT(context);
444 
445 static char *private_ns = NULL;
446 static char initialized = 0;
447 
448 static void
449 _nss_initf_printers(p)
450     nss_db_params_t *p;
451 {
452 	if (private_ns != NULL) {
453 		/*
454 		 * because we need to support a legacy interface that allows
455 		 * us to select a specific name service, we need to dummy up
456 		 * the parameters to use a private nsswitch database and set
457 		 * the * default_config entry to the name service we are
458 		 * looking into.
459 		 */
460 		p->name = NSS_DBNAM__PRINTERS;		/* "_printers" */
461 		p->default_config = private_ns;
462 		private_ns = NULL;
463 	} else if (initialized == 0) {
464 		/* regular behaviour */
465 		p->name = NSS_DBNAM_PRINTERS;	 /* "printers" */
466 		p->default_config = NSS_DEFCONF_PRINTERS;
467 		initialized = 1;
468 	}
469 	syslog(LOG_DEBUG, "database: %s, services: %s",
470 		(p->name ? p->name : "NULL"),
471 		(p->default_config ? p->default_config : "NULL"));
472 }
473 
474 /*
475  * Return values: 0 = success, 1 = parse error, 2 = erange ...
476  * The structure pointer passed in is a structure in the caller's space
477  * wherein the field pointers would be set to areas in the buffer if
478  * need be. instring and buffer should be separate areas.
479  */
480 /* ARGSUSED */
481 static int
482 str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
483 {
484 	if (lenstr + 1 > buflen)
485 		return (NSS_STR_PARSE_ERANGE);
486 	/*
487 	 * We copy the input string into the output buffer
488 	 */
489 	(void) memcpy(buffer, instr, lenstr);
490 	buffer[lenstr] = '\0';
491 
492 	return (NSS_STR_PARSE_SUCCESS);
493 }
494 #endif /* NSS_SOLARIS */
495 
496 int
497 setprinterentry(int stayopen, char *ns)
498 {
499 #ifdef NSS_EMULATION
500 	emul_setprinterentry(stayopen);
501 #elif NSS_SOLARIS
502 	initialized = 0;
503 	private_ns = ns;
504 	nss_setent(&db_root, _nss_initf_printers, &context);
505 #endif
506 	return (0);
507 }
508 
509 
510 int
511 endprinterentry(int i)
512 {
513 #ifdef NSS_EMULATION
514 	emul_endprinterentry();
515 #elif NSS_SOLARIS
516 	initialized = 0;
517 	nss_endent(&db_root, _nss_initf_printers, &context);
518 	nss_delete(&db_root);
519 #endif
520 	return (0);
521 }
522 
523 /* ARGSUSED2 */
524 papi_attribute_t **
525 getprinterentry(char *ns)
526 {
527 	papi_attribute_t **result = NULL;
528 
529 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
530 	char buf[10240];
531 	nss_status_t	res = NSS_NOTFOUND;
532 
533 #ifdef NSS_EMULATION
534 	res = emul_getprinterentry_r(buf, sizeof (buf));
535 #elif NSS_SOLARIS
536 	nss_XbyY_args_t arg;
537 
538 	NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
539 	res = nss_getent(&db_root, _nss_initf_printers, &context, &arg);
540 	(void) NSS_XbyY_FINI(&arg);
541 #endif
542 
543 	if (res != NSS_SUCCESS)
544 		buf[0] = '\0';
545 
546 	result = _cvt_nss_entry_to_printer(buf);
547 #if defined(__sun) && defined(__SVR4)
548 	solaris_lpsched_shortcircuit_hack(&result);
549 #endif
550 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
551 	fill_printer_uri(&result);
552 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
553 #endif
554 
555 #ifdef DEBUG
556 	printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result);
557 	if (result != NULL) {
558 		char buf[4096];
559 
560 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
561 		printf("\t%s\n", buf);
562 	}
563 #endif /* DEBUG */
564 
565 	return (result);
566 }
567 
568 
569 papi_attribute_t **
570 getprinterbyname(char *name, char *ns)
571 {
572 	papi_attribute_t **result = NULL;
573 
574 	if (strstr(name, "://") != NULL) {	/* shortcut for URI form */
575 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
576 				"printer-name", name);
577 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
578 				"printer-uri-supported", name);
579 	} else if (strchr(name, ':') != NULL) {	/* shortcut for POSIX form */
580 		char *uri = bsdaddr_to_uri(result, name);
581 
582 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
583 				"printer-name", name);
584 		if (uri != NULL) {
585 			papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
586 					"printer-uri-supported", uri);
587 			free(uri);
588 		}
589 	} else {				/* anything else */
590 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
591 		char buf[10240];
592 		nss_status_t	res = NSS_NOTFOUND;
593 
594 #ifdef NSS_EMULATION
595 		res = emul_getprinterbyname_r(name, buf, sizeof (buf));
596 #elif NSS_SOLARIS
597 		nss_XbyY_args_t arg;
598 
599 		private_ns = ns;
600 		NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
601 		arg.key.name = name;
602 		res = nss_search(&db_root, _nss_initf_printers,
603 				NSS_DBOP_PRINTERS_BYNAME, &arg);
604 		(void) NSS_XbyY_FINI(&arg);
605 
606 		if (res != NSS_SUCCESS)
607 			buf[0] = '\0';
608 #endif
609 
610 		result = _cvt_nss_entry_to_printer(buf);
611 #endif
612 	}
613 #if defined(__sun) && defined(__SVR4)
614 	solaris_lpsched_shortcircuit_hack(&result);
615 #endif
616 #ifdef DEBUG
617 	printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"),
618 		name, result);
619 	if (result != NULL) {
620 		char buf[4096];
621 
622 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
623 		printf("\t%s\n", buf);
624 	}
625 #endif /* DEBUG */
626 
627 	return (result);
628 }
629