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 /* 238521SJonathan.Ca@Sun.COM * Copyright 2009 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 #include <config-site.h> 312264Sjacobs 322264Sjacobs #include <stdio.h> 332264Sjacobs #include <stdlib.h> 342264Sjacobs #include <unistd.h> 352264Sjacobs #include <sys/types.h> 362264Sjacobs #include <sys/stat.h> 372264Sjacobs #include <fcntl.h> 382264Sjacobs #include <stdarg.h> 392264Sjacobs #include <string.h> 402264Sjacobs #include <signal.h> 412264Sjacobs #include <sys/socket.h> 422264Sjacobs #include <netinet/in.h> 432264Sjacobs #include <arpa/inet.h> 442264Sjacobs #include <netdb.h> 452264Sjacobs #include <errno.h> 462264Sjacobs #include <syslog.h> 472264Sjacobs #include <values.h> 482264Sjacobs #include <stropts.h> /* for sendfd */ 492264Sjacobs #include <sys/uio.h> /* for sendmsg stuff */ 502264Sjacobs #include <pwd.h> 512264Sjacobs #include <sys/sendfile.h> 522264Sjacobs #include <ctype.h> 532264Sjacobs #ifdef HAVE_PRIV_H 542264Sjacobs #include <priv.h> 552264Sjacobs #endif 562264Sjacobs 572264Sjacobs #ifndef JOB_ID_FILE 582264Sjacobs #define JOB_ID_FILE "/var/run/rfc-1179.seq" 592264Sjacobs #endif /* JOB_ID_FILE */ 602264Sjacobs 612264Sjacobs static int 622264Sjacobs sendfd(int sockfd, int fd) 632264Sjacobs { 642264Sjacobs syslog(LOG_DEBUG, "sendfd(%d, %d)", sockfd, fd); 652264Sjacobs 662264Sjacobs #if defined(sun) && defined(unix) && defined(I_SENDFD) 672264Sjacobs return (ioctl(sockfd, I_SENDFD, fd)); 682264Sjacobs #else 692264Sjacobs struct iovec iov[1]; 702264Sjacobs struct msghdr msg; 712264Sjacobs #ifdef CMSG_DATA 722264Sjacobs struct cmsghdr cmp[1]; 732264Sjacobs char buf[2]; /* send/recv 2 byte protocol */ 742264Sjacobs 752264Sjacobs iov[0].iov_base = buf; 762264Sjacobs iov[0].iov_len = 2; 772264Sjacobs 782264Sjacobs cmp[0].cmsg_level = SOL_SOCKET; 792264Sjacobs cmp[0].cmsg_type = SCM_RIGHTS; 802264Sjacobs cmp[0].cmsg_len = sizeof (struct cmsghdr) + sizeof (int); 812264Sjacobs * (int *)CMSG_DATA(cmp) = fd; 822264Sjacobs 832264Sjacobs buf[1] = 0; 842264Sjacobs buf[0] = 0; 852264Sjacobs msg.msg_control = cmp; 862264Sjacobs msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int); 872264Sjacobs #else 882264Sjacobs iov[0].iov_base = NULL; 892264Sjacobs iov[0].iov_len = 0; 902264Sjacobs msg.msg_accrights = (caddr_t)&fd; 912264Sjacobs msg.msg_accrights = sizeof (fd); 922264Sjacobs #endif 932264Sjacobs msg.msg_iov = iov; 942264Sjacobs msg.msg_iovlen = 1; 952264Sjacobs msg.msg_name = NULL; 962264Sjacobs msg.msg_namelen = 0; 972264Sjacobs 982264Sjacobs return (sendmsg(sockfd, &msg, 0)); 992264Sjacobs #endif 1002264Sjacobs } 1012264Sjacobs 1022264Sjacobs static void 1032264Sjacobs null(int i) 1042264Sjacobs { 1052264Sjacobs } 1062264Sjacobs 1072264Sjacobs static int 1084275Sjacobs sock_connect(int sock, char *host, int timeout) 1092264Sjacobs { 1102264Sjacobs struct hostent *hp; 1112264Sjacobs struct servent *sp; 1122264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 1132264Sjacobs struct sockaddr_in6 sin; 1142264Sjacobs #else 1152264Sjacobs struct sockaddr_in sin; 1162264Sjacobs #endif 1172264Sjacobs static void (*old_handler)(); 1188521SJonathan.Ca@Sun.COM int err, error_num; 1192264Sjacobs unsigned timo = 1; 1202264Sjacobs 1212264Sjacobs /* 1222264Sjacobs * Get the host address and port number to connect to. 1232264Sjacobs */ 1244275Sjacobs if (host == NULL) { 1252264Sjacobs return (-1); 1262264Sjacobs } 1272264Sjacobs 1282264Sjacobs /* linux style NULL usage */ 1292264Sjacobs (void) memset((char *)&sin, (int)NULL, sizeof (sin)); 1302264Sjacobs 1312264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 1324275Sjacobs if ((hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, 1338521SJonathan.Ca@Sun.COM &error_num)) == NULL) { 1342264Sjacobs errno = ENOENT; 1352264Sjacobs return (-1); 1362264Sjacobs } 1372264Sjacobs (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); 1382264Sjacobs sin.sin6_family = hp->h_addrtype; 1392264Sjacobs #else 1404275Sjacobs if ((hp = gethostbyname(host)) == NULL) { 1412264Sjacobs errno = ENOENT; 1422264Sjacobs return (-1); 1432264Sjacobs } 1442264Sjacobs 1452264Sjacobs (void) memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length); 1462264Sjacobs sin.sin_family = hp->h_addrtype; 1472264Sjacobs #endif 1482264Sjacobs 1492264Sjacobs if ((sp = getservbyname("printer", "tcp")) == NULL) { 1502264Sjacobs errno = ENOENT; 1512264Sjacobs return (-1); 1522264Sjacobs } 1532264Sjacobs 1542264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 1554275Sjacobs sin.sin6_port = sp->s_port; 1562264Sjacobs #else 1574275Sjacobs sin.sin_port = sp->s_port; 1582264Sjacobs #endif 1592264Sjacobs 1602264Sjacobs retry: 1612264Sjacobs old_handler = signal(SIGALRM, null); 1622264Sjacobs (void) alarm(timeout); 1632264Sjacobs 1642264Sjacobs if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 1652264Sjacobs (void) alarm(0); 1662264Sjacobs (void) signal(SIGALRM, old_handler); 1672264Sjacobs 1682264Sjacobs if (errno == ECONNREFUSED && timo <= 16) { 1692264Sjacobs (void) sleep(timo); 1702264Sjacobs timo *= 2; 1712264Sjacobs goto retry; 1722264Sjacobs } 1732264Sjacobs 1742264Sjacobs return (-1); 1752264Sjacobs } 1762264Sjacobs 1772264Sjacobs (void) alarm(0); 1782264Sjacobs (void) signal(SIGALRM, old_handler); 1792264Sjacobs return (sock); 1802264Sjacobs } 1812264Sjacobs 1822264Sjacobs static int 1832264Sjacobs next_job_id() 1842264Sjacobs { 1852264Sjacobs int fd, result = getpid() % 1000; 1862264Sjacobs 1872264Sjacobs /* gain back enough privilege to open the id file */ 1882264Sjacobs #ifdef PRIV_ALLSETS 1892264Sjacobs if ((priv_set(PRIV_ON, PRIV_EFFECTIVE, 1908521SJonathan.Ca@Sun.COM PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL)) < 0) { 1912264Sjacobs syslog(LOG_ERR, "lpd_port:next_job_id:priv_set fails: : %m"); 1922264Sjacobs return (-1); 1932264Sjacobs } 1942264Sjacobs #else 1952264Sjacobs seteuid(0); 1962264Sjacobs #endif 1972264Sjacobs 1982264Sjacobs /* open the sequence file */ 1992264Sjacobs if (((fd = open(JOB_ID_FILE, O_RDWR)) < 0) && (errno == ENOENT)) 2002264Sjacobs fd = open(JOB_ID_FILE, O_CREAT|O_EXCL|O_RDWR, 0644); 2012264Sjacobs 2022264Sjacobs syslog(LOG_DEBUG, "sequence file fd: %d", fd); 2032264Sjacobs 2042264Sjacobs /* drop our privilege again */ 2052264Sjacobs #ifdef PRIV_ALLSETS 2062264Sjacobs /* drop file access privilege */ 2072264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, 2088521SJonathan.Ca@Sun.COM PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL); 2092264Sjacobs #else 2102264Sjacobs seteuid(getuid()); 2112264Sjacobs #endif 2122264Sjacobs 2132264Sjacobs if (fd >= 0) { 2142264Sjacobs /* wait for a lock on the file */ 2152264Sjacobs if (lockf(fd, F_LOCK, 0) == 0) { 2162264Sjacobs char buf[8]; 2172264Sjacobs int next; 2182264Sjacobs 2192264Sjacobs /* get the current id */ 2202264Sjacobs (void) memset(buf, 0, sizeof (buf)); 2212264Sjacobs if (read(fd, buf, sizeof (buf)) > 0) 2222264Sjacobs result = atoi(buf); 2232264Sjacobs 2242264Sjacobs next = ((result < 999) ? (result + 1) : 0); 2252264Sjacobs 2262264Sjacobs /* store the next id in the file */ 2272264Sjacobs snprintf(buf, sizeof (buf), "%.3d", next); 2282264Sjacobs if ((lseek(fd, 0, SEEK_SET) == 0) && 2292264Sjacobs (ftruncate(fd, 0) == 0)) 2302264Sjacobs write(fd, buf, strlen(buf)); 2312264Sjacobs } 2327253Sjacobs close(fd); 2332264Sjacobs } 2342264Sjacobs syslog(LOG_DEBUG, "next_job_id() is %d", result); 2352264Sjacobs 2362264Sjacobs return (result); 2372264Sjacobs } 2382264Sjacobs 2392264Sjacobs static int 2402264Sjacobs reserved_port() 2412264Sjacobs { 2422264Sjacobs int result = -1; 2432264Sjacobs int port; 2442264Sjacobs 2452264Sjacobs /* gain back enough privilege to open a reserved port */ 2462264Sjacobs #ifdef PRIV_ALLSETS 2472264Sjacobs if ((priv_set( 2488521SJonathan.Ca@Sun.COM PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 2492264Sjacobs syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 2502264Sjacobs return (-1); 2512264Sjacobs } 2522264Sjacobs #else 2532264Sjacobs seteuid(0); 2542264Sjacobs #endif 2552264Sjacobs 2562264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 2572264Sjacobs port = 0; /* set to 0, rresvport_af() will find us one. */ 2582264Sjacobs result = rresvport_af(&port, AF_INET6); 2592264Sjacobs #else 2602264Sjacobs port = IPPORT_RESERVED - 1; 2612264Sjacobs while (((result = rresvport(&port)) < 0) && (port >= 0)) 2622264Sjacobs port--; 2632264Sjacobs #endif 2642264Sjacobs 2652264Sjacobs /* drop our privilege again */ 2662264Sjacobs #ifdef PRIV_ALLSETS 2672264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 2682264Sjacobs #else 2692264Sjacobs seteuid(getuid()); 2702264Sjacobs #endif 2712264Sjacobs 2722264Sjacobs return (result); 2732264Sjacobs } 2742264Sjacobs 2752264Sjacobs static char * 2762264Sjacobs get_user_name() 2772264Sjacobs { 2782264Sjacobs static struct passwd *p = NULL; 2792264Sjacobs 2802264Sjacobs if ((p = getpwuid(getuid())) != NULL) 2812264Sjacobs return (p->pw_name); 2822264Sjacobs else 2832264Sjacobs return ("unknown"); 2842264Sjacobs } 2852264Sjacobs 2862264Sjacobs static void 2872264Sjacobs add_args(int ac, char **av, char *buf, size_t len) 2882264Sjacobs { 2892264Sjacobs while (ac--) { 2902264Sjacobs strlcat(buf, " ", len); 2912264Sjacobs strlcat(buf, *(av++), len); 2922264Sjacobs } 2932264Sjacobs } 2942264Sjacobs 2952264Sjacobs static int 2962264Sjacobs massage_control_data(char *data, int id) 2972264Sjacobs { 2982264Sjacobs char *line, *iter = NULL; 299*9108SJonathan.Ca@Sun.COM char *ptr, *datacpy; 3002264Sjacobs char host[BUFSIZ]; 301*9108SJonathan.Ca@Sun.COM int host_present = 0; 302*9108SJonathan.Ca@Sun.COM 303*9108SJonathan.Ca@Sun.COM if (gethostname(host, sizeof (host)) != 0) 304*9108SJonathan.Ca@Sun.COM return (-1); 3052264Sjacobs 306*9108SJonathan.Ca@Sun.COM if ((datacpy = strdup(data)) == NULL) { 307*9108SJonathan.Ca@Sun.COM return (-1); 308*9108SJonathan.Ca@Sun.COM } 3092264Sjacobs 310*9108SJonathan.Ca@Sun.COM for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 311*9108SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 3122264Sjacobs 3132264Sjacobs if (ptr[0] == 'H') { 314*9108SJonathan.Ca@Sun.COM if (strncmp(++ptr, host, strlen(host)) != 0) { 315*9108SJonathan.Ca@Sun.COM free(datacpy); 3162264Sjacobs return (-1); 317*9108SJonathan.Ca@Sun.COM } 318*9108SJonathan.Ca@Sun.COM host_present = 1; 3192264Sjacobs } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 3202264Sjacobs /* check the user name */ 3212264Sjacobs uid_t uid = getuid(); 3222264Sjacobs struct passwd *pw; 3232264Sjacobs int len; 3242264Sjacobs 325*9108SJonathan.Ca@Sun.COM if (uid == 0) { /* let root do what they want */ 3262264Sjacobs continue; 327*9108SJonathan.Ca@Sun.COM } 328*9108SJonathan.Ca@Sun.COM if ((pw = getpwuid(uid)) == NULL) { 329*9108SJonathan.Ca@Sun.COM free(datacpy); 3302264Sjacobs return (-1); /* failed */ 331*9108SJonathan.Ca@Sun.COM } 3322264Sjacobs len = strlen(pw->pw_name); 333*9108SJonathan.Ca@Sun.COM if ((strncmp(++ptr, pw->pw_name, len) != 0)) { 334*9108SJonathan.Ca@Sun.COM free(datacpy); 3352264Sjacobs return (-1); /* failed */ 336*9108SJonathan.Ca@Sun.COM } 3372264Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 3382264Sjacobs /* check/fix df?XXXhostname */ 3392264Sjacobs ptr++; 3402264Sjacobs 341*9108SJonathan.Ca@Sun.COM if (strlen(ptr) < 6) { 342*9108SJonathan.Ca@Sun.COM free(datacpy); 3432264Sjacobs return (-1); 344*9108SJonathan.Ca@Sun.COM } 3452264Sjacobs if ((ptr[0] == 'd') && (ptr[1] == 'f') && 3462264Sjacobs (ptr[3] == 'X') && (ptr[4] == 'X') && 3472264Sjacobs (ptr[5] == 'X')) { 3482264Sjacobs ptr[3] = '0' + (id / 100) % 10; 3492264Sjacobs ptr[4] = '0' + (id / 10) % 10; 3502264Sjacobs ptr[5] = '0' + id % 10; 3512264Sjacobs 352*9108SJonathan.Ca@Sun.COM if (strncmp(&ptr[6], host, strlen(host)) != 0) { 353*9108SJonathan.Ca@Sun.COM free(datacpy); 354*9108SJonathan.Ca@Sun.COM return (-1); 355*9108SJonathan.Ca@Sun.COM } 356*9108SJonathan.Ca@Sun.COM } else { 357*9108SJonathan.Ca@Sun.COM free(datacpy); 3582264Sjacobs return (-1); 359*9108SJonathan.Ca@Sun.COM } 3602264Sjacobs } 361*9108SJonathan.Ca@Sun.COM 3622264Sjacobs } 363*9108SJonathan.Ca@Sun.COM free(datacpy); 364*9108SJonathan.Ca@Sun.COM 365*9108SJonathan.Ca@Sun.COM if (!host_present) { 366*9108SJonathan.Ca@Sun.COM return (-1); 367*9108SJonathan.Ca@Sun.COM } 368*9108SJonathan.Ca@Sun.COM 3692264Sjacobs return (1); 3702264Sjacobs } 3712264Sjacobs 3722264Sjacobs static int 3732264Sjacobs send_lpd_message(int fd, char *fmt, ...) 3742264Sjacobs { 3752264Sjacobs char buf[BUFSIZ]; 3762264Sjacobs size_t size; 3772264Sjacobs va_list ap; 3782264Sjacobs 3792264Sjacobs va_start(ap, fmt); 3802264Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 3812264Sjacobs va_end(ap); 3822264Sjacobs if (size == 0) 3832264Sjacobs size = 1; 3842264Sjacobs 3852264Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 3862264Sjacobs 3872264Sjacobs if (write(fd, buf, size) != size) { 3882264Sjacobs errno = EIO; 3892264Sjacobs return (-1); 3902264Sjacobs } 3912264Sjacobs 3922264Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 3932264Sjacobs return (-1); 3942264Sjacobs 3952264Sjacobs return (0); 3962264Sjacobs } 3972264Sjacobs 3982264Sjacobs static int 3992264Sjacobs send_data_file(int sock, char *dfname, char *name) 4002264Sjacobs { 4012264Sjacobs size_t len; 4022264Sjacobs off_t off = 0; 4032264Sjacobs struct stat st; 4042264Sjacobs char buf[32]; 4052264Sjacobs int fd = -1; 4062264Sjacobs 4072264Sjacobs if (strcmp(name, "standard input") != 0) { 4082264Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 4092264Sjacobs return (-1); 4102264Sjacobs 4112264Sjacobs if (fstat(fd, &st) < 0) 4122264Sjacobs return (-1); 4132264Sjacobs } else 4142264Sjacobs st.st_size = MAXINT; /* should be 0 */ 4152264Sjacobs 4162264Sjacobs /* request data file transfer, read ack/nack */ 4172264Sjacobs errno = ENOSPC; 4182264Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 4192264Sjacobs return (-1); 4202264Sjacobs 4212264Sjacobs if (fd != -1) { 4222264Sjacobs /* write the data */ 4232264Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 4242264Sjacobs return (-1); 4252264Sjacobs close(fd); 4262264Sjacobs 4272264Sjacobs /* request ack/nack after the data transfer */ 4282264Sjacobs errno = EIO; 4292264Sjacobs if (send_lpd_message(sock, "") < 0) 4302264Sjacobs return (-1); 4312264Sjacobs } 4322264Sjacobs 4332264Sjacobs return (0); 4342264Sjacobs } 4352264Sjacobs 4362264Sjacobs static int 4372264Sjacobs send_control_file(int sock, char *data, int id) 4382264Sjacobs { 4392264Sjacobs int len; 4402264Sjacobs char buf[BUFSIZ]; 4418521SJonathan.Ca@Sun.COM char *ptr, *iter = NULL; 4428521SJonathan.Ca@Sun.COM char *datacpy = NULL; 4438521SJonathan.Ca@Sun.COM char *host = NULL; 4442264Sjacobs 4452264Sjacobs len = strlen(data); 4462264Sjacobs 4478521SJonathan.Ca@Sun.COM if ((datacpy = strdup(data)) == NULL) 4488521SJonathan.Ca@Sun.COM return (-1); 4498521SJonathan.Ca@Sun.COM 4508521SJonathan.Ca@Sun.COM syslog(LOG_DEBUG, "cfA: %s\n", datacpy); 4518521SJonathan.Ca@Sun.COM 4528521SJonathan.Ca@Sun.COM for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 4538521SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 4548521SJonathan.Ca@Sun.COM 4558521SJonathan.Ca@Sun.COM if (ptr[0] != 'H') 4568521SJonathan.Ca@Sun.COM continue; 4578521SJonathan.Ca@Sun.COM 4588521SJonathan.Ca@Sun.COM ptr++; 4598521SJonathan.Ca@Sun.COM host = ptr; 4608521SJonathan.Ca@Sun.COM syslog(LOG_DEBUG, "hostname: %s\n", host); 4618521SJonathan.Ca@Sun.COM } 4628521SJonathan.Ca@Sun.COM 4638521SJonathan.Ca@Sun.COM free(datacpy); 4648521SJonathan.Ca@Sun.COM 4652264Sjacobs /* request data file transfer, read ack/nack */ 4662264Sjacobs errno = ENOSPC; 4672264Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 4682264Sjacobs return (-1); 4692264Sjacobs 4702264Sjacobs /* write the data */ 4712264Sjacobs if (write(sock, data, len) != len) 4722264Sjacobs return (-1); 4732264Sjacobs 4742264Sjacobs /* request ack/nack after the data transfer */ 4752264Sjacobs errno = EIO; 4762264Sjacobs if (send_lpd_message(sock, "") < 0) 4772264Sjacobs return (-1); 4782264Sjacobs 4792264Sjacobs return (0); 4802264Sjacobs } 4812264Sjacobs 4822264Sjacobs 4832264Sjacobs static int 4844275Sjacobs submit_job(int sock, char *printer, int job_id, char *path) 4852264Sjacobs { 4864275Sjacobs struct stat st; 4872264Sjacobs int current = 0; 4882264Sjacobs off_t off = 0; 4892264Sjacobs char *metadata = NULL; 4902264Sjacobs char *ptr, *iter = NULL; 4912264Sjacobs int fd, err; 4922264Sjacobs int sent_files = 0; 4932264Sjacobs char buf[BUFSIZ]; 4942264Sjacobs size_t len; 4954275Sjacobs 4964275Sjacobs /* open the control file */ 4974275Sjacobs if ((fd = open(path, O_RDONLY)) < 0) { 4984275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 4998521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5004275Sjacobs return (-1); 5014275Sjacobs } 5024275Sjacobs 5034275Sjacobs /* get the size of the control file */ 5044275Sjacobs if (fstat(fd, &st) < 0) { 5054275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 5068521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5074275Sjacobs close(fd); 5084275Sjacobs return (-1); 5094275Sjacobs } 5104275Sjacobs 5114275Sjacobs /* allocate memory for the control file */ 5124275Sjacobs if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 5134275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 5148521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5154275Sjacobs close(fd); 5164275Sjacobs return (-1); 5174275Sjacobs } 5182264Sjacobs 5192264Sjacobs /* read in the control file */ 5204275Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 5214275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 5228521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5234275Sjacobs free(metadata); 5244275Sjacobs close(fd); 5252264Sjacobs return (-1); 5262264Sjacobs } 5272264Sjacobs 5282264Sjacobs /* massage the control file */ 5292264Sjacobs if (massage_control_data(metadata, job_id) < 0) { 5302264Sjacobs /* bad control data, dump the job */ 5312264Sjacobs syslog(LOG_ALERT, 5328521SJonathan.Ca@Sun.COM "bad control file, possible subversion attempt"); 5334275Sjacobs free(metadata); 534*9108SJonathan.Ca@Sun.COM errno = EINVAL; 5354275Sjacobs close(fd); 5364275Sjacobs return (-1); 5372264Sjacobs } 5382264Sjacobs 5392264Sjacobs /* request to transfer the job */ 5402264Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 5412264Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 5422264Sjacobs errno = ENOENT; 5432264Sjacobs return (-1); 5442264Sjacobs } 5452264Sjacobs 5462264Sjacobs /* send the control data */ 5472264Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 5482264Sjacobs err = errno; 5492264Sjacobs write(sock, "\001\n", 2); /* abort */ 5502264Sjacobs errno = err; 5512264Sjacobs return (-1); 5522264Sjacobs } 5532264Sjacobs 5542264Sjacobs /* walk the control file sending the data files */ 5552264Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 5568521SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 5572264Sjacobs char *name = NULL; 5582264Sjacobs 5592264Sjacobs if (ptr[0] != 'U') 5602264Sjacobs continue; 5612264Sjacobs 5622264Sjacobs name = strtok_r(NULL, "\n", &iter); 5632264Sjacobs if (name[0] != 'N') 5642264Sjacobs continue; 5652264Sjacobs 5662264Sjacobs ptr++; 5672264Sjacobs name++; 5682264Sjacobs 5692264Sjacobs if (send_data_file(sock, ptr, name) < 0) { 5702264Sjacobs err = errno; 5712264Sjacobs write(sock, "\001\n", 2); /* abort */ 5722264Sjacobs errno = err; 5732264Sjacobs return (-1); 5742264Sjacobs } 5752264Sjacobs if (strcmp(name, "standard input") != 0) 5762264Sjacobs sent_files++; 5772264Sjacobs } 5782264Sjacobs 5792264Sjacobs /* write back the job-id */ 5802264Sjacobs err = errno; 5812264Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 5822264Sjacobs ftruncate(fd, 0); 5832264Sjacobs write(fd, &job_id, sizeof (job_id)); 5842264Sjacobs close(fd); 5852264Sjacobs } 5862264Sjacobs errno = err; 5872264Sjacobs 5882264Sjacobs if (sent_files != 0) { 5892264Sjacobs err = errno; 5902264Sjacobs close(sock); 5912264Sjacobs errno = err; 5922264Sjacobs } 5932264Sjacobs 5942264Sjacobs return (0); 5952264Sjacobs } 5962264Sjacobs static int 5974275Sjacobs query(int fd, char *printer, int ac, char **av) 5982264Sjacobs { 5992264Sjacobs char buf[BUFSIZ]; 6002264Sjacobs int rc, len; 6012264Sjacobs 6022264Sjacobs /* build the request */ 6032264Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 6042264Sjacobs add_args(ac, av, buf, sizeof (buf)); 6052264Sjacobs strlcat(buf, "\n", sizeof (buf)); 6062264Sjacobs len = strlen(buf); 6072264Sjacobs 6082264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 6092264Sjacobs errno = EMSGSIZE; 6102264Sjacobs rc = -1; 6112264Sjacobs } else 6122264Sjacobs rc = 0; 6132264Sjacobs 6142264Sjacobs return (rc); 6152264Sjacobs } 6162264Sjacobs 6172264Sjacobs static int 6184275Sjacobs cancel(int fd, char *printer, int ac, char **av) 6192264Sjacobs { 6202264Sjacobs char buf[BUFSIZ]; 6212264Sjacobs int rc, len; 6222264Sjacobs 6232264Sjacobs /* build the request */ 6242264Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 6252264Sjacobs add_args(ac, av, buf, sizeof (buf)); 6262264Sjacobs strlcat(buf, "\n", sizeof (buf)); 6272264Sjacobs len = strlen(buf); 6282264Sjacobs 6292264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 6302264Sjacobs errno = EMSGSIZE; 6312264Sjacobs rc = -1; 6322264Sjacobs } else 6332264Sjacobs rc = 0; 6342264Sjacobs 6352264Sjacobs return (rc); 6362264Sjacobs } 6372264Sjacobs 6382264Sjacobs static void 6392264Sjacobs usage(char *program) 6402264Sjacobs { 6412264Sjacobs char *name; 6422264Sjacobs 6432264Sjacobs setreuid(getuid(), getuid()); 6442264Sjacobs 6452264Sjacobs if ((name = strrchr(program, '/')) == NULL) 6462264Sjacobs name = program; 6472264Sjacobs else 6482264Sjacobs name++; 6492264Sjacobs 6504275Sjacobs fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 6518521SJonathan.Ca@Sun.COM name); 6524275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 6538521SJonathan.Ca@Sun.COM name); 6544275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 6558521SJonathan.Ca@Sun.COM name); 6562264Sjacobs exit(EINVAL); 6572264Sjacobs } 6582264Sjacobs 6592264Sjacobs /* 6602264Sjacobs * The main program temporarily loses privilege while searching the command 6612264Sjacobs * line arguments. It then allocates any resources it need privilege for 6622264Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 6632264Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 6642264Sjacobs * based on destination hostname. Doing it this way reduces the potenential 6652264Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 6662264Sjacobs * unconnected reserved port, and exploitation of the remote print service 6672264Sjacobs * by a calling program. 6682264Sjacobs */ 6692264Sjacobs int 6702264Sjacobs main(int ac, char *av[]) 6712264Sjacobs { 6722264Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 6732264Sjacobs int fd, c, timeout = 0, exit_code = 0; 6744275Sjacobs char *host = NULL, *queue = NULL; 6752264Sjacobs uid_t uid = getuid(); 6762264Sjacobs #ifdef PRIV_ALLSETS 6772264Sjacobs priv_set_t *saveset = NULL; 6782264Sjacobs #endif 6792264Sjacobs 6802264Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 6812264Sjacobs 6822264Sjacobs #ifdef PRIV_ALLSETS 6832264Sjacobs 6842264Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 6852264Sjacobs 6862264Sjacobs if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 6878521SJonathan.Ca@Sun.COM "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 6888521SJonathan.Ca@Sun.COM ",", (const char **)NULL)) == NULL) { 6892264Sjacobs syslog(LOG_ERR, 6902264Sjacobs "lpd_port: priv_str_to_set saveset failed: %m\n"); 6912264Sjacobs return (-1); 6922264Sjacobs } 6932264Sjacobs 6942264Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 6952264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 6962264Sjacobs return (-1); 6972264Sjacobs } 6982264Sjacobs 6992264Sjacobs /* 7002264Sjacobs * These privileges permanently dropped in next_job_id() and 7012264Sjacobs * reserved_port() 7022264Sjacobs */ 7032264Sjacobs 7042264Sjacobs if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 7052264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 7062264Sjacobs return (-1); 7072264Sjacobs } 7082264Sjacobs 7092264Sjacobs priv_freeset(saveset); 7102264Sjacobs 7112264Sjacobs syslog(LOG_DEBUG, "using privs"); 7122264Sjacobs #else 7132264Sjacobs 7142264Sjacobs syslog(LOG_DEBUG, "no privs"); 7152264Sjacobs seteuid(uid); 7162264Sjacobs #endif 7172264Sjacobs 7184275Sjacobs while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 7192264Sjacobs switch (c) { 7204275Sjacobs case 'H': 7214275Sjacobs host = optarg; 7224275Sjacobs break; 7234275Sjacobs case 't': 7244275Sjacobs timeout = atoi(optarg); 7254275Sjacobs break; 7262264Sjacobs case 'c': 7272264Sjacobs if (operation != OP_NONE) 7282264Sjacobs usage(av[0]); 7292264Sjacobs operation = OP_CANCEL; 7304275Sjacobs queue = optarg; 7312264Sjacobs break; 7322264Sjacobs case 'q': 7332264Sjacobs if (operation != OP_NONE) 7342264Sjacobs usage(av[0]); 7352264Sjacobs operation = OP_QUERY; 7364275Sjacobs queue = optarg; 7372264Sjacobs break; 7382264Sjacobs case 's': 7392264Sjacobs if (operation != OP_NONE) 7402264Sjacobs usage(av[0]); 7412264Sjacobs operation = OP_SUBMIT; 7424275Sjacobs queue = optarg; 7432264Sjacobs break; 7442264Sjacobs default: 7452264Sjacobs usage(av[0]); 7462264Sjacobs /* does not return */ 7472264Sjacobs } 7482264Sjacobs } 7492264Sjacobs 7504275Sjacobs if ((host == NULL) || (queue == NULL) || (timeout < 0) || 7514275Sjacobs (operation == OP_NONE)) 7522264Sjacobs usage(av[0]); 7532264Sjacobs 7542264Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 7552264Sjacobs if ((c = next_job_id()) < 0) { 7562264Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 7572264Sjacobs return (-1); 7582264Sjacobs } 7592264Sjacobs 7602264Sjacobs if ((fd = reserved_port()) < 0) { 7612264Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 7622264Sjacobs return (errno); 7632264Sjacobs } 7642264Sjacobs 7652264Sjacobs /* 7662264Sjacobs * we no longer want or need any elevated privilege, lose it all 7672264Sjacobs * permanently. 7682264Sjacobs */ 7692264Sjacobs 7702264Sjacobs setreuid(uid, uid); 7712264Sjacobs 7722264Sjacobs /* connect to the print service */ 7734275Sjacobs if ((fd = sock_connect(fd, host, timeout)) < 0) 7742264Sjacobs return (errno); 7752264Sjacobs 7762264Sjacobs /* perform the requested operation */ 7772264Sjacobs switch (operation) { 7782264Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 7794275Sjacobs if (submit_job(fd, queue, c, av[optind]) < 0) 7802264Sjacobs exit_code = errno; 7812264Sjacobs break; 7822264Sjacobs case OP_QUERY: /* send the query string, return the fd */ 7834275Sjacobs if (query(fd, queue, ac - optind, &av[optind]) < 0) 7842264Sjacobs exit_code = errno; 7852264Sjacobs break; 7862264Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 7874275Sjacobs if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 7882264Sjacobs exit_code = errno; 7892264Sjacobs break; 7902264Sjacobs default: /* This should never happen */ 7912264Sjacobs exit_code = EINVAL; 7922264Sjacobs } 7932264Sjacobs 7942264Sjacobs 7952264Sjacobs /* if the operation succeeded, send the fd to our parent */ 7962264Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 7972264Sjacobs char buf[BUFSIZ]; 7982264Sjacobs 7992264Sjacobs exit_code = errno; 8002264Sjacobs 8012264Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 8022264Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 8032264Sjacobs write(1, buf, c); 8042264Sjacobs } 8052264Sjacobs 8062264Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 8072264Sjacobs return (exit_code); 8082264Sjacobs } 809