12264Sjacobs /* 22264Sjacobs * CDDL HEADER START 32264Sjacobs * 42264Sjacobs * The contents of this file are subject to the terms of the 52264Sjacobs * Common Development and Distribution License (the "License"). 62264Sjacobs * You may not use this file except in compliance with the License. 72264Sjacobs * 82264Sjacobs * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92264Sjacobs * or http://www.opensolaris.org/os/licensing. 102264Sjacobs * See the License for the specific language governing permissions 112264Sjacobs * and limitations under the License. 122264Sjacobs * 132264Sjacobs * When distributing Covered Code, include this CDDL HEADER in each 142264Sjacobs * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152264Sjacobs * If applicable, add the following below this CDDL HEADER, with the 162264Sjacobs * fields enclosed by brackets "[]" replaced with your own identifying 172264Sjacobs * information: Portions Copyright [yyyy] [name of copyright owner] 182264Sjacobs * 192264Sjacobs * CDDL HEADER END 202264Sjacobs */ 212264Sjacobs 222264Sjacobs /* 23*7253Sjacobs * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 242264Sjacobs * Use is subject to license terms. 252264Sjacobs * 262264Sjacobs */ 272264Sjacobs 282264Sjacobs /* $Id: lpd-port.c 155 2006-04-26 02:34:54Z ktou $ */ 292264Sjacobs 302264Sjacobs #pragma ident "%Z%%M% %I% %E% SMI" 312264Sjacobs 322264Sjacobs #include <config-site.h> 332264Sjacobs 342264Sjacobs #include <stdio.h> 352264Sjacobs #include <stdlib.h> 362264Sjacobs #include <unistd.h> 372264Sjacobs #include <sys/types.h> 382264Sjacobs #include <sys/stat.h> 392264Sjacobs #include <fcntl.h> 402264Sjacobs #include <stdarg.h> 412264Sjacobs #include <string.h> 422264Sjacobs #include <signal.h> 432264Sjacobs #include <sys/socket.h> 442264Sjacobs #include <netinet/in.h> 452264Sjacobs #include <arpa/inet.h> 462264Sjacobs #include <netdb.h> 472264Sjacobs #include <errno.h> 482264Sjacobs #include <syslog.h> 492264Sjacobs #include <values.h> 502264Sjacobs #include <stropts.h> /* for sendfd */ 512264Sjacobs #include <sys/uio.h> /* for sendmsg stuff */ 522264Sjacobs #include <pwd.h> 532264Sjacobs #include <sys/sendfile.h> 542264Sjacobs #include <ctype.h> 552264Sjacobs #ifdef HAVE_PRIV_H 562264Sjacobs #include <priv.h> 572264Sjacobs #endif 582264Sjacobs 592264Sjacobs #ifndef JOB_ID_FILE 602264Sjacobs #define JOB_ID_FILE "/var/run/rfc-1179.seq" 612264Sjacobs #endif /* JOB_ID_FILE */ 622264Sjacobs 632264Sjacobs static int 642264Sjacobs sendfd(int sockfd, int fd) 652264Sjacobs { 662264Sjacobs syslog(LOG_DEBUG, "sendfd(%d, %d)", sockfd, fd); 672264Sjacobs 682264Sjacobs #if defined(sun) && defined(unix) && defined(I_SENDFD) 692264Sjacobs return (ioctl(sockfd, I_SENDFD, fd)); 702264Sjacobs #else 712264Sjacobs struct iovec iov[1]; 722264Sjacobs struct msghdr msg; 732264Sjacobs #ifdef CMSG_DATA 742264Sjacobs struct cmsghdr cmp[1]; 752264Sjacobs char buf[2]; /* send/recv 2 byte protocol */ 762264Sjacobs 772264Sjacobs iov[0].iov_base = buf; 782264Sjacobs iov[0].iov_len = 2; 792264Sjacobs 802264Sjacobs cmp[0].cmsg_level = SOL_SOCKET; 812264Sjacobs cmp[0].cmsg_type = SCM_RIGHTS; 822264Sjacobs cmp[0].cmsg_len = sizeof (struct cmsghdr) + sizeof (int); 832264Sjacobs * (int *)CMSG_DATA(cmp) = fd; 842264Sjacobs 852264Sjacobs buf[1] = 0; 862264Sjacobs buf[0] = 0; 872264Sjacobs msg.msg_control = cmp; 882264Sjacobs msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int); 892264Sjacobs #else 902264Sjacobs iov[0].iov_base = NULL; 912264Sjacobs iov[0].iov_len = 0; 922264Sjacobs msg.msg_accrights = (caddr_t)&fd; 932264Sjacobs msg.msg_accrights = sizeof (fd); 942264Sjacobs #endif 952264Sjacobs msg.msg_iov = iov; 962264Sjacobs msg.msg_iovlen = 1; 972264Sjacobs msg.msg_name = NULL; 982264Sjacobs msg.msg_namelen = 0; 992264Sjacobs 1002264Sjacobs return (sendmsg(sockfd, &msg, 0)); 1012264Sjacobs #endif 1022264Sjacobs } 1032264Sjacobs 1042264Sjacobs static void 1052264Sjacobs null(int i) 1062264Sjacobs { 1072264Sjacobs } 1082264Sjacobs 1092264Sjacobs static int 1104275Sjacobs sock_connect(int sock, char *host, int timeout) 1112264Sjacobs { 1122264Sjacobs struct hostent *hp; 1132264Sjacobs struct servent *sp; 1142264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 1152264Sjacobs struct sockaddr_in6 sin; 1162264Sjacobs #else 1172264Sjacobs struct sockaddr_in sin; 1182264Sjacobs #endif 1192264Sjacobs static void (*old_handler)(); 1202264Sjacobs int err, 1212264Sjacobs error_num; 1222264Sjacobs unsigned timo = 1; 1232264Sjacobs 1242264Sjacobs /* 1252264Sjacobs * Get the host address and port number to connect to. 1262264Sjacobs */ 1274275Sjacobs if (host == NULL) { 1282264Sjacobs return (-1); 1292264Sjacobs } 1302264Sjacobs 1312264Sjacobs /* linux style NULL usage */ 1322264Sjacobs (void) memset((char *)&sin, (int)NULL, sizeof (sin)); 1332264Sjacobs 1342264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 1354275Sjacobs if ((hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, 1362264Sjacobs &error_num)) == NULL) { 1372264Sjacobs errno = ENOENT; 1382264Sjacobs return (-1); 1392264Sjacobs } 1402264Sjacobs (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); 1412264Sjacobs sin.sin6_family = hp->h_addrtype; 1422264Sjacobs #else 1434275Sjacobs if ((hp = gethostbyname(host)) == NULL) { 1442264Sjacobs errno = ENOENT; 1452264Sjacobs return (-1); 1462264Sjacobs } 1472264Sjacobs 1482264Sjacobs (void) memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length); 1492264Sjacobs sin.sin_family = hp->h_addrtype; 1502264Sjacobs #endif 1512264Sjacobs 1522264Sjacobs if ((sp = getservbyname("printer", "tcp")) == NULL) { 1532264Sjacobs errno = ENOENT; 1542264Sjacobs return (-1); 1552264Sjacobs } 1562264Sjacobs 1572264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 1584275Sjacobs sin.sin6_port = sp->s_port; 1592264Sjacobs #else 1604275Sjacobs sin.sin_port = sp->s_port; 1612264Sjacobs #endif 1622264Sjacobs 1632264Sjacobs retry: 1642264Sjacobs old_handler = signal(SIGALRM, null); 1652264Sjacobs (void) alarm(timeout); 1662264Sjacobs 1672264Sjacobs if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 1682264Sjacobs (void) alarm(0); 1692264Sjacobs (void) signal(SIGALRM, old_handler); 1702264Sjacobs 1712264Sjacobs if (errno == ECONNREFUSED && timo <= 16) { 1722264Sjacobs (void) sleep(timo); 1732264Sjacobs timo *= 2; 1742264Sjacobs goto retry; 1752264Sjacobs } 1762264Sjacobs 1772264Sjacobs return (-1); 1782264Sjacobs } 1792264Sjacobs 1802264Sjacobs (void) alarm(0); 1812264Sjacobs (void) signal(SIGALRM, old_handler); 1822264Sjacobs return (sock); 1832264Sjacobs } 1842264Sjacobs 1852264Sjacobs static int 1862264Sjacobs next_job_id() 1872264Sjacobs { 1882264Sjacobs int fd, result = getpid() % 1000; 1892264Sjacobs 1902264Sjacobs /* gain back enough privilege to open the id file */ 1912264Sjacobs #ifdef PRIV_ALLSETS 1922264Sjacobs if ((priv_set(PRIV_ON, PRIV_EFFECTIVE, 1932264Sjacobs PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL)) < 0) { 1942264Sjacobs syslog(LOG_ERR, "lpd_port:next_job_id:priv_set fails: : %m"); 1952264Sjacobs return (-1); 1962264Sjacobs } 1972264Sjacobs #else 1982264Sjacobs seteuid(0); 1992264Sjacobs #endif 2002264Sjacobs 2012264Sjacobs /* open the sequence file */ 2022264Sjacobs if (((fd = open(JOB_ID_FILE, O_RDWR)) < 0) && (errno == ENOENT)) 2032264Sjacobs fd = open(JOB_ID_FILE, O_CREAT|O_EXCL|O_RDWR, 0644); 2042264Sjacobs 2052264Sjacobs syslog(LOG_DEBUG, "sequence file fd: %d", fd); 2062264Sjacobs 2072264Sjacobs /* drop our privilege again */ 2082264Sjacobs #ifdef PRIV_ALLSETS 2092264Sjacobs /* drop file access privilege */ 2102264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, 2112264Sjacobs PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL); 2122264Sjacobs #else 2132264Sjacobs seteuid(getuid()); 2142264Sjacobs #endif 2152264Sjacobs 2162264Sjacobs if (fd >= 0) { 2172264Sjacobs /* wait for a lock on the file */ 2182264Sjacobs if (lockf(fd, F_LOCK, 0) == 0) { 2192264Sjacobs char buf[8]; 2202264Sjacobs int next; 2212264Sjacobs 2222264Sjacobs /* get the current id */ 2232264Sjacobs (void) memset(buf, 0, sizeof (buf)); 2242264Sjacobs if (read(fd, buf, sizeof (buf)) > 0) 2252264Sjacobs result = atoi(buf); 2262264Sjacobs 2272264Sjacobs next = ((result < 999) ? (result + 1) : 0); 2282264Sjacobs 2292264Sjacobs /* store the next id in the file */ 2302264Sjacobs snprintf(buf, sizeof (buf), "%.3d", next); 2312264Sjacobs if ((lseek(fd, 0, SEEK_SET) == 0) && 2322264Sjacobs (ftruncate(fd, 0) == 0)) 2332264Sjacobs write(fd, buf, strlen(buf)); 2342264Sjacobs } 235*7253Sjacobs close(fd); 2362264Sjacobs } 2372264Sjacobs syslog(LOG_DEBUG, "next_job_id() is %d", result); 2382264Sjacobs 2392264Sjacobs return (result); 2402264Sjacobs } 2412264Sjacobs 2422264Sjacobs static int 2432264Sjacobs reserved_port() 2442264Sjacobs { 2452264Sjacobs int result = -1; 2462264Sjacobs int port; 2472264Sjacobs 2482264Sjacobs /* gain back enough privilege to open a reserved port */ 2492264Sjacobs #ifdef PRIV_ALLSETS 2502264Sjacobs if ((priv_set( 2512264Sjacobs PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 2522264Sjacobs syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 2532264Sjacobs return (-1); 2542264Sjacobs } 2552264Sjacobs #else 2562264Sjacobs seteuid(0); 2572264Sjacobs #endif 2582264Sjacobs 2592264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 2602264Sjacobs port = 0; /* set to 0, rresvport_af() will find us one. */ 2612264Sjacobs result = rresvport_af(&port, AF_INET6); 2622264Sjacobs #else 2632264Sjacobs port = IPPORT_RESERVED - 1; 2642264Sjacobs while (((result = rresvport(&port)) < 0) && (port >= 0)) 2652264Sjacobs port--; 2662264Sjacobs #endif 2672264Sjacobs 2682264Sjacobs /* drop our privilege again */ 2692264Sjacobs #ifdef PRIV_ALLSETS 2702264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 2712264Sjacobs #else 2722264Sjacobs seteuid(getuid()); 2732264Sjacobs #endif 2742264Sjacobs 2752264Sjacobs return (result); 2762264Sjacobs } 2772264Sjacobs 2782264Sjacobs static char * 2792264Sjacobs get_user_name() 2802264Sjacobs { 2812264Sjacobs static struct passwd *p = NULL; 2822264Sjacobs 2832264Sjacobs if ((p = getpwuid(getuid())) != NULL) 2842264Sjacobs return (p->pw_name); 2852264Sjacobs else 2862264Sjacobs return ("unknown"); 2872264Sjacobs } 2882264Sjacobs 2892264Sjacobs static void 2902264Sjacobs add_args(int ac, char **av, char *buf, size_t len) 2912264Sjacobs { 2922264Sjacobs while (ac--) { 2932264Sjacobs strlcat(buf, " ", len); 2942264Sjacobs strlcat(buf, *(av++), len); 2952264Sjacobs } 2962264Sjacobs } 2972264Sjacobs 2982264Sjacobs static int 2992264Sjacobs massage_control_data(char *data, int id) 3002264Sjacobs { 3012264Sjacobs char *line, *iter = NULL; 3022264Sjacobs char *ptr; 3032264Sjacobs char host[BUFSIZ]; 3042264Sjacobs 3052264Sjacobs gethostname(host, sizeof (host)); 3062264Sjacobs 3072264Sjacobs for (ptr = strchr(data, '\n'); ptr != NULL; ptr = strchr(ptr, '\n')) { 3082264Sjacobs ptr++; 3092264Sjacobs 3102264Sjacobs if (ptr[0] == 'H') { 3112264Sjacobs if (strncmp(++ptr, host, strlen(host)) != 0) 3122264Sjacobs return (-1); 3132264Sjacobs } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 3142264Sjacobs /* check the user name */ 3152264Sjacobs uid_t uid = getuid(); 3162264Sjacobs struct passwd *pw; 3172264Sjacobs int len; 3182264Sjacobs 3192264Sjacobs if (uid == 0) /* let root do what they want */ 3202264Sjacobs continue; 3212264Sjacobs if ((pw = getpwuid(uid)) == NULL) 3222264Sjacobs return (-1); /* failed */ 3232264Sjacobs len = strlen(pw->pw_name); 3242264Sjacobs if ((strncmp(++ptr, pw->pw_name, len) != 0) || 3252264Sjacobs (ptr[len] != '\n')) 3262264Sjacobs return (-1); /* failed */ 3272264Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 3282264Sjacobs /* check/fix df?XXXhostname */ 3292264Sjacobs ptr++; 3302264Sjacobs 3312264Sjacobs if (strlen(ptr) < 6) 3322264Sjacobs return (-1); 3332264Sjacobs if ((ptr[0] == 'd') && (ptr[1] == 'f') && 3342264Sjacobs (ptr[3] == 'X') && (ptr[4] == 'X') && 3352264Sjacobs (ptr[5] == 'X')) { 3362264Sjacobs ptr[3] = '0' + (id / 100) % 10; 3372264Sjacobs ptr[4] = '0' + (id / 10) % 10; 3382264Sjacobs ptr[5] = '0' + id % 10; 3392264Sjacobs 3402264Sjacobs if (strncmp(&ptr[6], host, strlen(host)) != 0) 3412264Sjacobs return (-1); 3422264Sjacobs } else 3432264Sjacobs return (-1); 3442264Sjacobs } 3452264Sjacobs } 3462264Sjacobs return (1); 3472264Sjacobs } 3482264Sjacobs 3492264Sjacobs static int 3502264Sjacobs send_lpd_message(int fd, char *fmt, ...) 3512264Sjacobs { 3522264Sjacobs char buf[BUFSIZ]; 3532264Sjacobs size_t size; 3542264Sjacobs va_list ap; 3552264Sjacobs 3562264Sjacobs va_start(ap, fmt); 3572264Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 3582264Sjacobs va_end(ap); 3592264Sjacobs if (size == 0) 3602264Sjacobs size = 1; 3612264Sjacobs 3622264Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 3632264Sjacobs 3642264Sjacobs if (write(fd, buf, size) != size) { 3652264Sjacobs errno = EIO; 3662264Sjacobs return (-1); 3672264Sjacobs } 3682264Sjacobs 3692264Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 3702264Sjacobs return (-1); 3712264Sjacobs 3722264Sjacobs return (0); 3732264Sjacobs } 3742264Sjacobs 3752264Sjacobs static int 3762264Sjacobs send_data_file(int sock, char *dfname, char *name) 3772264Sjacobs { 3782264Sjacobs size_t len; 3792264Sjacobs off_t off = 0; 3802264Sjacobs struct stat st; 3812264Sjacobs char buf[32]; 3822264Sjacobs int fd = -1; 3832264Sjacobs 3842264Sjacobs if (strcmp(name, "standard input") != 0) { 3852264Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 3862264Sjacobs return (-1); 3872264Sjacobs 3882264Sjacobs if (fstat(fd, &st) < 0) 3892264Sjacobs return (-1); 3902264Sjacobs } else 3912264Sjacobs st.st_size = MAXINT; /* should be 0 */ 3922264Sjacobs 3932264Sjacobs /* request data file transfer, read ack/nack */ 3942264Sjacobs errno = ENOSPC; 3952264Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 3962264Sjacobs return (-1); 3972264Sjacobs 3982264Sjacobs if (fd != -1) { 3992264Sjacobs /* write the data */ 4002264Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 4012264Sjacobs return (-1); 4022264Sjacobs close(fd); 4032264Sjacobs 4042264Sjacobs /* request ack/nack after the data transfer */ 4052264Sjacobs errno = EIO; 4062264Sjacobs if (send_lpd_message(sock, "") < 0) 4072264Sjacobs return (-1); 4082264Sjacobs } 4092264Sjacobs 4102264Sjacobs return (0); 4112264Sjacobs } 4122264Sjacobs 4132264Sjacobs static int 4142264Sjacobs send_control_file(int sock, char *data, int id) 4152264Sjacobs { 4162264Sjacobs int len; 4172264Sjacobs char buf[BUFSIZ]; 4182264Sjacobs char *host = "localhost"; 4192264Sjacobs 4202264Sjacobs len = strlen(data); 4212264Sjacobs 4222264Sjacobs /* request data file transfer, read ack/nack */ 4232264Sjacobs errno = ENOSPC; 4242264Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 4252264Sjacobs return (-1); 4262264Sjacobs 4272264Sjacobs /* write the data */ 4282264Sjacobs if (write(sock, data, len) != len) 4292264Sjacobs return (-1); 4302264Sjacobs 4312264Sjacobs /* request ack/nack after the data transfer */ 4322264Sjacobs errno = EIO; 4332264Sjacobs if (send_lpd_message(sock, "") < 0) 4342264Sjacobs return (-1); 4352264Sjacobs 4362264Sjacobs return (0); 4372264Sjacobs } 4382264Sjacobs 4392264Sjacobs 4402264Sjacobs static int 4414275Sjacobs submit_job(int sock, char *printer, int job_id, char *path) 4422264Sjacobs { 4434275Sjacobs struct stat st; 4442264Sjacobs int current = 0; 4452264Sjacobs off_t off = 0; 4462264Sjacobs char *metadata = NULL; 4472264Sjacobs char *ptr, *iter = NULL; 4482264Sjacobs int fd, err; 4492264Sjacobs int sent_files = 0; 4502264Sjacobs char buf[BUFSIZ]; 4512264Sjacobs size_t len; 4524275Sjacobs 4534275Sjacobs /* open the control file */ 4544275Sjacobs if ((fd = open(path, O_RDONLY)) < 0) { 4554275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 4564275Sjacobs sock, printer, job_id, path); 4574275Sjacobs return (-1); 4584275Sjacobs } 4594275Sjacobs 4604275Sjacobs /* get the size of the control file */ 4614275Sjacobs if (fstat(fd, &st) < 0) { 4624275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 4634275Sjacobs sock, printer, job_id, path); 4644275Sjacobs close(fd); 4654275Sjacobs return (-1); 4664275Sjacobs } 4674275Sjacobs 4684275Sjacobs /* allocate memory for the control file */ 4694275Sjacobs if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 4704275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 4714275Sjacobs sock, printer, job_id, path); 4724275Sjacobs close(fd); 4734275Sjacobs return (-1); 4744275Sjacobs } 4752264Sjacobs 4762264Sjacobs /* read in the control file */ 4774275Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 4784275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 4794275Sjacobs sock, printer, job_id, path); 4804275Sjacobs free(metadata); 4814275Sjacobs close(fd); 4822264Sjacobs return (-1); 4832264Sjacobs } 4842264Sjacobs 4852264Sjacobs /* massage the control file */ 4862264Sjacobs if (massage_control_data(metadata, job_id) < 0) { 4872264Sjacobs /* bad control data, dump the job */ 4882264Sjacobs syslog(LOG_ALERT, 4892264Sjacobs "bad control file, possible subversion attempt"); 4904275Sjacobs free(metadata); 4914275Sjacobs close(fd); 4924275Sjacobs return (-1); 4932264Sjacobs } 4942264Sjacobs 4952264Sjacobs /* request to transfer the job */ 4962264Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 4972264Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 4982264Sjacobs errno = ENOENT; 4992264Sjacobs return (-1); 5002264Sjacobs } 5012264Sjacobs 5022264Sjacobs /* send the control data */ 5032264Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 5042264Sjacobs err = errno; 5052264Sjacobs write(sock, "\001\n", 2); /* abort */ 5062264Sjacobs errno = err; 5072264Sjacobs return (-1); 5082264Sjacobs } 5092264Sjacobs 5102264Sjacobs /* walk the control file sending the data files */ 5112264Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 5122264Sjacobs ptr = strtok_r(NULL, "\n", &iter)) { 5132264Sjacobs char *name = NULL; 5142264Sjacobs 5152264Sjacobs if (ptr[0] != 'U') 5162264Sjacobs continue; 5172264Sjacobs 5182264Sjacobs name = strtok_r(NULL, "\n", &iter); 5192264Sjacobs if (name[0] != 'N') 5202264Sjacobs continue; 5212264Sjacobs 5222264Sjacobs ptr++; 5232264Sjacobs name++; 5242264Sjacobs 5252264Sjacobs if (send_data_file(sock, ptr, name) < 0) { 5262264Sjacobs err = errno; 5272264Sjacobs write(sock, "\001\n", 2); /* abort */ 5282264Sjacobs errno = err; 5292264Sjacobs return (-1); 5302264Sjacobs } 5312264Sjacobs if (strcmp(name, "standard input") != 0) 5322264Sjacobs sent_files++; 5332264Sjacobs } 5342264Sjacobs 5352264Sjacobs /* write back the job-id */ 5362264Sjacobs err = errno; 5372264Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 5382264Sjacobs ftruncate(fd, 0); 5392264Sjacobs write(fd, &job_id, sizeof (job_id)); 5402264Sjacobs close(fd); 5412264Sjacobs } 5422264Sjacobs errno = err; 5432264Sjacobs 5442264Sjacobs if (sent_files != 0) { 5452264Sjacobs err = errno; 5462264Sjacobs close(sock); 5472264Sjacobs errno = err; 5482264Sjacobs } 5492264Sjacobs 5502264Sjacobs return (0); 5512264Sjacobs } 5522264Sjacobs static int 5534275Sjacobs query(int fd, char *printer, int ac, char **av) 5542264Sjacobs { 5552264Sjacobs char buf[BUFSIZ]; 5562264Sjacobs int rc, len; 5572264Sjacobs 5582264Sjacobs /* build the request */ 5592264Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 5602264Sjacobs add_args(ac, av, buf, sizeof (buf)); 5612264Sjacobs strlcat(buf, "\n", sizeof (buf)); 5622264Sjacobs len = strlen(buf); 5632264Sjacobs 5642264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 5652264Sjacobs errno = EMSGSIZE; 5662264Sjacobs rc = -1; 5672264Sjacobs } else 5682264Sjacobs rc = 0; 5692264Sjacobs 5702264Sjacobs return (rc); 5712264Sjacobs } 5722264Sjacobs 5732264Sjacobs static int 5744275Sjacobs cancel(int fd, char *printer, int ac, char **av) 5752264Sjacobs { 5762264Sjacobs char buf[BUFSIZ]; 5772264Sjacobs int rc, len; 5782264Sjacobs 5792264Sjacobs /* build the request */ 5802264Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 5812264Sjacobs add_args(ac, av, buf, sizeof (buf)); 5822264Sjacobs strlcat(buf, "\n", sizeof (buf)); 5832264Sjacobs len = strlen(buf); 5842264Sjacobs 5852264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 5862264Sjacobs errno = EMSGSIZE; 5872264Sjacobs rc = -1; 5882264Sjacobs } else 5892264Sjacobs rc = 0; 5902264Sjacobs 5912264Sjacobs return (rc); 5922264Sjacobs } 5932264Sjacobs 5942264Sjacobs static void 5952264Sjacobs usage(char *program) 5962264Sjacobs { 5972264Sjacobs char *name; 5982264Sjacobs 5992264Sjacobs setreuid(getuid(), getuid()); 6002264Sjacobs 6012264Sjacobs if ((name = strrchr(program, '/')) == NULL) 6022264Sjacobs name = program; 6032264Sjacobs else 6042264Sjacobs name++; 6052264Sjacobs 6064275Sjacobs fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 6074275Sjacobs name); 6084275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 6094275Sjacobs name); 6104275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 6114275Sjacobs name); 6122264Sjacobs exit(EINVAL); 6132264Sjacobs } 6142264Sjacobs 6152264Sjacobs /* 6162264Sjacobs * The main program temporarily loses privilege while searching the command 6172264Sjacobs * line arguments. It then allocates any resources it need privilege for 6182264Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 6192264Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 6202264Sjacobs * based on destination hostname. Doing it this way reduces the potenential 6212264Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 6222264Sjacobs * unconnected reserved port, and exploitation of the remote print service 6232264Sjacobs * by a calling program. 6242264Sjacobs */ 6252264Sjacobs int 6262264Sjacobs main(int ac, char *av[]) 6272264Sjacobs { 6282264Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 6292264Sjacobs int fd, c, timeout = 0, exit_code = 0; 6304275Sjacobs char *host = NULL, *queue = NULL; 6312264Sjacobs uid_t uid = getuid(); 6322264Sjacobs #ifdef PRIV_ALLSETS 6332264Sjacobs priv_set_t *saveset = NULL; 6342264Sjacobs #endif 6352264Sjacobs 6362264Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 6372264Sjacobs 6382264Sjacobs #ifdef PRIV_ALLSETS 6392264Sjacobs 6402264Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 6412264Sjacobs 6422264Sjacobs if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 6432264Sjacobs "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 6442264Sjacobs ",", (const char **)NULL)) == NULL) { 6452264Sjacobs syslog(LOG_ERR, 6462264Sjacobs "lpd_port: priv_str_to_set saveset failed: %m\n"); 6472264Sjacobs return (-1); 6482264Sjacobs } 6492264Sjacobs 6502264Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 6512264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 6522264Sjacobs return (-1); 6532264Sjacobs } 6542264Sjacobs 6552264Sjacobs /* 6562264Sjacobs * These privileges permanently dropped in next_job_id() and 6572264Sjacobs * reserved_port() 6582264Sjacobs */ 6592264Sjacobs 6602264Sjacobs if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 6612264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 6622264Sjacobs return (-1); 6632264Sjacobs } 6642264Sjacobs 6652264Sjacobs priv_freeset(saveset); 6662264Sjacobs 6672264Sjacobs syslog(LOG_DEBUG, "using privs"); 6682264Sjacobs #else 6692264Sjacobs 6702264Sjacobs syslog(LOG_DEBUG, "no privs"); 6712264Sjacobs seteuid(uid); 6722264Sjacobs #endif 6732264Sjacobs 6744275Sjacobs while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 6752264Sjacobs switch (c) { 6764275Sjacobs case 'H': 6774275Sjacobs host = optarg; 6784275Sjacobs break; 6794275Sjacobs case 't': 6804275Sjacobs timeout = atoi(optarg); 6814275Sjacobs break; 6822264Sjacobs case 'c': 6832264Sjacobs if (operation != OP_NONE) 6842264Sjacobs usage(av[0]); 6852264Sjacobs operation = OP_CANCEL; 6864275Sjacobs queue = optarg; 6872264Sjacobs break; 6882264Sjacobs case 'q': 6892264Sjacobs if (operation != OP_NONE) 6902264Sjacobs usage(av[0]); 6912264Sjacobs operation = OP_QUERY; 6924275Sjacobs queue = optarg; 6932264Sjacobs break; 6942264Sjacobs case 's': 6952264Sjacobs if (operation != OP_NONE) 6962264Sjacobs usage(av[0]); 6972264Sjacobs operation = OP_SUBMIT; 6984275Sjacobs queue = optarg; 6992264Sjacobs break; 7002264Sjacobs default: 7012264Sjacobs usage(av[0]); 7022264Sjacobs /* does not return */ 7032264Sjacobs } 7042264Sjacobs } 7052264Sjacobs 7064275Sjacobs if ((host == NULL) || (queue == NULL) || (timeout < 0) || 7074275Sjacobs (operation == OP_NONE)) 7082264Sjacobs usage(av[0]); 7092264Sjacobs 7102264Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 7112264Sjacobs if ((c = next_job_id()) < 0) { 7122264Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 7132264Sjacobs return (-1); 7142264Sjacobs } 7152264Sjacobs 7162264Sjacobs if ((fd = reserved_port()) < 0) { 7172264Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 7182264Sjacobs return (errno); 7192264Sjacobs } 7202264Sjacobs 7212264Sjacobs /* 7222264Sjacobs * we no longer want or need any elevated privilege, lose it all 7232264Sjacobs * permanently. 7242264Sjacobs */ 7252264Sjacobs 7262264Sjacobs setreuid(uid, uid); 7272264Sjacobs 7282264Sjacobs /* connect to the print service */ 7294275Sjacobs if ((fd = sock_connect(fd, host, timeout)) < 0) 7302264Sjacobs return (errno); 7312264Sjacobs 7322264Sjacobs /* perform the requested operation */ 7332264Sjacobs switch (operation) { 7342264Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 7354275Sjacobs if (submit_job(fd, queue, c, av[optind]) < 0) 7362264Sjacobs exit_code = errno; 7372264Sjacobs break; 7382264Sjacobs case OP_QUERY: /* send the query string, return the fd */ 7394275Sjacobs if (query(fd, queue, ac - optind, &av[optind]) < 0) 7402264Sjacobs exit_code = errno; 7412264Sjacobs break; 7422264Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 7434275Sjacobs if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 7442264Sjacobs exit_code = errno; 7452264Sjacobs break; 7462264Sjacobs default: /* This should never happen */ 7472264Sjacobs exit_code = EINVAL; 7482264Sjacobs } 7492264Sjacobs 7502264Sjacobs 7512264Sjacobs /* if the operation succeeded, send the fd to our parent */ 7522264Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 7532264Sjacobs char buf[BUFSIZ]; 7542264Sjacobs 7552264Sjacobs exit_code = errno; 7562264Sjacobs 7572264Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 7582264Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 7592264Sjacobs write(1, buf, c); 7602264Sjacobs } 7612264Sjacobs 7622264Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 7632264Sjacobs return (exit_code); 7642264Sjacobs } 765