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*4275Sjacobs * Copyright 2007 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 110*4275Sjacobs 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 */ 127*4275Sjacobs 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) 135*4275Sjacobs 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 143*4275Sjacobs 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) 158*4275Sjacobs sin.sin6_port = sp->s_port; 1592264Sjacobs #else 160*4275Sjacobs 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 } 2352264Sjacobs } 2362264Sjacobs syslog(LOG_DEBUG, "next_job_id() is %d", result); 2372264Sjacobs 2382264Sjacobs return (result); 2392264Sjacobs } 2402264Sjacobs 2412264Sjacobs static int 2422264Sjacobs reserved_port() 2432264Sjacobs { 2442264Sjacobs int result = -1; 2452264Sjacobs int port; 2462264Sjacobs 2472264Sjacobs /* gain back enough privilege to open a reserved port */ 2482264Sjacobs #ifdef PRIV_ALLSETS 2492264Sjacobs if ((priv_set( 2502264Sjacobs PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 2512264Sjacobs syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 2522264Sjacobs return (-1); 2532264Sjacobs } 2542264Sjacobs #else 2552264Sjacobs seteuid(0); 2562264Sjacobs #endif 2572264Sjacobs 2582264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 2592264Sjacobs port = 0; /* set to 0, rresvport_af() will find us one. */ 2602264Sjacobs result = rresvport_af(&port, AF_INET6); 2612264Sjacobs #else 2622264Sjacobs port = IPPORT_RESERVED - 1; 2632264Sjacobs while (((result = rresvport(&port)) < 0) && (port >= 0)) 2642264Sjacobs port--; 2652264Sjacobs #endif 2662264Sjacobs 2672264Sjacobs /* drop our privilege again */ 2682264Sjacobs #ifdef PRIV_ALLSETS 2692264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 2702264Sjacobs #else 2712264Sjacobs seteuid(getuid()); 2722264Sjacobs #endif 2732264Sjacobs 2742264Sjacobs return (result); 2752264Sjacobs } 2762264Sjacobs 2772264Sjacobs static char * 2782264Sjacobs get_user_name() 2792264Sjacobs { 2802264Sjacobs static struct passwd *p = NULL; 2812264Sjacobs 2822264Sjacobs if ((p = getpwuid(getuid())) != NULL) 2832264Sjacobs return (p->pw_name); 2842264Sjacobs else 2852264Sjacobs return ("unknown"); 2862264Sjacobs } 2872264Sjacobs 2882264Sjacobs static void 2892264Sjacobs add_args(int ac, char **av, char *buf, size_t len) 2902264Sjacobs { 2912264Sjacobs while (ac--) { 2922264Sjacobs strlcat(buf, " ", len); 2932264Sjacobs strlcat(buf, *(av++), len); 2942264Sjacobs } 2952264Sjacobs } 2962264Sjacobs 2972264Sjacobs static int 2982264Sjacobs massage_control_data(char *data, int id) 2992264Sjacobs { 3002264Sjacobs char *line, *iter = NULL; 3012264Sjacobs char *ptr; 3022264Sjacobs char host[BUFSIZ]; 3032264Sjacobs 3042264Sjacobs gethostname(host, sizeof (host)); 3052264Sjacobs 3062264Sjacobs for (ptr = strchr(data, '\n'); ptr != NULL; ptr = strchr(ptr, '\n')) { 3072264Sjacobs ptr++; 3082264Sjacobs 3092264Sjacobs if (ptr[0] == 'H') { 3102264Sjacobs if (strncmp(++ptr, host, strlen(host)) != 0) 3112264Sjacobs return (-1); 3122264Sjacobs } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 3132264Sjacobs /* check the user name */ 3142264Sjacobs uid_t uid = getuid(); 3152264Sjacobs struct passwd *pw; 3162264Sjacobs int len; 3172264Sjacobs 3182264Sjacobs if (uid == 0) /* let root do what they want */ 3192264Sjacobs continue; 3202264Sjacobs if ((pw = getpwuid(uid)) == NULL) 3212264Sjacobs return (-1); /* failed */ 3222264Sjacobs len = strlen(pw->pw_name); 3232264Sjacobs if ((strncmp(++ptr, pw->pw_name, len) != 0) || 3242264Sjacobs (ptr[len] != '\n')) 3252264Sjacobs return (-1); /* failed */ 3262264Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 3272264Sjacobs /* check/fix df?XXXhostname */ 3282264Sjacobs ptr++; 3292264Sjacobs 3302264Sjacobs if (strlen(ptr) < 6) 3312264Sjacobs return (-1); 3322264Sjacobs if ((ptr[0] == 'd') && (ptr[1] == 'f') && 3332264Sjacobs (ptr[3] == 'X') && (ptr[4] == 'X') && 3342264Sjacobs (ptr[5] == 'X')) { 3352264Sjacobs ptr[3] = '0' + (id / 100) % 10; 3362264Sjacobs ptr[4] = '0' + (id / 10) % 10; 3372264Sjacobs ptr[5] = '0' + id % 10; 3382264Sjacobs 3392264Sjacobs if (strncmp(&ptr[6], host, strlen(host)) != 0) 3402264Sjacobs return (-1); 3412264Sjacobs } else 3422264Sjacobs return (-1); 3432264Sjacobs } 3442264Sjacobs } 3452264Sjacobs return (1); 3462264Sjacobs } 3472264Sjacobs 3482264Sjacobs static int 3492264Sjacobs send_lpd_message(int fd, char *fmt, ...) 3502264Sjacobs { 3512264Sjacobs char buf[BUFSIZ]; 3522264Sjacobs size_t size; 3532264Sjacobs va_list ap; 3542264Sjacobs 3552264Sjacobs va_start(ap, fmt); 3562264Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 3572264Sjacobs va_end(ap); 3582264Sjacobs if (size == 0) 3592264Sjacobs size = 1; 3602264Sjacobs 3612264Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 3622264Sjacobs 3632264Sjacobs if (write(fd, buf, size) != size) { 3642264Sjacobs errno = EIO; 3652264Sjacobs return (-1); 3662264Sjacobs } 3672264Sjacobs 3682264Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 3692264Sjacobs return (-1); 3702264Sjacobs 3712264Sjacobs return (0); 3722264Sjacobs } 3732264Sjacobs 3742264Sjacobs static int 3752264Sjacobs send_data_file(int sock, char *dfname, char *name) 3762264Sjacobs { 3772264Sjacobs size_t len; 3782264Sjacobs off_t off = 0; 3792264Sjacobs struct stat st; 3802264Sjacobs char buf[32]; 3812264Sjacobs int fd = -1; 3822264Sjacobs 3832264Sjacobs if (strcmp(name, "standard input") != 0) { 3842264Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 3852264Sjacobs return (-1); 3862264Sjacobs 3872264Sjacobs if (fstat(fd, &st) < 0) 3882264Sjacobs return (-1); 3892264Sjacobs } else 3902264Sjacobs st.st_size = MAXINT; /* should be 0 */ 3912264Sjacobs 3922264Sjacobs /* request data file transfer, read ack/nack */ 3932264Sjacobs errno = ENOSPC; 3942264Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 3952264Sjacobs return (-1); 3962264Sjacobs 3972264Sjacobs if (fd != -1) { 3982264Sjacobs /* write the data */ 3992264Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 4002264Sjacobs return (-1); 4012264Sjacobs close(fd); 4022264Sjacobs 4032264Sjacobs /* request ack/nack after the data transfer */ 4042264Sjacobs errno = EIO; 4052264Sjacobs if (send_lpd_message(sock, "") < 0) 4062264Sjacobs return (-1); 4072264Sjacobs } 4082264Sjacobs 4092264Sjacobs return (0); 4102264Sjacobs } 4112264Sjacobs 4122264Sjacobs static int 4132264Sjacobs send_control_file(int sock, char *data, int id) 4142264Sjacobs { 4152264Sjacobs int len; 4162264Sjacobs char buf[BUFSIZ]; 4172264Sjacobs char *host = "localhost"; 4182264Sjacobs 4192264Sjacobs len = strlen(data); 4202264Sjacobs 4212264Sjacobs /* request data file transfer, read ack/nack */ 4222264Sjacobs errno = ENOSPC; 4232264Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 4242264Sjacobs return (-1); 4252264Sjacobs 4262264Sjacobs /* write the data */ 4272264Sjacobs if (write(sock, data, len) != len) 4282264Sjacobs return (-1); 4292264Sjacobs 4302264Sjacobs /* request ack/nack after the data transfer */ 4312264Sjacobs errno = EIO; 4322264Sjacobs if (send_lpd_message(sock, "") < 0) 4332264Sjacobs return (-1); 4342264Sjacobs 4352264Sjacobs return (0); 4362264Sjacobs } 4372264Sjacobs 4382264Sjacobs 4392264Sjacobs static int 440*4275Sjacobs submit_job(int sock, char *printer, int job_id, char *path) 4412264Sjacobs { 442*4275Sjacobs struct stat st; 4432264Sjacobs int current = 0; 4442264Sjacobs off_t off = 0; 4452264Sjacobs char *metadata = NULL; 4462264Sjacobs char *ptr, *iter = NULL; 4472264Sjacobs int fd, err; 4482264Sjacobs int sent_files = 0; 4492264Sjacobs char buf[BUFSIZ]; 4502264Sjacobs size_t len; 451*4275Sjacobs 452*4275Sjacobs /* open the control file */ 453*4275Sjacobs if ((fd = open(path, O_RDONLY)) < 0) { 454*4275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 455*4275Sjacobs sock, printer, job_id, path); 456*4275Sjacobs return (-1); 457*4275Sjacobs } 458*4275Sjacobs 459*4275Sjacobs /* get the size of the control file */ 460*4275Sjacobs if (fstat(fd, &st) < 0) { 461*4275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 462*4275Sjacobs sock, printer, job_id, path); 463*4275Sjacobs close(fd); 464*4275Sjacobs return (-1); 465*4275Sjacobs } 466*4275Sjacobs 467*4275Sjacobs /* allocate memory for the control file */ 468*4275Sjacobs if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 469*4275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 470*4275Sjacobs sock, printer, job_id, path); 471*4275Sjacobs close(fd); 472*4275Sjacobs return (-1); 473*4275Sjacobs } 4742264Sjacobs 4752264Sjacobs /* read in the control file */ 476*4275Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 477*4275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 478*4275Sjacobs sock, printer, job_id, path); 479*4275Sjacobs free(metadata); 480*4275Sjacobs close(fd); 4812264Sjacobs return (-1); 4822264Sjacobs } 4832264Sjacobs 4842264Sjacobs /* massage the control file */ 4852264Sjacobs if (massage_control_data(metadata, job_id) < 0) { 4862264Sjacobs /* bad control data, dump the job */ 4872264Sjacobs syslog(LOG_ALERT, 4882264Sjacobs "bad control file, possible subversion attempt"); 489*4275Sjacobs free(metadata); 490*4275Sjacobs close(fd); 491*4275Sjacobs return (-1); 4922264Sjacobs } 4932264Sjacobs 4942264Sjacobs /* request to transfer the job */ 4952264Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 4962264Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 4972264Sjacobs errno = ENOENT; 4982264Sjacobs return (-1); 4992264Sjacobs } 5002264Sjacobs 5012264Sjacobs /* send the control data */ 5022264Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 5032264Sjacobs err = errno; 5042264Sjacobs write(sock, "\001\n", 2); /* abort */ 5052264Sjacobs errno = err; 5062264Sjacobs return (-1); 5072264Sjacobs } 5082264Sjacobs 5092264Sjacobs /* walk the control file sending the data files */ 5102264Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 5112264Sjacobs ptr = strtok_r(NULL, "\n", &iter)) { 5122264Sjacobs char *name = NULL; 5132264Sjacobs 5142264Sjacobs if (ptr[0] != 'U') 5152264Sjacobs continue; 5162264Sjacobs 5172264Sjacobs name = strtok_r(NULL, "\n", &iter); 5182264Sjacobs if (name[0] != 'N') 5192264Sjacobs continue; 5202264Sjacobs 5212264Sjacobs ptr++; 5222264Sjacobs name++; 5232264Sjacobs 5242264Sjacobs if (send_data_file(sock, ptr, name) < 0) { 5252264Sjacobs err = errno; 5262264Sjacobs write(sock, "\001\n", 2); /* abort */ 5272264Sjacobs errno = err; 5282264Sjacobs return (-1); 5292264Sjacobs } 5302264Sjacobs if (strcmp(name, "standard input") != 0) 5312264Sjacobs sent_files++; 5322264Sjacobs } 5332264Sjacobs 5342264Sjacobs /* write back the job-id */ 5352264Sjacobs err = errno; 5362264Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 5372264Sjacobs ftruncate(fd, 0); 5382264Sjacobs write(fd, &job_id, sizeof (job_id)); 5392264Sjacobs close(fd); 5402264Sjacobs } 5412264Sjacobs errno = err; 5422264Sjacobs 5432264Sjacobs if (sent_files != 0) { 5442264Sjacobs err = errno; 5452264Sjacobs close(sock); 5462264Sjacobs errno = err; 5472264Sjacobs } 5482264Sjacobs 5492264Sjacobs return (0); 5502264Sjacobs } 5512264Sjacobs static int 552*4275Sjacobs query(int fd, char *printer, int ac, char **av) 5532264Sjacobs { 5542264Sjacobs char buf[BUFSIZ]; 5552264Sjacobs int rc, len; 5562264Sjacobs 5572264Sjacobs /* build the request */ 5582264Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 5592264Sjacobs add_args(ac, av, buf, sizeof (buf)); 5602264Sjacobs strlcat(buf, "\n", sizeof (buf)); 5612264Sjacobs len = strlen(buf); 5622264Sjacobs 5632264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 5642264Sjacobs errno = EMSGSIZE; 5652264Sjacobs rc = -1; 5662264Sjacobs } else 5672264Sjacobs rc = 0; 5682264Sjacobs 5692264Sjacobs return (rc); 5702264Sjacobs } 5712264Sjacobs 5722264Sjacobs static int 573*4275Sjacobs cancel(int fd, char *printer, int ac, char **av) 5742264Sjacobs { 5752264Sjacobs char buf[BUFSIZ]; 5762264Sjacobs int rc, len; 5772264Sjacobs 5782264Sjacobs /* build the request */ 5792264Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 5802264Sjacobs add_args(ac, av, buf, sizeof (buf)); 5812264Sjacobs strlcat(buf, "\n", sizeof (buf)); 5822264Sjacobs len = strlen(buf); 5832264Sjacobs 5842264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 5852264Sjacobs errno = EMSGSIZE; 5862264Sjacobs rc = -1; 5872264Sjacobs } else 5882264Sjacobs rc = 0; 5892264Sjacobs 5902264Sjacobs return (rc); 5912264Sjacobs } 5922264Sjacobs 5932264Sjacobs static void 5942264Sjacobs usage(char *program) 5952264Sjacobs { 5962264Sjacobs char *name; 5972264Sjacobs 5982264Sjacobs setreuid(getuid(), getuid()); 5992264Sjacobs 6002264Sjacobs if ((name = strrchr(program, '/')) == NULL) 6012264Sjacobs name = program; 6022264Sjacobs else 6032264Sjacobs name++; 6042264Sjacobs 605*4275Sjacobs fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 606*4275Sjacobs name); 607*4275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 608*4275Sjacobs name); 609*4275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 610*4275Sjacobs name); 6112264Sjacobs exit(EINVAL); 6122264Sjacobs } 6132264Sjacobs 6142264Sjacobs /* 6152264Sjacobs * The main program temporarily loses privilege while searching the command 6162264Sjacobs * line arguments. It then allocates any resources it need privilege for 6172264Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 6182264Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 6192264Sjacobs * based on destination hostname. Doing it this way reduces the potenential 6202264Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 6212264Sjacobs * unconnected reserved port, and exploitation of the remote print service 6222264Sjacobs * by a calling program. 6232264Sjacobs */ 6242264Sjacobs int 6252264Sjacobs main(int ac, char *av[]) 6262264Sjacobs { 6272264Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 6282264Sjacobs int fd, c, timeout = 0, exit_code = 0; 629*4275Sjacobs char *host = NULL, *queue = NULL; 6302264Sjacobs uid_t uid = getuid(); 6312264Sjacobs #ifdef PRIV_ALLSETS 6322264Sjacobs priv_set_t *saveset = NULL; 6332264Sjacobs #endif 6342264Sjacobs 6352264Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 6362264Sjacobs 6372264Sjacobs #ifdef PRIV_ALLSETS 6382264Sjacobs 6392264Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 6402264Sjacobs 6412264Sjacobs if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 6422264Sjacobs "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 6432264Sjacobs ",", (const char **)NULL)) == NULL) { 6442264Sjacobs syslog(LOG_ERR, 6452264Sjacobs "lpd_port: priv_str_to_set saveset failed: %m\n"); 6462264Sjacobs return (-1); 6472264Sjacobs } 6482264Sjacobs 6492264Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 6502264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 6512264Sjacobs return (-1); 6522264Sjacobs } 6532264Sjacobs 6542264Sjacobs /* 6552264Sjacobs * These privileges permanently dropped in next_job_id() and 6562264Sjacobs * reserved_port() 6572264Sjacobs */ 6582264Sjacobs 6592264Sjacobs if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 6602264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 6612264Sjacobs return (-1); 6622264Sjacobs } 6632264Sjacobs 6642264Sjacobs priv_freeset(saveset); 6652264Sjacobs 6662264Sjacobs syslog(LOG_DEBUG, "using privs"); 6672264Sjacobs #else 6682264Sjacobs 6692264Sjacobs syslog(LOG_DEBUG, "no privs"); 6702264Sjacobs seteuid(uid); 6712264Sjacobs #endif 6722264Sjacobs 673*4275Sjacobs while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 6742264Sjacobs switch (c) { 675*4275Sjacobs case 'H': 676*4275Sjacobs host = optarg; 677*4275Sjacobs break; 678*4275Sjacobs case 't': 679*4275Sjacobs timeout = atoi(optarg); 680*4275Sjacobs break; 6812264Sjacobs case 'c': 6822264Sjacobs if (operation != OP_NONE) 6832264Sjacobs usage(av[0]); 6842264Sjacobs operation = OP_CANCEL; 685*4275Sjacobs queue = optarg; 6862264Sjacobs break; 6872264Sjacobs case 'q': 6882264Sjacobs if (operation != OP_NONE) 6892264Sjacobs usage(av[0]); 6902264Sjacobs operation = OP_QUERY; 691*4275Sjacobs queue = optarg; 6922264Sjacobs break; 6932264Sjacobs case 's': 6942264Sjacobs if (operation != OP_NONE) 6952264Sjacobs usage(av[0]); 6962264Sjacobs operation = OP_SUBMIT; 697*4275Sjacobs queue = optarg; 6982264Sjacobs break; 6992264Sjacobs default: 7002264Sjacobs usage(av[0]); 7012264Sjacobs /* does not return */ 7022264Sjacobs } 7032264Sjacobs } 7042264Sjacobs 705*4275Sjacobs if ((host == NULL) || (queue == NULL) || (timeout < 0) || 706*4275Sjacobs (operation == OP_NONE)) 7072264Sjacobs usage(av[0]); 7082264Sjacobs 7092264Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 7102264Sjacobs if ((c = next_job_id()) < 0) { 7112264Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 7122264Sjacobs return (-1); 7132264Sjacobs } 7142264Sjacobs 7152264Sjacobs if ((fd = reserved_port()) < 0) { 7162264Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 7172264Sjacobs return (errno); 7182264Sjacobs } 7192264Sjacobs 7202264Sjacobs /* 7212264Sjacobs * we no longer want or need any elevated privilege, lose it all 7222264Sjacobs * permanently. 7232264Sjacobs */ 7242264Sjacobs 7252264Sjacobs setreuid(uid, uid); 7262264Sjacobs 7272264Sjacobs /* connect to the print service */ 728*4275Sjacobs if ((fd = sock_connect(fd, host, timeout)) < 0) 7292264Sjacobs return (errno); 7302264Sjacobs 7312264Sjacobs /* perform the requested operation */ 7322264Sjacobs switch (operation) { 7332264Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 734*4275Sjacobs if (submit_job(fd, queue, c, av[optind]) < 0) 7352264Sjacobs exit_code = errno; 7362264Sjacobs break; 7372264Sjacobs case OP_QUERY: /* send the query string, return the fd */ 738*4275Sjacobs if (query(fd, queue, ac - optind, &av[optind]) < 0) 7392264Sjacobs exit_code = errno; 7402264Sjacobs break; 7412264Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 742*4275Sjacobs if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 7432264Sjacobs exit_code = errno; 7442264Sjacobs break; 7452264Sjacobs default: /* This should never happen */ 7462264Sjacobs exit_code = EINVAL; 7472264Sjacobs } 7482264Sjacobs 7492264Sjacobs 7502264Sjacobs /* if the operation succeeded, send the fd to our parent */ 7512264Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 7522264Sjacobs char buf[BUFSIZ]; 7532264Sjacobs 7542264Sjacobs exit_code = errno; 7552264Sjacobs 7562264Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 7572264Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 7582264Sjacobs write(1, buf, c); 7592264Sjacobs } 7602264Sjacobs 7612264Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 7622264Sjacobs return (exit_code); 7632264Sjacobs } 764