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*8521SJonathan.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)(); 118*8521SJonathan.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, 133*8521SJonathan.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, 190*8521SJonathan.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, 208*8521SJonathan.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( 248*8521SJonathan.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; 2992264Sjacobs char *ptr; 3002264Sjacobs char host[BUFSIZ]; 3012264Sjacobs 3022264Sjacobs gethostname(host, sizeof (host)); 3032264Sjacobs 3042264Sjacobs for (ptr = strchr(data, '\n'); ptr != NULL; ptr = strchr(ptr, '\n')) { 3052264Sjacobs ptr++; 3062264Sjacobs 3072264Sjacobs if (ptr[0] == 'H') { 3082264Sjacobs if (strncmp(++ptr, host, strlen(host)) != 0) 3092264Sjacobs return (-1); 3102264Sjacobs } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 3112264Sjacobs /* check the user name */ 3122264Sjacobs uid_t uid = getuid(); 3132264Sjacobs struct passwd *pw; 3142264Sjacobs int len; 3152264Sjacobs 3162264Sjacobs if (uid == 0) /* let root do what they want */ 3172264Sjacobs continue; 3182264Sjacobs if ((pw = getpwuid(uid)) == NULL) 3192264Sjacobs return (-1); /* failed */ 3202264Sjacobs len = strlen(pw->pw_name); 3212264Sjacobs if ((strncmp(++ptr, pw->pw_name, len) != 0) || 3222264Sjacobs (ptr[len] != '\n')) 3232264Sjacobs return (-1); /* failed */ 3242264Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 3252264Sjacobs /* check/fix df?XXXhostname */ 3262264Sjacobs ptr++; 3272264Sjacobs 3282264Sjacobs if (strlen(ptr) < 6) 3292264Sjacobs return (-1); 3302264Sjacobs if ((ptr[0] == 'd') && (ptr[1] == 'f') && 3312264Sjacobs (ptr[3] == 'X') && (ptr[4] == 'X') && 3322264Sjacobs (ptr[5] == 'X')) { 3332264Sjacobs ptr[3] = '0' + (id / 100) % 10; 3342264Sjacobs ptr[4] = '0' + (id / 10) % 10; 3352264Sjacobs ptr[5] = '0' + id % 10; 3362264Sjacobs 3372264Sjacobs if (strncmp(&ptr[6], host, strlen(host)) != 0) 3382264Sjacobs return (-1); 3392264Sjacobs } else 3402264Sjacobs return (-1); 3412264Sjacobs } 3422264Sjacobs } 3432264Sjacobs return (1); 3442264Sjacobs } 3452264Sjacobs 3462264Sjacobs static int 3472264Sjacobs send_lpd_message(int fd, char *fmt, ...) 3482264Sjacobs { 3492264Sjacobs char buf[BUFSIZ]; 3502264Sjacobs size_t size; 3512264Sjacobs va_list ap; 3522264Sjacobs 3532264Sjacobs va_start(ap, fmt); 3542264Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 3552264Sjacobs va_end(ap); 3562264Sjacobs if (size == 0) 3572264Sjacobs size = 1; 3582264Sjacobs 3592264Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 3602264Sjacobs 3612264Sjacobs if (write(fd, buf, size) != size) { 3622264Sjacobs errno = EIO; 3632264Sjacobs return (-1); 3642264Sjacobs } 3652264Sjacobs 3662264Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 3672264Sjacobs return (-1); 3682264Sjacobs 3692264Sjacobs return (0); 3702264Sjacobs } 3712264Sjacobs 3722264Sjacobs static int 3732264Sjacobs send_data_file(int sock, char *dfname, char *name) 3742264Sjacobs { 3752264Sjacobs size_t len; 3762264Sjacobs off_t off = 0; 3772264Sjacobs struct stat st; 3782264Sjacobs char buf[32]; 3792264Sjacobs int fd = -1; 3802264Sjacobs 3812264Sjacobs if (strcmp(name, "standard input") != 0) { 3822264Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 3832264Sjacobs return (-1); 3842264Sjacobs 3852264Sjacobs if (fstat(fd, &st) < 0) 3862264Sjacobs return (-1); 3872264Sjacobs } else 3882264Sjacobs st.st_size = MAXINT; /* should be 0 */ 3892264Sjacobs 3902264Sjacobs /* request data file transfer, read ack/nack */ 3912264Sjacobs errno = ENOSPC; 3922264Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 3932264Sjacobs return (-1); 3942264Sjacobs 3952264Sjacobs if (fd != -1) { 3962264Sjacobs /* write the data */ 3972264Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 3982264Sjacobs return (-1); 3992264Sjacobs close(fd); 4002264Sjacobs 4012264Sjacobs /* request ack/nack after the data transfer */ 4022264Sjacobs errno = EIO; 4032264Sjacobs if (send_lpd_message(sock, "") < 0) 4042264Sjacobs return (-1); 4052264Sjacobs } 4062264Sjacobs 4072264Sjacobs return (0); 4082264Sjacobs } 4092264Sjacobs 4102264Sjacobs static int 4112264Sjacobs send_control_file(int sock, char *data, int id) 4122264Sjacobs { 4132264Sjacobs int len; 4142264Sjacobs char buf[BUFSIZ]; 415*8521SJonathan.Ca@Sun.COM char *ptr, *iter = NULL; 416*8521SJonathan.Ca@Sun.COM char *datacpy = NULL; 417*8521SJonathan.Ca@Sun.COM char *host = NULL; 4182264Sjacobs 4192264Sjacobs len = strlen(data); 4202264Sjacobs 421*8521SJonathan.Ca@Sun.COM if ((datacpy = strdup(data)) == NULL) 422*8521SJonathan.Ca@Sun.COM return (-1); 423*8521SJonathan.Ca@Sun.COM 424*8521SJonathan.Ca@Sun.COM syslog(LOG_DEBUG, "cfA: %s\n", datacpy); 425*8521SJonathan.Ca@Sun.COM 426*8521SJonathan.Ca@Sun.COM for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 427*8521SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 428*8521SJonathan.Ca@Sun.COM 429*8521SJonathan.Ca@Sun.COM if (ptr[0] != 'H') 430*8521SJonathan.Ca@Sun.COM continue; 431*8521SJonathan.Ca@Sun.COM 432*8521SJonathan.Ca@Sun.COM ptr++; 433*8521SJonathan.Ca@Sun.COM host = ptr; 434*8521SJonathan.Ca@Sun.COM syslog(LOG_DEBUG, "hostname: %s\n", host); 435*8521SJonathan.Ca@Sun.COM } 436*8521SJonathan.Ca@Sun.COM 437*8521SJonathan.Ca@Sun.COM free(datacpy); 438*8521SJonathan.Ca@Sun.COM 4392264Sjacobs /* request data file transfer, read ack/nack */ 4402264Sjacobs errno = ENOSPC; 4412264Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 4422264Sjacobs return (-1); 4432264Sjacobs 4442264Sjacobs /* write the data */ 4452264Sjacobs if (write(sock, data, len) != len) 4462264Sjacobs return (-1); 4472264Sjacobs 4482264Sjacobs /* request ack/nack after the data transfer */ 4492264Sjacobs errno = EIO; 4502264Sjacobs if (send_lpd_message(sock, "") < 0) 4512264Sjacobs return (-1); 4522264Sjacobs 4532264Sjacobs return (0); 4542264Sjacobs } 4552264Sjacobs 4562264Sjacobs 4572264Sjacobs static int 4584275Sjacobs submit_job(int sock, char *printer, int job_id, char *path) 4592264Sjacobs { 4604275Sjacobs struct stat st; 4612264Sjacobs int current = 0; 4622264Sjacobs off_t off = 0; 4632264Sjacobs char *metadata = NULL; 4642264Sjacobs char *ptr, *iter = NULL; 4652264Sjacobs int fd, err; 4662264Sjacobs int sent_files = 0; 4672264Sjacobs char buf[BUFSIZ]; 4682264Sjacobs size_t len; 4694275Sjacobs 4704275Sjacobs /* open the control file */ 4714275Sjacobs if ((fd = open(path, O_RDONLY)) < 0) { 4724275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 473*8521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 4744275Sjacobs return (-1); 4754275Sjacobs } 4764275Sjacobs 4774275Sjacobs /* get the size of the control file */ 4784275Sjacobs if (fstat(fd, &st) < 0) { 4794275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 480*8521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 4814275Sjacobs close(fd); 4824275Sjacobs return (-1); 4834275Sjacobs } 4844275Sjacobs 4854275Sjacobs /* allocate memory for the control file */ 4864275Sjacobs if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 4874275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 488*8521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 4894275Sjacobs close(fd); 4904275Sjacobs return (-1); 4914275Sjacobs } 4922264Sjacobs 4932264Sjacobs /* read in the control file */ 4944275Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 4954275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 496*8521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 4974275Sjacobs free(metadata); 4984275Sjacobs close(fd); 4992264Sjacobs return (-1); 5002264Sjacobs } 5012264Sjacobs 5022264Sjacobs /* massage the control file */ 5032264Sjacobs if (massage_control_data(metadata, job_id) < 0) { 5042264Sjacobs /* bad control data, dump the job */ 5052264Sjacobs syslog(LOG_ALERT, 506*8521SJonathan.Ca@Sun.COM "bad control file, possible subversion attempt"); 5074275Sjacobs free(metadata); 5084275Sjacobs close(fd); 5094275Sjacobs return (-1); 5102264Sjacobs } 5112264Sjacobs 5122264Sjacobs /* request to transfer the job */ 5132264Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 5142264Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 5152264Sjacobs errno = ENOENT; 5162264Sjacobs return (-1); 5172264Sjacobs } 5182264Sjacobs 5192264Sjacobs /* send the control data */ 5202264Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 5212264Sjacobs err = errno; 5222264Sjacobs write(sock, "\001\n", 2); /* abort */ 5232264Sjacobs errno = err; 5242264Sjacobs return (-1); 5252264Sjacobs } 5262264Sjacobs 5272264Sjacobs /* walk the control file sending the data files */ 5282264Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 529*8521SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 5302264Sjacobs char *name = NULL; 5312264Sjacobs 5322264Sjacobs if (ptr[0] != 'U') 5332264Sjacobs continue; 5342264Sjacobs 5352264Sjacobs name = strtok_r(NULL, "\n", &iter); 5362264Sjacobs if (name[0] != 'N') 5372264Sjacobs continue; 5382264Sjacobs 5392264Sjacobs ptr++; 5402264Sjacobs name++; 5412264Sjacobs 5422264Sjacobs if (send_data_file(sock, ptr, name) < 0) { 5432264Sjacobs err = errno; 5442264Sjacobs write(sock, "\001\n", 2); /* abort */ 5452264Sjacobs errno = err; 5462264Sjacobs return (-1); 5472264Sjacobs } 5482264Sjacobs if (strcmp(name, "standard input") != 0) 5492264Sjacobs sent_files++; 5502264Sjacobs } 5512264Sjacobs 5522264Sjacobs /* write back the job-id */ 5532264Sjacobs err = errno; 5542264Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 5552264Sjacobs ftruncate(fd, 0); 5562264Sjacobs write(fd, &job_id, sizeof (job_id)); 5572264Sjacobs close(fd); 5582264Sjacobs } 5592264Sjacobs errno = err; 5602264Sjacobs 5612264Sjacobs if (sent_files != 0) { 5622264Sjacobs err = errno; 5632264Sjacobs close(sock); 5642264Sjacobs errno = err; 5652264Sjacobs } 5662264Sjacobs 5672264Sjacobs return (0); 5682264Sjacobs } 5692264Sjacobs static int 5704275Sjacobs query(int fd, char *printer, int ac, char **av) 5712264Sjacobs { 5722264Sjacobs char buf[BUFSIZ]; 5732264Sjacobs int rc, len; 5742264Sjacobs 5752264Sjacobs /* build the request */ 5762264Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 5772264Sjacobs add_args(ac, av, buf, sizeof (buf)); 5782264Sjacobs strlcat(buf, "\n", sizeof (buf)); 5792264Sjacobs len = strlen(buf); 5802264Sjacobs 5812264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 5822264Sjacobs errno = EMSGSIZE; 5832264Sjacobs rc = -1; 5842264Sjacobs } else 5852264Sjacobs rc = 0; 5862264Sjacobs 5872264Sjacobs return (rc); 5882264Sjacobs } 5892264Sjacobs 5902264Sjacobs static int 5914275Sjacobs cancel(int fd, char *printer, int ac, char **av) 5922264Sjacobs { 5932264Sjacobs char buf[BUFSIZ]; 5942264Sjacobs int rc, len; 5952264Sjacobs 5962264Sjacobs /* build the request */ 5972264Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 5982264Sjacobs add_args(ac, av, buf, sizeof (buf)); 5992264Sjacobs strlcat(buf, "\n", sizeof (buf)); 6002264Sjacobs len = strlen(buf); 6012264Sjacobs 6022264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 6032264Sjacobs errno = EMSGSIZE; 6042264Sjacobs rc = -1; 6052264Sjacobs } else 6062264Sjacobs rc = 0; 6072264Sjacobs 6082264Sjacobs return (rc); 6092264Sjacobs } 6102264Sjacobs 6112264Sjacobs static void 6122264Sjacobs usage(char *program) 6132264Sjacobs { 6142264Sjacobs char *name; 6152264Sjacobs 6162264Sjacobs setreuid(getuid(), getuid()); 6172264Sjacobs 6182264Sjacobs if ((name = strrchr(program, '/')) == NULL) 6192264Sjacobs name = program; 6202264Sjacobs else 6212264Sjacobs name++; 6222264Sjacobs 6234275Sjacobs fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 624*8521SJonathan.Ca@Sun.COM name); 6254275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 626*8521SJonathan.Ca@Sun.COM name); 6274275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 628*8521SJonathan.Ca@Sun.COM name); 6292264Sjacobs exit(EINVAL); 6302264Sjacobs } 6312264Sjacobs 6322264Sjacobs /* 6332264Sjacobs * The main program temporarily loses privilege while searching the command 6342264Sjacobs * line arguments. It then allocates any resources it need privilege for 6352264Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 6362264Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 6372264Sjacobs * based on destination hostname. Doing it this way reduces the potenential 6382264Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 6392264Sjacobs * unconnected reserved port, and exploitation of the remote print service 6402264Sjacobs * by a calling program. 6412264Sjacobs */ 6422264Sjacobs int 6432264Sjacobs main(int ac, char *av[]) 6442264Sjacobs { 6452264Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 6462264Sjacobs int fd, c, timeout = 0, exit_code = 0; 6474275Sjacobs char *host = NULL, *queue = NULL; 6482264Sjacobs uid_t uid = getuid(); 6492264Sjacobs #ifdef PRIV_ALLSETS 6502264Sjacobs priv_set_t *saveset = NULL; 6512264Sjacobs #endif 6522264Sjacobs 6532264Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 6542264Sjacobs 6552264Sjacobs #ifdef PRIV_ALLSETS 6562264Sjacobs 6572264Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 6582264Sjacobs 6592264Sjacobs if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 660*8521SJonathan.Ca@Sun.COM "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 661*8521SJonathan.Ca@Sun.COM ",", (const char **)NULL)) == NULL) { 6622264Sjacobs syslog(LOG_ERR, 6632264Sjacobs "lpd_port: priv_str_to_set saveset failed: %m\n"); 6642264Sjacobs return (-1); 6652264Sjacobs } 6662264Sjacobs 6672264Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 6682264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 6692264Sjacobs return (-1); 6702264Sjacobs } 6712264Sjacobs 6722264Sjacobs /* 6732264Sjacobs * These privileges permanently dropped in next_job_id() and 6742264Sjacobs * reserved_port() 6752264Sjacobs */ 6762264Sjacobs 6772264Sjacobs if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 6782264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 6792264Sjacobs return (-1); 6802264Sjacobs } 6812264Sjacobs 6822264Sjacobs priv_freeset(saveset); 6832264Sjacobs 6842264Sjacobs syslog(LOG_DEBUG, "using privs"); 6852264Sjacobs #else 6862264Sjacobs 6872264Sjacobs syslog(LOG_DEBUG, "no privs"); 6882264Sjacobs seteuid(uid); 6892264Sjacobs #endif 6902264Sjacobs 6914275Sjacobs while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 6922264Sjacobs switch (c) { 6934275Sjacobs case 'H': 6944275Sjacobs host = optarg; 6954275Sjacobs break; 6964275Sjacobs case 't': 6974275Sjacobs timeout = atoi(optarg); 6984275Sjacobs break; 6992264Sjacobs case 'c': 7002264Sjacobs if (operation != OP_NONE) 7012264Sjacobs usage(av[0]); 7022264Sjacobs operation = OP_CANCEL; 7034275Sjacobs queue = optarg; 7042264Sjacobs break; 7052264Sjacobs case 'q': 7062264Sjacobs if (operation != OP_NONE) 7072264Sjacobs usage(av[0]); 7082264Sjacobs operation = OP_QUERY; 7094275Sjacobs queue = optarg; 7102264Sjacobs break; 7112264Sjacobs case 's': 7122264Sjacobs if (operation != OP_NONE) 7132264Sjacobs usage(av[0]); 7142264Sjacobs operation = OP_SUBMIT; 7154275Sjacobs queue = optarg; 7162264Sjacobs break; 7172264Sjacobs default: 7182264Sjacobs usage(av[0]); 7192264Sjacobs /* does not return */ 7202264Sjacobs } 7212264Sjacobs } 7222264Sjacobs 7234275Sjacobs if ((host == NULL) || (queue == NULL) || (timeout < 0) || 7244275Sjacobs (operation == OP_NONE)) 7252264Sjacobs usage(av[0]); 7262264Sjacobs 7272264Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 7282264Sjacobs if ((c = next_job_id()) < 0) { 7292264Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 7302264Sjacobs return (-1); 7312264Sjacobs } 7322264Sjacobs 7332264Sjacobs if ((fd = reserved_port()) < 0) { 7342264Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 7352264Sjacobs return (errno); 7362264Sjacobs } 7372264Sjacobs 7382264Sjacobs /* 7392264Sjacobs * we no longer want or need any elevated privilege, lose it all 7402264Sjacobs * permanently. 7412264Sjacobs */ 7422264Sjacobs 7432264Sjacobs setreuid(uid, uid); 7442264Sjacobs 7452264Sjacobs /* connect to the print service */ 7464275Sjacobs if ((fd = sock_connect(fd, host, timeout)) < 0) 7472264Sjacobs return (errno); 7482264Sjacobs 7492264Sjacobs /* perform the requested operation */ 7502264Sjacobs switch (operation) { 7512264Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 7524275Sjacobs if (submit_job(fd, queue, c, av[optind]) < 0) 7532264Sjacobs exit_code = errno; 7542264Sjacobs break; 7552264Sjacobs case OP_QUERY: /* send the query string, return the fd */ 7564275Sjacobs if (query(fd, queue, ac - optind, &av[optind]) < 0) 7572264Sjacobs exit_code = errno; 7582264Sjacobs break; 7592264Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 7604275Sjacobs if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 7612264Sjacobs exit_code = errno; 7622264Sjacobs break; 7632264Sjacobs default: /* This should never happen */ 7642264Sjacobs exit_code = EINVAL; 7652264Sjacobs } 7662264Sjacobs 7672264Sjacobs 7682264Sjacobs /* if the operation succeeded, send the fd to our parent */ 7692264Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 7702264Sjacobs char buf[BUFSIZ]; 7712264Sjacobs 7722264Sjacobs exit_code = errno; 7732264Sjacobs 7742264Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 7752264Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 7762264Sjacobs write(1, buf, c); 7772264Sjacobs } 7782264Sjacobs 7792264Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 7802264Sjacobs return (exit_code); 7812264Sjacobs } 782