xref: /onnv-gate/usr/src/lib/print/libpapi-lpd/common/lpd-port.c (revision 2264:b2b9267d002d)
1*2264Sjacobs /*
2*2264Sjacobs  * CDDL HEADER START
3*2264Sjacobs  *
4*2264Sjacobs  * The contents of this file are subject to the terms of the
5*2264Sjacobs  * Common Development and Distribution License (the "License").
6*2264Sjacobs  * You may not use this file except in compliance with the License.
7*2264Sjacobs  *
8*2264Sjacobs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2264Sjacobs  * or http://www.opensolaris.org/os/licensing.
10*2264Sjacobs  * See the License for the specific language governing permissions
11*2264Sjacobs  * and limitations under the License.
12*2264Sjacobs  *
13*2264Sjacobs  * When distributing Covered Code, include this CDDL HEADER in each
14*2264Sjacobs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2264Sjacobs  * If applicable, add the following below this CDDL HEADER, with the
16*2264Sjacobs  * fields enclosed by brackets "[]" replaced with your own identifying
17*2264Sjacobs  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2264Sjacobs  *
19*2264Sjacobs  * CDDL HEADER END
20*2264Sjacobs  */
21*2264Sjacobs 
22*2264Sjacobs /*
23*2264Sjacobs  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*2264Sjacobs  * Use is subject to license terms.
25*2264Sjacobs  *
26*2264Sjacobs  */
27*2264Sjacobs 
28*2264Sjacobs /* $Id: lpd-port.c 155 2006-04-26 02:34:54Z ktou $ */
29*2264Sjacobs 
30*2264Sjacobs #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*2264Sjacobs 
32*2264Sjacobs #include <config-site.h>
33*2264Sjacobs 
34*2264Sjacobs #include <stdio.h>
35*2264Sjacobs #include <stdlib.h>
36*2264Sjacobs #include <unistd.h>
37*2264Sjacobs #include <sys/types.h>
38*2264Sjacobs #include <sys/stat.h>
39*2264Sjacobs #include <fcntl.h>
40*2264Sjacobs #include <stdarg.h>
41*2264Sjacobs #include <string.h>
42*2264Sjacobs #include <signal.h>
43*2264Sjacobs #include <sys/socket.h>
44*2264Sjacobs #include <netinet/in.h>
45*2264Sjacobs #include <arpa/inet.h>
46*2264Sjacobs #include <netdb.h>
47*2264Sjacobs #include <errno.h>
48*2264Sjacobs #include <syslog.h>
49*2264Sjacobs #include <values.h>
50*2264Sjacobs #include <stropts.h>	/* for sendfd */
51*2264Sjacobs #include <sys/uio.h>	/* for sendmsg stuff */
52*2264Sjacobs #include <pwd.h>
53*2264Sjacobs #include <sys/sendfile.h>
54*2264Sjacobs #include <ctype.h>
55*2264Sjacobs #include <alloca.h>
56*2264Sjacobs #ifdef HAVE_PRIV_H
57*2264Sjacobs #include <priv.h>
58*2264Sjacobs #endif
59*2264Sjacobs #include <papi_impl.h>
60*2264Sjacobs 
61*2264Sjacobs #ifndef	JOB_ID_FILE
62*2264Sjacobs #define	JOB_ID_FILE	"/var/run/rfc-1179.seq"
63*2264Sjacobs #endif	/* JOB_ID_FILE */
64*2264Sjacobs 
65*2264Sjacobs static int
66*2264Sjacobs sendfd(int sockfd, int fd)
67*2264Sjacobs {
68*2264Sjacobs 	syslog(LOG_DEBUG, "sendfd(%d, %d)", sockfd, fd);
69*2264Sjacobs 
70*2264Sjacobs #if defined(sun) && defined(unix) && defined(I_SENDFD)
71*2264Sjacobs 	return (ioctl(sockfd, I_SENDFD, fd));
72*2264Sjacobs #else
73*2264Sjacobs 	struct iovec	iov[1];
74*2264Sjacobs 	struct msghdr	msg;
75*2264Sjacobs #ifdef CMSG_DATA
76*2264Sjacobs 	struct cmsghdr cmp[1];
77*2264Sjacobs 	char buf[2];    /* send/recv 2 byte protocol */
78*2264Sjacobs 
79*2264Sjacobs 	iov[0].iov_base = buf;
80*2264Sjacobs 	iov[0].iov_len = 2;
81*2264Sjacobs 
82*2264Sjacobs 	cmp[0].cmsg_level = SOL_SOCKET;
83*2264Sjacobs 	cmp[0].cmsg_type = SCM_RIGHTS;
84*2264Sjacobs 	cmp[0].cmsg_len = sizeof (struct cmsghdr) + sizeof (int);
85*2264Sjacobs 	* (int *)CMSG_DATA(cmp) = fd;
86*2264Sjacobs 
87*2264Sjacobs 	buf[1] = 0;
88*2264Sjacobs 	buf[0] = 0;
89*2264Sjacobs 	msg.msg_control = cmp;
90*2264Sjacobs 	msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int);
91*2264Sjacobs #else
92*2264Sjacobs 	iov[0].iov_base = NULL;
93*2264Sjacobs 	iov[0].iov_len = 0;
94*2264Sjacobs 	msg.msg_accrights = (caddr_t)&fd;
95*2264Sjacobs 	msg.msg_accrights = sizeof (fd);
96*2264Sjacobs #endif
97*2264Sjacobs 	msg.msg_iov = iov;
98*2264Sjacobs 	msg.msg_iovlen = 1;
99*2264Sjacobs 	msg.msg_name = NULL;
100*2264Sjacobs 	msg.msg_namelen = 0;
101*2264Sjacobs 
102*2264Sjacobs 	return (sendmsg(sockfd, &msg, 0));
103*2264Sjacobs #endif
104*2264Sjacobs }
105*2264Sjacobs 
106*2264Sjacobs static void
107*2264Sjacobs null(int i)
108*2264Sjacobs {
109*2264Sjacobs }
110*2264Sjacobs 
111*2264Sjacobs static int
112*2264Sjacobs sock_connect(int sock, uri_t *uri, int timeout)
113*2264Sjacobs {
114*2264Sjacobs 	struct hostent *hp;
115*2264Sjacobs 	struct servent *sp;
116*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF)
117*2264Sjacobs 	struct sockaddr_in6 sin;
118*2264Sjacobs #else
119*2264Sjacobs 	struct sockaddr_in sin;
120*2264Sjacobs #endif
121*2264Sjacobs 	static void (*old_handler)();
122*2264Sjacobs 	int	err,
123*2264Sjacobs 		error_num;
124*2264Sjacobs 	unsigned timo = 1;
125*2264Sjacobs 	int port = -1;
126*2264Sjacobs 
127*2264Sjacobs 
128*2264Sjacobs 	/*
129*2264Sjacobs 	 * Get the host address and port number to connect to.
130*2264Sjacobs 	 */
131*2264Sjacobs 	if ((uri == NULL) || (uri->host == NULL)) {
132*2264Sjacobs 		return (-1);
133*2264Sjacobs 	}
134*2264Sjacobs 
135*2264Sjacobs 	/* linux style NULL usage */
136*2264Sjacobs 	(void) memset((char *)&sin, (int)NULL, sizeof (sin));
137*2264Sjacobs 
138*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF)
139*2264Sjacobs 	if ((hp = getipnodebyname(uri->host, AF_INET6, AI_DEFAULT,
140*2264Sjacobs 		    &error_num)) == NULL) {
141*2264Sjacobs 		errno = ENOENT;
142*2264Sjacobs 		return (-1);
143*2264Sjacobs 	}
144*2264Sjacobs 	(void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length);
145*2264Sjacobs 	sin.sin6_family = hp->h_addrtype;
146*2264Sjacobs #else
147*2264Sjacobs 	if ((hp = gethostbyname(uri->host)) == NULL) {
148*2264Sjacobs 		errno = ENOENT;
149*2264Sjacobs 		return (-1);
150*2264Sjacobs 	}
151*2264Sjacobs 
152*2264Sjacobs 	(void) memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length);
153*2264Sjacobs 	sin.sin_family = hp->h_addrtype;
154*2264Sjacobs #endif
155*2264Sjacobs 
156*2264Sjacobs 	if ((sp = getservbyname("printer", "tcp")) == NULL) {
157*2264Sjacobs 		errno = ENOENT;
158*2264Sjacobs 		return (-1);
159*2264Sjacobs 	}
160*2264Sjacobs 
161*2264Sjacobs 	if (uri->port != NULL)
162*2264Sjacobs 		port = atoi(uri->port);
163*2264Sjacobs 	if (port < 0)
164*2264Sjacobs 		port = sp->s_port;
165*2264Sjacobs 
166*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF)
167*2264Sjacobs 	sin.sin6_port = port;
168*2264Sjacobs #else
169*2264Sjacobs 	sin.sin_port = port;
170*2264Sjacobs #endif
171*2264Sjacobs 
172*2264Sjacobs retry:
173*2264Sjacobs 	old_handler = signal(SIGALRM, null);
174*2264Sjacobs 	(void) alarm(timeout);
175*2264Sjacobs 
176*2264Sjacobs 	if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
177*2264Sjacobs 		(void) alarm(0);
178*2264Sjacobs 		(void) signal(SIGALRM, old_handler);
179*2264Sjacobs 
180*2264Sjacobs 		if (errno == ECONNREFUSED && timo <= 16) {
181*2264Sjacobs 			(void) sleep(timo);
182*2264Sjacobs 			timo *= 2;
183*2264Sjacobs 			goto retry;
184*2264Sjacobs 		}
185*2264Sjacobs 
186*2264Sjacobs 		return (-1);
187*2264Sjacobs 	}
188*2264Sjacobs 
189*2264Sjacobs 	(void) alarm(0);
190*2264Sjacobs 	(void) signal(SIGALRM, old_handler);
191*2264Sjacobs 	return (sock);
192*2264Sjacobs }
193*2264Sjacobs 
194*2264Sjacobs static int
195*2264Sjacobs next_job_id()
196*2264Sjacobs {
197*2264Sjacobs 	int fd, result = getpid() % 1000;
198*2264Sjacobs 
199*2264Sjacobs 	/* gain back enough privilege to open the id file */
200*2264Sjacobs #ifdef	PRIV_ALLSETS
201*2264Sjacobs 	if ((priv_set(PRIV_ON, PRIV_EFFECTIVE,
202*2264Sjacobs 			PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL)) < 0) {
203*2264Sjacobs 		syslog(LOG_ERR, "lpd_port:next_job_id:priv_set fails: : %m");
204*2264Sjacobs 		return (-1);
205*2264Sjacobs 	}
206*2264Sjacobs #else
207*2264Sjacobs 	seteuid(0);
208*2264Sjacobs #endif
209*2264Sjacobs 
210*2264Sjacobs 	/* open the sequence file */
211*2264Sjacobs 	if (((fd = open(JOB_ID_FILE, O_RDWR)) < 0) && (errno == ENOENT))
212*2264Sjacobs 		fd = open(JOB_ID_FILE, O_CREAT|O_EXCL|O_RDWR, 0644);
213*2264Sjacobs 
214*2264Sjacobs 	syslog(LOG_DEBUG, "sequence file fd: %d", fd);
215*2264Sjacobs 
216*2264Sjacobs 	/* drop our privilege again */
217*2264Sjacobs #ifdef	PRIV_ALLSETS
218*2264Sjacobs 	/* drop file access privilege */
219*2264Sjacobs 	priv_set(PRIV_OFF, PRIV_PERMITTED,
220*2264Sjacobs 			PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL);
221*2264Sjacobs #else
222*2264Sjacobs 	seteuid(getuid());
223*2264Sjacobs #endif
224*2264Sjacobs 
225*2264Sjacobs 	if (fd >= 0) {
226*2264Sjacobs 		/* wait for a lock on the file */
227*2264Sjacobs 		if (lockf(fd, F_LOCK, 0) == 0) {
228*2264Sjacobs 			char buf[8];
229*2264Sjacobs 			int next;
230*2264Sjacobs 
231*2264Sjacobs 			/* get the current id */
232*2264Sjacobs 			(void) memset(buf, 0, sizeof (buf));
233*2264Sjacobs 			if (read(fd, buf, sizeof (buf)) > 0)
234*2264Sjacobs 				result = atoi(buf);
235*2264Sjacobs 
236*2264Sjacobs 			next = ((result < 999) ? (result + 1) : 0);
237*2264Sjacobs 
238*2264Sjacobs 			/* store the next id in the file */
239*2264Sjacobs 			snprintf(buf, sizeof (buf), "%.3d", next);
240*2264Sjacobs 			if ((lseek(fd, 0, SEEK_SET) == 0) &&
241*2264Sjacobs 			    (ftruncate(fd, 0) == 0))
242*2264Sjacobs 				write(fd, buf, strlen(buf));
243*2264Sjacobs 		}
244*2264Sjacobs 	}
245*2264Sjacobs 	syslog(LOG_DEBUG, "next_job_id() is %d", result);
246*2264Sjacobs 
247*2264Sjacobs 	return (result);
248*2264Sjacobs }
249*2264Sjacobs 
250*2264Sjacobs static int
251*2264Sjacobs reserved_port()
252*2264Sjacobs {
253*2264Sjacobs 	int result = -1;
254*2264Sjacobs 	int port;
255*2264Sjacobs 
256*2264Sjacobs 	/* gain back enough privilege to open a reserved port */
257*2264Sjacobs #ifdef	PRIV_ALLSETS
258*2264Sjacobs 	if ((priv_set(
259*2264Sjacobs 		PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) {
260*2264Sjacobs 		syslog(LOG_ERR, "priv_set fails for net_privaddr %m");
261*2264Sjacobs 		return (-1);
262*2264Sjacobs 	}
263*2264Sjacobs #else
264*2264Sjacobs 	seteuid(0);
265*2264Sjacobs #endif
266*2264Sjacobs 
267*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF)
268*2264Sjacobs 	port = 0;	/* set to 0, rresvport_af() will find us one. */
269*2264Sjacobs 	result = rresvport_af(&port, AF_INET6);
270*2264Sjacobs #else
271*2264Sjacobs 	port = IPPORT_RESERVED - 1;
272*2264Sjacobs 	while (((result = rresvport(&port)) < 0) && (port >= 0))
273*2264Sjacobs 		port--;
274*2264Sjacobs #endif
275*2264Sjacobs 
276*2264Sjacobs 	/* drop our privilege again */
277*2264Sjacobs #ifdef	PRIV_ALLSETS
278*2264Sjacobs 	priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL);
279*2264Sjacobs #else
280*2264Sjacobs 	seteuid(getuid());
281*2264Sjacobs #endif
282*2264Sjacobs 
283*2264Sjacobs 	return (result);
284*2264Sjacobs }
285*2264Sjacobs 
286*2264Sjacobs static char *
287*2264Sjacobs get_user_name()
288*2264Sjacobs {
289*2264Sjacobs 	static struct passwd *p = NULL;
290*2264Sjacobs 
291*2264Sjacobs 	if ((p = getpwuid(getuid())) != NULL)
292*2264Sjacobs 		return (p->pw_name);
293*2264Sjacobs 	else
294*2264Sjacobs 		return ("unknown");
295*2264Sjacobs }
296*2264Sjacobs 
297*2264Sjacobs static void
298*2264Sjacobs add_args(int ac, char **av, char *buf, size_t len)
299*2264Sjacobs {
300*2264Sjacobs 	while (ac--) {
301*2264Sjacobs 		strlcat(buf, " ", len);
302*2264Sjacobs 		strlcat(buf, *(av++), len);
303*2264Sjacobs 	}
304*2264Sjacobs }
305*2264Sjacobs 
306*2264Sjacobs static int
307*2264Sjacobs massage_control_data(char *data, int id)
308*2264Sjacobs {
309*2264Sjacobs 	char *line, *iter = NULL;
310*2264Sjacobs 	char *ptr;
311*2264Sjacobs 	char host[BUFSIZ];
312*2264Sjacobs 
313*2264Sjacobs 	gethostname(host, sizeof (host));
314*2264Sjacobs 
315*2264Sjacobs 	for (ptr = strchr(data, '\n'); ptr != NULL; ptr = strchr(ptr, '\n')) {
316*2264Sjacobs 		ptr++;
317*2264Sjacobs 
318*2264Sjacobs 		if (ptr[0] == 'H') {
319*2264Sjacobs 			if (strncmp(++ptr, host, strlen(host)) != 0)
320*2264Sjacobs 				return (-1);
321*2264Sjacobs 		} else if ((ptr[0] == 'P') || (ptr[0] == 'L')) {
322*2264Sjacobs 			/* check the user name */
323*2264Sjacobs 			uid_t uid = getuid();
324*2264Sjacobs 			struct passwd *pw;
325*2264Sjacobs 			int len;
326*2264Sjacobs 
327*2264Sjacobs 			if (uid == 0)	/* let root do what they want */
328*2264Sjacobs 				continue;
329*2264Sjacobs 			if ((pw = getpwuid(uid)) == NULL)
330*2264Sjacobs 				return (-1);	/* failed */
331*2264Sjacobs 			len = strlen(pw->pw_name);
332*2264Sjacobs 			if ((strncmp(++ptr, pw->pw_name, len) != 0) ||
333*2264Sjacobs 			    (ptr[len] != '\n'))
334*2264Sjacobs 				return (-1);	/* failed */
335*2264Sjacobs 		} else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) {
336*2264Sjacobs 			/* check/fix df?XXXhostname */
337*2264Sjacobs 			ptr++;
338*2264Sjacobs 
339*2264Sjacobs 			if (strlen(ptr) < 6)
340*2264Sjacobs 				return (-1);
341*2264Sjacobs 			if ((ptr[0] == 'd') && (ptr[1] == 'f') &&
342*2264Sjacobs 			    (ptr[3] == 'X') && (ptr[4] == 'X') &&
343*2264Sjacobs 			    (ptr[5] == 'X')) {
344*2264Sjacobs 				ptr[3] = '0' + (id / 100) % 10;
345*2264Sjacobs 				ptr[4] = '0' + (id / 10) % 10;
346*2264Sjacobs 				ptr[5] = '0' + id % 10;
347*2264Sjacobs 
348*2264Sjacobs 			if (strncmp(&ptr[6], host, strlen(host)) != 0)
349*2264Sjacobs 				return (-1);
350*2264Sjacobs 			} else
351*2264Sjacobs 				return (-1);
352*2264Sjacobs 		}
353*2264Sjacobs 	}
354*2264Sjacobs 	return (1);
355*2264Sjacobs }
356*2264Sjacobs 
357*2264Sjacobs static int
358*2264Sjacobs send_lpd_message(int fd, char *fmt, ...)
359*2264Sjacobs {
360*2264Sjacobs 	char buf[BUFSIZ];
361*2264Sjacobs 	size_t size;
362*2264Sjacobs 	va_list ap;
363*2264Sjacobs 
364*2264Sjacobs 	va_start(ap, fmt);
365*2264Sjacobs 	size = vsnprintf(buf, sizeof (buf), fmt, ap);
366*2264Sjacobs 	va_end(ap);
367*2264Sjacobs 	if (size == 0)
368*2264Sjacobs 		size = 1;
369*2264Sjacobs 
370*2264Sjacobs 	syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf);
371*2264Sjacobs 
372*2264Sjacobs 	if (write(fd, buf, size) != size) {
373*2264Sjacobs 		errno = EIO;
374*2264Sjacobs 		return (-1);
375*2264Sjacobs 	}
376*2264Sjacobs 
377*2264Sjacobs 	if ((read(fd, buf, 1) != 1) || (buf[0] != 0))
378*2264Sjacobs 		return (-1);
379*2264Sjacobs 
380*2264Sjacobs 	return (0);
381*2264Sjacobs }
382*2264Sjacobs 
383*2264Sjacobs static int
384*2264Sjacobs send_data_file(int sock, char *dfname, char *name)
385*2264Sjacobs {
386*2264Sjacobs 	size_t len;
387*2264Sjacobs 	off_t off = 0;
388*2264Sjacobs 	struct stat st;
389*2264Sjacobs 	char buf[32];
390*2264Sjacobs 	int fd = -1;
391*2264Sjacobs 
392*2264Sjacobs 	if (strcmp(name, "standard input") != 0) {
393*2264Sjacobs 		if ((fd = open(name, O_RDONLY)) < 0)
394*2264Sjacobs 			return (-1);
395*2264Sjacobs 
396*2264Sjacobs 		if (fstat(fd, &st) < 0)
397*2264Sjacobs 			return (-1);
398*2264Sjacobs 	} else
399*2264Sjacobs 		st.st_size = MAXINT; /* should be 0 */
400*2264Sjacobs 
401*2264Sjacobs 	/* request data file transfer, read ack/nack */
402*2264Sjacobs 	errno = ENOSPC;
403*2264Sjacobs 	if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0)
404*2264Sjacobs 		return (-1);
405*2264Sjacobs 
406*2264Sjacobs 	if (fd != -1) {
407*2264Sjacobs 		/* write the data */
408*2264Sjacobs 		if (sendfile(sock, fd, &off, st.st_size) != st.st_size)
409*2264Sjacobs 			return (-1);
410*2264Sjacobs 		close(fd);
411*2264Sjacobs 
412*2264Sjacobs 		/* request ack/nack after the data transfer */
413*2264Sjacobs 		errno = EIO;
414*2264Sjacobs 		if (send_lpd_message(sock, "") < 0)
415*2264Sjacobs 			return (-1);
416*2264Sjacobs 	}
417*2264Sjacobs 
418*2264Sjacobs 	return (0);
419*2264Sjacobs }
420*2264Sjacobs 
421*2264Sjacobs static int
422*2264Sjacobs send_control_file(int sock, char *data, int id)
423*2264Sjacobs {
424*2264Sjacobs 	int len;
425*2264Sjacobs 	char buf[BUFSIZ];
426*2264Sjacobs 	char *host = "localhost";
427*2264Sjacobs 
428*2264Sjacobs 	len = strlen(data);
429*2264Sjacobs 
430*2264Sjacobs 	/* request data file transfer, read ack/nack */
431*2264Sjacobs 	errno = ENOSPC;
432*2264Sjacobs 	if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0)
433*2264Sjacobs 		return (-1);
434*2264Sjacobs 
435*2264Sjacobs 	/* write the data */
436*2264Sjacobs 	if (write(sock, data, len) != len)
437*2264Sjacobs 		return (-1);
438*2264Sjacobs 
439*2264Sjacobs 	/* request ack/nack after the data transfer */
440*2264Sjacobs 	errno = EIO;
441*2264Sjacobs 	if (send_lpd_message(sock, "") < 0)
442*2264Sjacobs 		return (-1);
443*2264Sjacobs 
444*2264Sjacobs 	return (0);
445*2264Sjacobs }
446*2264Sjacobs 
447*2264Sjacobs 
448*2264Sjacobs static int
449*2264Sjacobs submit_job(int sock, uri_t *uri, int job_id, char *path)
450*2264Sjacobs {
451*2264Sjacobs 	int current = 0;
452*2264Sjacobs 	off_t off = 0;
453*2264Sjacobs 	char *metadata = NULL;
454*2264Sjacobs 	char *ptr, *iter = NULL;
455*2264Sjacobs 	int fd, err;
456*2264Sjacobs 	int sent_files = 0;
457*2264Sjacobs 	char buf[BUFSIZ];
458*2264Sjacobs 	size_t len;
459*2264Sjacobs 	char *printer = queue_name_from_uri(uri);
460*2264Sjacobs 
461*2264Sjacobs 	/* read in the control file */
462*2264Sjacobs 	if ((fd = open(path, O_RDONLY)) >= 0) {
463*2264Sjacobs 		struct stat st;
464*2264Sjacobs 
465*2264Sjacobs 		if (fstat(fd, &st) < 0) {
466*2264Sjacobs 			close(fd);
467*2264Sjacobs 			return (-1);
468*2264Sjacobs 		}
469*2264Sjacobs 
470*2264Sjacobs 		metadata = alloca(st.st_size + 1);
471*2264Sjacobs 		memset(metadata, 0, st.st_size + 1);
472*2264Sjacobs 
473*2264Sjacobs 		if (read(fd, metadata, st.st_size) != st.st_size) {
474*2264Sjacobs 			close(fd);
475*2264Sjacobs 			free(metadata);
476*2264Sjacobs 			metadata = NULL;
477*2264Sjacobs 			return (-1);
478*2264Sjacobs 		}
479*2264Sjacobs 
480*2264Sjacobs 	} else {
481*2264Sjacobs 		syslog(LOG_ERR,
482*2264Sjacobs 			"lpd-port:submit_job:open failed : %m path %s", path);
483*2264Sjacobs 		return (-1);
484*2264Sjacobs 	}
485*2264Sjacobs 
486*2264Sjacobs 	/* massage the control file */
487*2264Sjacobs 	if (massage_control_data(metadata, job_id) < 0) {
488*2264Sjacobs 		/* bad control data, dump the job */
489*2264Sjacobs 		syslog(LOG_ALERT,
490*2264Sjacobs 			"bad control file, possible subversion attempt");
491*2264Sjacobs 	}
492*2264Sjacobs 
493*2264Sjacobs 	/* request to transfer the job */
494*2264Sjacobs 	if (send_lpd_message(sock, "\002%s\n", printer) < 0) {
495*2264Sjacobs 		/* no such (or disabled) queue, got to love rfc-1179 */
496*2264Sjacobs 		errno = ENOENT;
497*2264Sjacobs 		return (-1);
498*2264Sjacobs 	}
499*2264Sjacobs 
500*2264Sjacobs 	/* send the control data */
501*2264Sjacobs 	if (send_control_file(sock, metadata, job_id) < 0) {
502*2264Sjacobs 		err = errno;
503*2264Sjacobs 		write(sock, "\001\n", 2); /* abort */
504*2264Sjacobs 		errno = err;
505*2264Sjacobs 		return (-1);
506*2264Sjacobs 	}
507*2264Sjacobs 
508*2264Sjacobs 	/* walk the control file sending the data files */
509*2264Sjacobs 	for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL;
510*2264Sjacobs 			ptr = strtok_r(NULL, "\n", &iter)) {
511*2264Sjacobs 		char *name = NULL;
512*2264Sjacobs 
513*2264Sjacobs 		if (ptr[0] != 'U')
514*2264Sjacobs 			continue;
515*2264Sjacobs 
516*2264Sjacobs 		name = strtok_r(NULL, "\n", &iter);
517*2264Sjacobs 		if (name[0] != 'N')
518*2264Sjacobs 			continue;
519*2264Sjacobs 
520*2264Sjacobs 		ptr++;
521*2264Sjacobs 		name++;
522*2264Sjacobs 
523*2264Sjacobs 		if (send_data_file(sock, ptr, name) < 0) {
524*2264Sjacobs 			err = errno;
525*2264Sjacobs 			write(sock, "\001\n", 2); /* abort */
526*2264Sjacobs 			errno = err;
527*2264Sjacobs 			return (-1);
528*2264Sjacobs 		}
529*2264Sjacobs 		if (strcmp(name, "standard input") != 0)
530*2264Sjacobs 			sent_files++;
531*2264Sjacobs 	}
532*2264Sjacobs 
533*2264Sjacobs 	/* write back the job-id */
534*2264Sjacobs 	err = errno;
535*2264Sjacobs 	if ((fd = open(path, O_WRONLY)) >= 0) {
536*2264Sjacobs 		ftruncate(fd, 0);
537*2264Sjacobs 		write(fd, &job_id, sizeof (job_id));
538*2264Sjacobs 		close(fd);
539*2264Sjacobs 	}
540*2264Sjacobs 	errno = err;
541*2264Sjacobs 
542*2264Sjacobs 	if (sent_files != 0) {
543*2264Sjacobs 		err = errno;
544*2264Sjacobs 		close(sock);
545*2264Sjacobs 		errno = err;
546*2264Sjacobs 	}
547*2264Sjacobs 
548*2264Sjacobs 	return (0);
549*2264Sjacobs }
550*2264Sjacobs 
551*2264Sjacobs static int
552*2264Sjacobs query(int fd, uri_t *uri, int ac, char **av)
553*2264Sjacobs {
554*2264Sjacobs 	char buf[BUFSIZ];
555*2264Sjacobs 	int rc, len;
556*2264Sjacobs 	char *printer = queue_name_from_uri(uri);
557*2264Sjacobs 
558*2264Sjacobs 	/* build the request */
559*2264Sjacobs 	snprintf(buf, sizeof (buf), "\04%s", printer);
560*2264Sjacobs 	add_args(ac, av, buf, sizeof (buf));
561*2264Sjacobs 	strlcat(buf, "\n", sizeof (buf));
562*2264Sjacobs 	len = strlen(buf);
563*2264Sjacobs 
564*2264Sjacobs 	if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) {
565*2264Sjacobs 		errno = EMSGSIZE;
566*2264Sjacobs 		rc = -1;
567*2264Sjacobs 	} else
568*2264Sjacobs 		rc = 0;
569*2264Sjacobs 
570*2264Sjacobs 	return (rc);
571*2264Sjacobs }
572*2264Sjacobs 
573*2264Sjacobs static int
574*2264Sjacobs cancel(int fd, uri_t *uri, int ac, char **av)
575*2264Sjacobs {
576*2264Sjacobs 	char buf[BUFSIZ];
577*2264Sjacobs 	int rc, len;
578*2264Sjacobs 	char *printer = queue_name_from_uri(uri);
579*2264Sjacobs 
580*2264Sjacobs 	/* build the request */
581*2264Sjacobs 	snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name());
582*2264Sjacobs 	add_args(ac, av, buf, sizeof (buf));
583*2264Sjacobs 	strlcat(buf, "\n", sizeof (buf));
584*2264Sjacobs 	len = strlen(buf);
585*2264Sjacobs 
586*2264Sjacobs 	if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) {
587*2264Sjacobs 		errno = EMSGSIZE;
588*2264Sjacobs 		rc = -1;
589*2264Sjacobs 	} else
590*2264Sjacobs 		rc = 0;
591*2264Sjacobs 
592*2264Sjacobs 	return (rc);
593*2264Sjacobs }
594*2264Sjacobs 
595*2264Sjacobs static void
596*2264Sjacobs usage(char *program)
597*2264Sjacobs {
598*2264Sjacobs 	char *name;
599*2264Sjacobs 
600*2264Sjacobs 	setreuid(getuid(), getuid());
601*2264Sjacobs 
602*2264Sjacobs 	if ((name = strrchr(program, '/')) == NULL)
603*2264Sjacobs 		name = program;
604*2264Sjacobs 	else
605*2264Sjacobs 		name++;
606*2264Sjacobs 
607*2264Sjacobs 	fprintf(stderr, "usage:\t%s -u uri [-t timeout] "
608*2264Sjacobs 			"[-s control ]\n", name);
609*2264Sjacobs 	fprintf(stderr, "\t%s -u uri [-t timeout] "
610*2264Sjacobs 			"[-c user|job ...]\n", name);
611*2264Sjacobs 	fprintf(stderr, "\t%s -u uri [-t timeout] "
612*2264Sjacobs 			"[-q user|job ...]\n", name);
613*2264Sjacobs 	exit(EINVAL);
614*2264Sjacobs }
615*2264Sjacobs 
616*2264Sjacobs /*
617*2264Sjacobs  * The main program temporarily loses privilege while searching the command
618*2264Sjacobs  * line arguments.  It then allocates any resources it need privilege for
619*2264Sjacobs  * job-id, reserved port.  Once it has the resources it needs, it perminently
620*2264Sjacobs  * drops all elevated privilege.  It ghen connects to the remote print service
621*2264Sjacobs  * based on destination hostname.  Doing it this way reduces the potenential
622*2264Sjacobs  * opportunity for a breakout with elevated privilege, breakout with an
623*2264Sjacobs  * unconnected reserved port, and exploitation of the remote print service
624*2264Sjacobs  * by a calling program.
625*2264Sjacobs  */
626*2264Sjacobs int
627*2264Sjacobs main(int ac, char *av[])
628*2264Sjacobs {
629*2264Sjacobs 	enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE;
630*2264Sjacobs 	int fd, c, timeout = 0, exit_code = 0;
631*2264Sjacobs 	uri_t *uri = NULL;
632*2264Sjacobs 	uid_t uid = getuid();
633*2264Sjacobs #ifdef	PRIV_ALLSETS
634*2264Sjacobs 	priv_set_t *saveset = NULL;
635*2264Sjacobs #endif
636*2264Sjacobs 
637*2264Sjacobs 	openlog("lpd-port", LOG_PID, LOG_LPR);
638*2264Sjacobs 
639*2264Sjacobs #ifdef	PRIV_ALLSETS
640*2264Sjacobs 
641*2264Sjacobs 	/* lose as much as we can perminently and temporarily drop the rest. */
642*2264Sjacobs 
643*2264Sjacobs 	if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR,"
644*2264Sjacobs 			"PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,",
645*2264Sjacobs 			",", (const char **)NULL)) == NULL) {
646*2264Sjacobs 		syslog(LOG_ERR,
647*2264Sjacobs 		    "lpd_port: priv_str_to_set saveset failed: %m\n");
648*2264Sjacobs 		return (-1);
649*2264Sjacobs 	}
650*2264Sjacobs 
651*2264Sjacobs 	if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) {
652*2264Sjacobs 		syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m");
653*2264Sjacobs 		return (-1);
654*2264Sjacobs 	}
655*2264Sjacobs 
656*2264Sjacobs 	/*
657*2264Sjacobs 	 * These privileges permanently dropped in next_job_id() and
658*2264Sjacobs 	 * reserved_port()
659*2264Sjacobs 	 */
660*2264Sjacobs 
661*2264Sjacobs 	if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) {
662*2264Sjacobs 		syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m");
663*2264Sjacobs 		return (-1);
664*2264Sjacobs 	}
665*2264Sjacobs 
666*2264Sjacobs 	priv_freeset(saveset);
667*2264Sjacobs 
668*2264Sjacobs 	syslog(LOG_DEBUG, "using privs");
669*2264Sjacobs #else
670*2264Sjacobs 
671*2264Sjacobs 	syslog(LOG_DEBUG, "no  privs");
672*2264Sjacobs 	seteuid(uid);
673*2264Sjacobs #endif
674*2264Sjacobs 
675*2264Sjacobs 
676*2264Sjacobs 	while ((c = getopt(ac, av, "cqst:u:")) != EOF) {
677*2264Sjacobs 		switch (c) {
678*2264Sjacobs 		case 'c':
679*2264Sjacobs 			if (operation != OP_NONE)
680*2264Sjacobs 				usage(av[0]);
681*2264Sjacobs 			operation = OP_CANCEL;
682*2264Sjacobs 			break;
683*2264Sjacobs 		case 'q':
684*2264Sjacobs 			if (operation != OP_NONE)
685*2264Sjacobs 				usage(av[0]);
686*2264Sjacobs 			operation = OP_QUERY;
687*2264Sjacobs 			break;
688*2264Sjacobs 		case 's':
689*2264Sjacobs 			if (operation != OP_NONE)
690*2264Sjacobs 				usage(av[0]);
691*2264Sjacobs 			operation = OP_SUBMIT;
692*2264Sjacobs 			break;
693*2264Sjacobs 		case 't':
694*2264Sjacobs 			timeout = atoi(optarg);
695*2264Sjacobs 			break;
696*2264Sjacobs 		case 'u':
697*2264Sjacobs 			if (uri_from_string(optarg, &uri) < 0)
698*2264Sjacobs 				usage(av[0]);
699*2264Sjacobs 			break;
700*2264Sjacobs 		default:
701*2264Sjacobs 			usage(av[0]);
702*2264Sjacobs 			/* does not return */
703*2264Sjacobs 		}
704*2264Sjacobs 	}
705*2264Sjacobs 
706*2264Sjacobs 	if ((uri == NULL) || (timeout < 0) || (operation == OP_NONE))
707*2264Sjacobs 		usage(av[0]);
708*2264Sjacobs 
709*2264Sjacobs 	if ((strcasecmp(uri->scheme, "lpd") != 0) &&
710*2264Sjacobs 	    (strcasecmp(uri->scheme, "rfc-1179") != 0))
711*2264Sjacobs 		usage(av[0]);
712*2264Sjacobs 
713*2264Sjacobs 	if (operation == OP_SUBMIT)	/* get a job-id if we need it */
714*2264Sjacobs 		if ((c = next_job_id()) < 0) {
715*2264Sjacobs 			syslog(LOG_ERR, "lpd_port:main:next_job_id fails");
716*2264Sjacobs 			return (-1);
717*2264Sjacobs 		}
718*2264Sjacobs 
719*2264Sjacobs 	if ((fd = reserved_port()) < 0) {
720*2264Sjacobs 		syslog(LOG_ERR, "reserved_port() failed %m");
721*2264Sjacobs 		return (errno);
722*2264Sjacobs 	}
723*2264Sjacobs 
724*2264Sjacobs 	/*
725*2264Sjacobs 	 * we no longer want or need any elevated privilege, lose it all
726*2264Sjacobs 	 * permanently.
727*2264Sjacobs 	 */
728*2264Sjacobs 
729*2264Sjacobs 	setreuid(uid, uid);
730*2264Sjacobs 
731*2264Sjacobs 
732*2264Sjacobs 	/* connect to the print service */
733*2264Sjacobs 	if ((fd = sock_connect(fd, uri, timeout)) < 0)
734*2264Sjacobs 		return (errno);
735*2264Sjacobs 
736*2264Sjacobs 	/* perform the requested operation */
737*2264Sjacobs 	switch (operation) {
738*2264Sjacobs 	case OP_SUBMIT:	/* transfer the job, close the fd */
739*2264Sjacobs 		if (submit_job(fd, uri, c, av[optind]) < 0)
740*2264Sjacobs 			exit_code = errno;
741*2264Sjacobs 		break;
742*2264Sjacobs 	case OP_QUERY:	/* send the query string, return the fd */
743*2264Sjacobs 		if (query(fd, uri, ac - optind, &av[optind]) < 0)
744*2264Sjacobs 			exit_code = errno;
745*2264Sjacobs 		break;
746*2264Sjacobs 	case OP_CANCEL:	/* send the cancel string, return the fd */
747*2264Sjacobs 		if (cancel(fd, uri, ac - optind, &av[optind]) < 0)
748*2264Sjacobs 			exit_code = errno;
749*2264Sjacobs 		break;
750*2264Sjacobs 	default:	/* This should never happen */
751*2264Sjacobs 		exit_code = EINVAL;
752*2264Sjacobs 	}
753*2264Sjacobs 
754*2264Sjacobs 
755*2264Sjacobs 	/* if the operation succeeded, send the fd to our parent */
756*2264Sjacobs 	if ((exit_code == 0) && (sendfd(1, fd) < 0)) {
757*2264Sjacobs 		char buf[BUFSIZ];
758*2264Sjacobs 
759*2264Sjacobs 		exit_code = errno;
760*2264Sjacobs 
761*2264Sjacobs 		/* sendfd() failed, dump the socket data for the heck of it */
762*2264Sjacobs 		while ((c = read(fd, buf, sizeof (buf))) > 0)
763*2264Sjacobs 			write(1, buf, c);
764*2264Sjacobs 	}
765*2264Sjacobs 
766*2264Sjacobs 	syslog(LOG_DEBUG, "exit code: %d", exit_code);
767*2264Sjacobs 	return (exit_code);
768*2264Sjacobs }
769