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*11537SCasper.Dik@Sun.COM * Copyright 2010 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; 2999696SJonathan.Ca@Sun.COM char *ptr, *mod_ptr, *datacpy; 3002264Sjacobs char host[BUFSIZ]; 3019108SJonathan.Ca@Sun.COM int host_present = 0; 3029108SJonathan.Ca@Sun.COM 3039108SJonathan.Ca@Sun.COM if (gethostname(host, sizeof (host)) != 0) 3049108SJonathan.Ca@Sun.COM return (-1); 3052264Sjacobs 3069108SJonathan.Ca@Sun.COM if ((datacpy = strdup(data)) == NULL) { 3079108SJonathan.Ca@Sun.COM return (-1); 3089108SJonathan.Ca@Sun.COM } 3092264Sjacobs 3109108SJonathan.Ca@Sun.COM for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 3119108SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 3122264Sjacobs 3132264Sjacobs if (ptr[0] == 'H') { 3149108SJonathan.Ca@Sun.COM if (strncmp(++ptr, host, strlen(host)) != 0) { 3159108SJonathan.Ca@Sun.COM free(datacpy); 3162264Sjacobs return (-1); 3179108SJonathan.Ca@Sun.COM } 3189108SJonathan.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 3259108SJonathan.Ca@Sun.COM if (uid == 0) { /* let root do what they want */ 3262264Sjacobs continue; 3279108SJonathan.Ca@Sun.COM } 3289108SJonathan.Ca@Sun.COM if ((pw = getpwuid(uid)) == NULL) { 3299108SJonathan.Ca@Sun.COM free(datacpy); 3302264Sjacobs return (-1); /* failed */ 3319108SJonathan.Ca@Sun.COM } 3322264Sjacobs len = strlen(pw->pw_name); 3339108SJonathan.Ca@Sun.COM if ((strncmp(++ptr, pw->pw_name, len) != 0)) { 3349108SJonathan.Ca@Sun.COM free(datacpy); 3352264Sjacobs return (-1); /* failed */ 3369108SJonathan.Ca@Sun.COM } 3372264Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 3382264Sjacobs /* check/fix df?XXXhostname */ 3392264Sjacobs ptr++; 3402264Sjacobs 3419108SJonathan.Ca@Sun.COM if (strlen(ptr) < 6) { 3429108SJonathan.Ca@Sun.COM free(datacpy); 3432264Sjacobs return (-1); 3449108SJonathan.Ca@Sun.COM } 3459696SJonathan.Ca@Sun.COM 3469696SJonathan.Ca@Sun.COM /* 3479696SJonathan.Ca@Sun.COM * As ptr is a copy of the string (df?XXX...) the code 3489696SJonathan.Ca@Sun.COM * needs to work on the original, hence the need for 3499696SJonathan.Ca@Sun.COM * mod_ptr. No need to check for a NULL mod_ptr 3509696SJonathan.Ca@Sun.COM * because the required string must already exist as 3519696SJonathan.Ca@Sun.COM * ptr is a copy of the original data. 3529696SJonathan.Ca@Sun.COM */ 3532264Sjacobs 3549696SJonathan.Ca@Sun.COM mod_ptr = strstr(data, ptr); 3559696SJonathan.Ca@Sun.COM 3569696SJonathan.Ca@Sun.COM if ((mod_ptr[0] == 'd') && (mod_ptr[1] == 'f') && 3579696SJonathan.Ca@Sun.COM (mod_ptr[3] == 'X') && (mod_ptr[4] == 'X') && 3589696SJonathan.Ca@Sun.COM (mod_ptr[5] == 'X')) { 3599696SJonathan.Ca@Sun.COM mod_ptr[3] = '0' + (id / 100) % 10; 3609696SJonathan.Ca@Sun.COM mod_ptr[4] = '0' + (id / 10) % 10; 3619696SJonathan.Ca@Sun.COM mod_ptr[5] = '0' + id % 10; 3629696SJonathan.Ca@Sun.COM 3639696SJonathan.Ca@Sun.COM if (strncmp(&mod_ptr[6], host, strlen(host)) 3649696SJonathan.Ca@Sun.COM != 0) { 3659108SJonathan.Ca@Sun.COM free(datacpy); 3669108SJonathan.Ca@Sun.COM return (-1); 3679108SJonathan.Ca@Sun.COM } 3689108SJonathan.Ca@Sun.COM } else { 3699108SJonathan.Ca@Sun.COM free(datacpy); 3702264Sjacobs return (-1); 3719108SJonathan.Ca@Sun.COM } 3722264Sjacobs } 3739108SJonathan.Ca@Sun.COM 3742264Sjacobs } 3759108SJonathan.Ca@Sun.COM free(datacpy); 3769108SJonathan.Ca@Sun.COM 3779108SJonathan.Ca@Sun.COM if (!host_present) { 3789108SJonathan.Ca@Sun.COM return (-1); 3799108SJonathan.Ca@Sun.COM } 3809108SJonathan.Ca@Sun.COM 3812264Sjacobs return (1); 3822264Sjacobs } 3832264Sjacobs 3842264Sjacobs static int 3852264Sjacobs send_lpd_message(int fd, char *fmt, ...) 3862264Sjacobs { 3872264Sjacobs char buf[BUFSIZ]; 3882264Sjacobs size_t size; 3892264Sjacobs va_list ap; 3902264Sjacobs 3912264Sjacobs va_start(ap, fmt); 3922264Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 3932264Sjacobs va_end(ap); 3942264Sjacobs if (size == 0) 3952264Sjacobs size = 1; 3962264Sjacobs 3972264Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 3982264Sjacobs 3992264Sjacobs if (write(fd, buf, size) != size) { 4002264Sjacobs errno = EIO; 4012264Sjacobs return (-1); 4022264Sjacobs } 4032264Sjacobs 4042264Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 4052264Sjacobs return (-1); 4062264Sjacobs 4072264Sjacobs return (0); 4082264Sjacobs } 4092264Sjacobs 4102264Sjacobs static int 4112264Sjacobs send_data_file(int sock, char *dfname, char *name) 4122264Sjacobs { 4132264Sjacobs size_t len; 4142264Sjacobs off_t off = 0; 4152264Sjacobs struct stat st; 4162264Sjacobs char buf[32]; 4172264Sjacobs int fd = -1; 4182264Sjacobs 4192264Sjacobs if (strcmp(name, "standard input") != 0) { 4202264Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 4212264Sjacobs return (-1); 4222264Sjacobs 4232264Sjacobs if (fstat(fd, &st) < 0) 4242264Sjacobs return (-1); 4252264Sjacobs } else 4262264Sjacobs st.st_size = MAXINT; /* should be 0 */ 4272264Sjacobs 4282264Sjacobs /* request data file transfer, read ack/nack */ 4292264Sjacobs errno = ENOSPC; 4302264Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 4312264Sjacobs return (-1); 4322264Sjacobs 4332264Sjacobs if (fd != -1) { 4342264Sjacobs /* write the data */ 4352264Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 4362264Sjacobs return (-1); 4372264Sjacobs close(fd); 4382264Sjacobs 4392264Sjacobs /* request ack/nack after the data transfer */ 4402264Sjacobs errno = EIO; 4412264Sjacobs if (send_lpd_message(sock, "") < 0) 4422264Sjacobs return (-1); 4432264Sjacobs } 4442264Sjacobs 4452264Sjacobs return (0); 4462264Sjacobs } 4472264Sjacobs 4482264Sjacobs static int 4492264Sjacobs send_control_file(int sock, char *data, int id) 4502264Sjacobs { 4512264Sjacobs int len; 4522264Sjacobs char buf[BUFSIZ]; 4538521SJonathan.Ca@Sun.COM char *ptr, *iter = NULL; 4548521SJonathan.Ca@Sun.COM char *datacpy = NULL; 4558521SJonathan.Ca@Sun.COM char *host = NULL; 4562264Sjacobs 4572264Sjacobs len = strlen(data); 4582264Sjacobs 4598521SJonathan.Ca@Sun.COM if ((datacpy = strdup(data)) == NULL) 4608521SJonathan.Ca@Sun.COM return (-1); 4618521SJonathan.Ca@Sun.COM 4628521SJonathan.Ca@Sun.COM syslog(LOG_DEBUG, "cfA: %s\n", datacpy); 4638521SJonathan.Ca@Sun.COM 4648521SJonathan.Ca@Sun.COM for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 4658521SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 4668521SJonathan.Ca@Sun.COM 4678521SJonathan.Ca@Sun.COM if (ptr[0] != 'H') 4688521SJonathan.Ca@Sun.COM continue; 4698521SJonathan.Ca@Sun.COM 4708521SJonathan.Ca@Sun.COM ptr++; 4718521SJonathan.Ca@Sun.COM host = ptr; 4728521SJonathan.Ca@Sun.COM syslog(LOG_DEBUG, "hostname: %s\n", host); 4738521SJonathan.Ca@Sun.COM } 4748521SJonathan.Ca@Sun.COM 4758521SJonathan.Ca@Sun.COM free(datacpy); 4768521SJonathan.Ca@Sun.COM 4772264Sjacobs /* request data file transfer, read ack/nack */ 4782264Sjacobs errno = ENOSPC; 4792264Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 4802264Sjacobs return (-1); 4812264Sjacobs 4822264Sjacobs /* write the data */ 4832264Sjacobs if (write(sock, data, len) != len) 4842264Sjacobs return (-1); 4852264Sjacobs 4862264Sjacobs /* request ack/nack after the data transfer */ 4872264Sjacobs errno = EIO; 4882264Sjacobs if (send_lpd_message(sock, "") < 0) 4892264Sjacobs return (-1); 4902264Sjacobs 4912264Sjacobs return (0); 4922264Sjacobs } 4932264Sjacobs 4942264Sjacobs 4952264Sjacobs static int 4964275Sjacobs submit_job(int sock, char *printer, int job_id, char *path) 4972264Sjacobs { 4984275Sjacobs struct stat st; 4992264Sjacobs int current = 0; 5002264Sjacobs off_t off = 0; 5012264Sjacobs char *metadata = NULL; 5022264Sjacobs char *ptr, *iter = NULL; 5032264Sjacobs int fd, err; 5042264Sjacobs int sent_files = 0; 5052264Sjacobs char buf[BUFSIZ]; 5062264Sjacobs size_t len; 5074275Sjacobs 5084275Sjacobs /* open the control file */ 5094275Sjacobs if ((fd = open(path, O_RDONLY)) < 0) { 5104275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 5118521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5124275Sjacobs return (-1); 5134275Sjacobs } 5144275Sjacobs 5154275Sjacobs /* get the size of the control file */ 5164275Sjacobs if (fstat(fd, &st) < 0) { 5174275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 5188521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5194275Sjacobs close(fd); 5204275Sjacobs return (-1); 5214275Sjacobs } 5224275Sjacobs 5234275Sjacobs /* allocate memory for the control file */ 5244275Sjacobs if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 5254275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 5268521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5274275Sjacobs close(fd); 5284275Sjacobs return (-1); 5294275Sjacobs } 5302264Sjacobs 5312264Sjacobs /* read in the control file */ 5324275Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 5334275Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 5348521SJonathan.Ca@Sun.COM sock, printer, job_id, path); 5354275Sjacobs free(metadata); 5364275Sjacobs close(fd); 5372264Sjacobs return (-1); 5382264Sjacobs } 5392264Sjacobs 5402264Sjacobs /* massage the control file */ 5412264Sjacobs if (massage_control_data(metadata, job_id) < 0) { 5422264Sjacobs /* bad control data, dump the job */ 5432264Sjacobs syslog(LOG_ALERT, 5448521SJonathan.Ca@Sun.COM "bad control file, possible subversion attempt"); 5454275Sjacobs free(metadata); 5469108SJonathan.Ca@Sun.COM errno = EINVAL; 5474275Sjacobs close(fd); 5484275Sjacobs return (-1); 5492264Sjacobs } 5502264Sjacobs 5512264Sjacobs /* request to transfer the job */ 5522264Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 5532264Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 5542264Sjacobs errno = ENOENT; 5552264Sjacobs return (-1); 5562264Sjacobs } 5572264Sjacobs 5582264Sjacobs /* send the control data */ 5592264Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 5602264Sjacobs err = errno; 5612264Sjacobs write(sock, "\001\n", 2); /* abort */ 5622264Sjacobs errno = err; 5632264Sjacobs return (-1); 5642264Sjacobs } 5652264Sjacobs 5662264Sjacobs /* walk the control file sending the data files */ 5672264Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 5688521SJonathan.Ca@Sun.COM ptr = strtok_r(NULL, "\n", &iter)) { 5692264Sjacobs char *name = NULL; 5702264Sjacobs 5712264Sjacobs if (ptr[0] != 'U') 5722264Sjacobs continue; 5732264Sjacobs 5742264Sjacobs name = strtok_r(NULL, "\n", &iter); 5752264Sjacobs if (name[0] != 'N') 5762264Sjacobs continue; 5772264Sjacobs 5782264Sjacobs ptr++; 5792264Sjacobs name++; 5802264Sjacobs 5812264Sjacobs if (send_data_file(sock, ptr, name) < 0) { 5822264Sjacobs err = errno; 5832264Sjacobs write(sock, "\001\n", 2); /* abort */ 5842264Sjacobs errno = err; 5852264Sjacobs return (-1); 5862264Sjacobs } 5872264Sjacobs if (strcmp(name, "standard input") != 0) 5882264Sjacobs sent_files++; 5892264Sjacobs } 5902264Sjacobs 5912264Sjacobs /* write back the job-id */ 5922264Sjacobs err = errno; 5932264Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 5942264Sjacobs ftruncate(fd, 0); 5952264Sjacobs write(fd, &job_id, sizeof (job_id)); 5962264Sjacobs close(fd); 5972264Sjacobs } 5982264Sjacobs errno = err; 5992264Sjacobs 6002264Sjacobs if (sent_files != 0) { 6012264Sjacobs err = errno; 6022264Sjacobs close(sock); 6032264Sjacobs errno = err; 6042264Sjacobs } 6052264Sjacobs 6062264Sjacobs return (0); 6072264Sjacobs } 6082264Sjacobs static int 6094275Sjacobs query(int fd, char *printer, int ac, char **av) 6102264Sjacobs { 6112264Sjacobs char buf[BUFSIZ]; 6122264Sjacobs int rc, len; 6132264Sjacobs 6142264Sjacobs /* build the request */ 6152264Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 6162264Sjacobs add_args(ac, av, buf, sizeof (buf)); 6172264Sjacobs strlcat(buf, "\n", sizeof (buf)); 6182264Sjacobs len = strlen(buf); 6192264Sjacobs 6202264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 6212264Sjacobs errno = EMSGSIZE; 6222264Sjacobs rc = -1; 6232264Sjacobs } else 6242264Sjacobs rc = 0; 6252264Sjacobs 6262264Sjacobs return (rc); 6272264Sjacobs } 6282264Sjacobs 6292264Sjacobs static int 6304275Sjacobs cancel(int fd, char *printer, int ac, char **av) 6312264Sjacobs { 6322264Sjacobs char buf[BUFSIZ]; 6332264Sjacobs int rc, len; 6342264Sjacobs 6352264Sjacobs /* build the request */ 6362264Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 6372264Sjacobs add_args(ac, av, buf, sizeof (buf)); 6382264Sjacobs strlcat(buf, "\n", sizeof (buf)); 6392264Sjacobs len = strlen(buf); 6402264Sjacobs 6412264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 6422264Sjacobs errno = EMSGSIZE; 6432264Sjacobs rc = -1; 6442264Sjacobs } else 6452264Sjacobs rc = 0; 6462264Sjacobs 6472264Sjacobs return (rc); 6482264Sjacobs } 6492264Sjacobs 6502264Sjacobs static void 6512264Sjacobs usage(char *program) 6522264Sjacobs { 6532264Sjacobs char *name; 6542264Sjacobs 6552264Sjacobs setreuid(getuid(), getuid()); 6562264Sjacobs 6572264Sjacobs if ((name = strrchr(program, '/')) == NULL) 6582264Sjacobs name = program; 6592264Sjacobs else 6602264Sjacobs name++; 6612264Sjacobs 6624275Sjacobs fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 6638521SJonathan.Ca@Sun.COM name); 6644275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 6658521SJonathan.Ca@Sun.COM name); 6664275Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 6678521SJonathan.Ca@Sun.COM name); 6682264Sjacobs exit(EINVAL); 6692264Sjacobs } 6702264Sjacobs 6712264Sjacobs /* 6722264Sjacobs * The main program temporarily loses privilege while searching the command 6732264Sjacobs * line arguments. It then allocates any resources it need privilege for 6742264Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 6752264Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 6762264Sjacobs * based on destination hostname. Doing it this way reduces the potenential 6772264Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 6782264Sjacobs * unconnected reserved port, and exploitation of the remote print service 6792264Sjacobs * by a calling program. 6802264Sjacobs */ 6812264Sjacobs int 6822264Sjacobs main(int ac, char *av[]) 6832264Sjacobs { 6842264Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 6852264Sjacobs int fd, c, timeout = 0, exit_code = 0; 6864275Sjacobs char *host = NULL, *queue = NULL; 6872264Sjacobs uid_t uid = getuid(); 6882264Sjacobs #ifdef PRIV_ALLSETS 689*11537SCasper.Dik@Sun.COM priv_set_t *saveset; 6902264Sjacobs #endif 6912264Sjacobs 6922264Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 6932264Sjacobs 6942264Sjacobs #ifdef PRIV_ALLSETS 6952264Sjacobs 6962264Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 6972264Sjacobs 698*11537SCasper.Dik@Sun.COM if ((saveset = priv_allocset()) == NULL) { 699*11537SCasper.Dik@Sun.COM syslog(LOG_ERR, "lpd_port: priv_allocset saveset failed: %m\n"); 7002264Sjacobs return (-1); 7012264Sjacobs } 7022264Sjacobs 703*11537SCasper.Dik@Sun.COM priv_basicset(saveset); 704*11537SCasper.Dik@Sun.COM (void) priv_addset(saveset, PRIV_NET_PRIVADDR); 705*11537SCasper.Dik@Sun.COM (void) priv_addset(saveset, PRIV_FILE_DAC_READ); 706*11537SCasper.Dik@Sun.COM (void) priv_addset(saveset, PRIV_FILE_DAC_WRITE); 707*11537SCasper.Dik@Sun.COM 7082264Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 7092264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 7102264Sjacobs return (-1); 7112264Sjacobs } 7122264Sjacobs 713*11537SCasper.Dik@Sun.COM priv_freeset(saveset); 714*11537SCasper.Dik@Sun.COM 7152264Sjacobs /* 7162264Sjacobs * These privileges permanently dropped in next_job_id() and 7172264Sjacobs * reserved_port() 7182264Sjacobs */ 7192264Sjacobs 720*11537SCasper.Dik@Sun.COM if (priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, 721*11537SCasper.Dik@Sun.COM PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, (char *)NULL) < 0) { 722*11537SCasper.Dik@Sun.COM syslog(LOG_ERR, "lpd_port:priv_set:priv_off failed: %m"); 7232264Sjacobs return (-1); 7242264Sjacobs } 7252264Sjacobs 7262264Sjacobs syslog(LOG_DEBUG, "using privs"); 7272264Sjacobs #else 7282264Sjacobs 7292264Sjacobs syslog(LOG_DEBUG, "no privs"); 7302264Sjacobs seteuid(uid); 7312264Sjacobs #endif 7322264Sjacobs 7334275Sjacobs while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 7342264Sjacobs switch (c) { 7354275Sjacobs case 'H': 7364275Sjacobs host = optarg; 7374275Sjacobs break; 7384275Sjacobs case 't': 7394275Sjacobs timeout = atoi(optarg); 7404275Sjacobs break; 7412264Sjacobs case 'c': 7422264Sjacobs if (operation != OP_NONE) 7432264Sjacobs usage(av[0]); 7442264Sjacobs operation = OP_CANCEL; 7454275Sjacobs queue = optarg; 7462264Sjacobs break; 7472264Sjacobs case 'q': 7482264Sjacobs if (operation != OP_NONE) 7492264Sjacobs usage(av[0]); 7502264Sjacobs operation = OP_QUERY; 7514275Sjacobs queue = optarg; 7522264Sjacobs break; 7532264Sjacobs case 's': 7542264Sjacobs if (operation != OP_NONE) 7552264Sjacobs usage(av[0]); 7562264Sjacobs operation = OP_SUBMIT; 7574275Sjacobs queue = optarg; 7582264Sjacobs break; 7592264Sjacobs default: 7602264Sjacobs usage(av[0]); 7612264Sjacobs /* does not return */ 7622264Sjacobs } 7632264Sjacobs } 7642264Sjacobs 7654275Sjacobs if ((host == NULL) || (queue == NULL) || (timeout < 0) || 7664275Sjacobs (operation == OP_NONE)) 7672264Sjacobs usage(av[0]); 7682264Sjacobs 7692264Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 7702264Sjacobs if ((c = next_job_id()) < 0) { 7712264Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 7722264Sjacobs return (-1); 7732264Sjacobs } 7742264Sjacobs 7752264Sjacobs if ((fd = reserved_port()) < 0) { 7762264Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 7772264Sjacobs return (errno); 7782264Sjacobs } 7792264Sjacobs 7802264Sjacobs /* 7812264Sjacobs * we no longer want or need any elevated privilege, lose it all 7822264Sjacobs * permanently. 7832264Sjacobs */ 7842264Sjacobs 7852264Sjacobs setreuid(uid, uid); 7862264Sjacobs 7872264Sjacobs /* connect to the print service */ 7884275Sjacobs if ((fd = sock_connect(fd, host, timeout)) < 0) 7892264Sjacobs return (errno); 7902264Sjacobs 7912264Sjacobs /* perform the requested operation */ 7922264Sjacobs switch (operation) { 7932264Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 7944275Sjacobs if (submit_job(fd, queue, c, av[optind]) < 0) 7952264Sjacobs exit_code = errno; 7962264Sjacobs break; 7972264Sjacobs case OP_QUERY: /* send the query string, return the fd */ 7984275Sjacobs if (query(fd, queue, ac - optind, &av[optind]) < 0) 7992264Sjacobs exit_code = errno; 8002264Sjacobs break; 8012264Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 8024275Sjacobs if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 8032264Sjacobs exit_code = errno; 8042264Sjacobs break; 8052264Sjacobs default: /* This should never happen */ 8062264Sjacobs exit_code = EINVAL; 8072264Sjacobs } 8082264Sjacobs 8092264Sjacobs 8102264Sjacobs /* if the operation succeeded, send the fd to our parent */ 8112264Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 8122264Sjacobs char buf[BUFSIZ]; 8132264Sjacobs 8142264Sjacobs exit_code = errno; 8152264Sjacobs 8162264Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 8172264Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 8182264Sjacobs write(1, buf, c); 8192264Sjacobs } 8202264Sjacobs 8212264Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 8222264Sjacobs return (exit_code); 8232264Sjacobs } 824