1355b4669Sjacobs /* 2355b4669Sjacobs * CDDL HEADER START 3355b4669Sjacobs * 4355b4669Sjacobs * The contents of this file are subject to the terms of the 5355b4669Sjacobs * Common Development and Distribution License (the "License"). 6355b4669Sjacobs * You may not use this file except in compliance with the License. 7355b4669Sjacobs * 8355b4669Sjacobs * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9355b4669Sjacobs * or http://www.opensolaris.org/os/licensing. 10355b4669Sjacobs * See the License for the specific language governing permissions 11355b4669Sjacobs * and limitations under the License. 12355b4669Sjacobs * 13355b4669Sjacobs * When distributing Covered Code, include this CDDL HEADER in each 14355b4669Sjacobs * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15355b4669Sjacobs * If applicable, add the following below this CDDL HEADER, with the 16355b4669Sjacobs * fields enclosed by brackets "[]" replaced with your own identifying 17355b4669Sjacobs * information: Portions Copyright [yyyy] [name of copyright owner] 18355b4669Sjacobs * 19355b4669Sjacobs * CDDL HEADER END 20355b4669Sjacobs */ 21355b4669Sjacobs 22355b4669Sjacobs /* 23*634e26ecSCasper H.S. Dik * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24355b4669Sjacobs * Use is subject to license terms. 25355b4669Sjacobs * 26355b4669Sjacobs */ 27355b4669Sjacobs 28355b4669Sjacobs /* $Id: lpd-port.c 155 2006-04-26 02:34:54Z ktou $ */ 29355b4669Sjacobs 30355b4669Sjacobs #include <config-site.h> 31355b4669Sjacobs 32355b4669Sjacobs #include <stdio.h> 33355b4669Sjacobs #include <stdlib.h> 34355b4669Sjacobs #include <unistd.h> 35355b4669Sjacobs #include <sys/types.h> 36355b4669Sjacobs #include <sys/stat.h> 37355b4669Sjacobs #include <fcntl.h> 38355b4669Sjacobs #include <stdarg.h> 39355b4669Sjacobs #include <string.h> 40355b4669Sjacobs #include <signal.h> 41355b4669Sjacobs #include <sys/socket.h> 42355b4669Sjacobs #include <netinet/in.h> 43355b4669Sjacobs #include <arpa/inet.h> 44355b4669Sjacobs #include <netdb.h> 45355b4669Sjacobs #include <errno.h> 46355b4669Sjacobs #include <syslog.h> 47355b4669Sjacobs #include <values.h> 48355b4669Sjacobs #include <stropts.h> /* for sendfd */ 49355b4669Sjacobs #include <sys/uio.h> /* for sendmsg stuff */ 50355b4669Sjacobs #include <pwd.h> 51355b4669Sjacobs #include <sys/sendfile.h> 52355b4669Sjacobs #include <ctype.h> 53355b4669Sjacobs #ifdef HAVE_PRIV_H 54355b4669Sjacobs #include <priv.h> 55355b4669Sjacobs #endif 56355b4669Sjacobs 57355b4669Sjacobs #ifndef JOB_ID_FILE 58355b4669Sjacobs #define JOB_ID_FILE "/var/run/rfc-1179.seq" 59355b4669Sjacobs #endif /* JOB_ID_FILE */ 60355b4669Sjacobs 61355b4669Sjacobs static int 62355b4669Sjacobs sendfd(int sockfd, int fd) 63355b4669Sjacobs { 64355b4669Sjacobs syslog(LOG_DEBUG, "sendfd(%d, %d)", sockfd, fd); 65355b4669Sjacobs 66355b4669Sjacobs #if defined(sun) && defined(unix) && defined(I_SENDFD) 67355b4669Sjacobs return (ioctl(sockfd, I_SENDFD, fd)); 68355b4669Sjacobs #else 69355b4669Sjacobs struct iovec iov[1]; 70355b4669Sjacobs struct msghdr msg; 71355b4669Sjacobs #ifdef CMSG_DATA 72355b4669Sjacobs struct cmsghdr cmp[1]; 73355b4669Sjacobs char buf[2]; /* send/recv 2 byte protocol */ 74355b4669Sjacobs 75355b4669Sjacobs iov[0].iov_base = buf; 76355b4669Sjacobs iov[0].iov_len = 2; 77355b4669Sjacobs 78355b4669Sjacobs cmp[0].cmsg_level = SOL_SOCKET; 79355b4669Sjacobs cmp[0].cmsg_type = SCM_RIGHTS; 80355b4669Sjacobs cmp[0].cmsg_len = sizeof (struct cmsghdr) + sizeof (int); 81355b4669Sjacobs * (int *)CMSG_DATA(cmp) = fd; 82355b4669Sjacobs 83355b4669Sjacobs buf[1] = 0; 84355b4669Sjacobs buf[0] = 0; 85355b4669Sjacobs msg.msg_control = cmp; 86355b4669Sjacobs msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int); 87355b4669Sjacobs #else 88355b4669Sjacobs iov[0].iov_base = NULL; 89355b4669Sjacobs iov[0].iov_len = 0; 90355b4669Sjacobs msg.msg_accrights = (caddr_t)&fd; 91355b4669Sjacobs msg.msg_accrights = sizeof (fd); 92355b4669Sjacobs #endif 93355b4669Sjacobs msg.msg_iov = iov; 94355b4669Sjacobs msg.msg_iovlen = 1; 95355b4669Sjacobs msg.msg_name = NULL; 96355b4669Sjacobs msg.msg_namelen = 0; 97355b4669Sjacobs 98355b4669Sjacobs return (sendmsg(sockfd, &msg, 0)); 99355b4669Sjacobs #endif 100355b4669Sjacobs } 101355b4669Sjacobs 102355b4669Sjacobs static void 103355b4669Sjacobs null(int i) 104355b4669Sjacobs { 105355b4669Sjacobs } 106355b4669Sjacobs 107355b4669Sjacobs static int 10810144ea8Sjacobs sock_connect(int sock, char *host, int timeout) 109355b4669Sjacobs { 110355b4669Sjacobs struct hostent *hp; 111355b4669Sjacobs struct servent *sp; 112355b4669Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 113355b4669Sjacobs struct sockaddr_in6 sin; 114355b4669Sjacobs #else 115355b4669Sjacobs struct sockaddr_in sin; 116355b4669Sjacobs #endif 117355b4669Sjacobs static void (*old_handler)(); 1180485cf53SJonathan Cowper-Andrewes int err, error_num; 119355b4669Sjacobs unsigned timo = 1; 120355b4669Sjacobs 121355b4669Sjacobs /* 122355b4669Sjacobs * Get the host address and port number to connect to. 123355b4669Sjacobs */ 12410144ea8Sjacobs if (host == NULL) { 125355b4669Sjacobs return (-1); 126355b4669Sjacobs } 127355b4669Sjacobs 128355b4669Sjacobs /* linux style NULL usage */ 129355b4669Sjacobs (void) memset((char *)&sin, (int)NULL, sizeof (sin)); 130355b4669Sjacobs 131355b4669Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 13210144ea8Sjacobs if ((hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, 133355b4669Sjacobs &error_num)) == NULL) { 134355b4669Sjacobs errno = ENOENT; 135355b4669Sjacobs return (-1); 136355b4669Sjacobs } 137355b4669Sjacobs (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); 138355b4669Sjacobs sin.sin6_family = hp->h_addrtype; 139355b4669Sjacobs #else 14010144ea8Sjacobs if ((hp = gethostbyname(host)) == NULL) { 141355b4669Sjacobs errno = ENOENT; 142355b4669Sjacobs return (-1); 143355b4669Sjacobs } 144355b4669Sjacobs 145355b4669Sjacobs (void) memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length); 146355b4669Sjacobs sin.sin_family = hp->h_addrtype; 147355b4669Sjacobs #endif 148355b4669Sjacobs 149355b4669Sjacobs if ((sp = getservbyname("printer", "tcp")) == NULL) { 150355b4669Sjacobs errno = ENOENT; 151355b4669Sjacobs return (-1); 152355b4669Sjacobs } 153355b4669Sjacobs 154355b4669Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 15510144ea8Sjacobs sin.sin6_port = sp->s_port; 156355b4669Sjacobs #else 15710144ea8Sjacobs sin.sin_port = sp->s_port; 158355b4669Sjacobs #endif 159355b4669Sjacobs 160355b4669Sjacobs retry: 161355b4669Sjacobs old_handler = signal(SIGALRM, null); 162355b4669Sjacobs (void) alarm(timeout); 163355b4669Sjacobs 164355b4669Sjacobs if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 165355b4669Sjacobs (void) alarm(0); 166355b4669Sjacobs (void) signal(SIGALRM, old_handler); 167355b4669Sjacobs 168355b4669Sjacobs if (errno == ECONNREFUSED && timo <= 16) { 169355b4669Sjacobs (void) sleep(timo); 170355b4669Sjacobs timo *= 2; 171355b4669Sjacobs goto retry; 172355b4669Sjacobs } 173355b4669Sjacobs 174355b4669Sjacobs return (-1); 175355b4669Sjacobs } 176355b4669Sjacobs 177355b4669Sjacobs (void) alarm(0); 178355b4669Sjacobs (void) signal(SIGALRM, old_handler); 179355b4669Sjacobs return (sock); 180355b4669Sjacobs } 181355b4669Sjacobs 182355b4669Sjacobs static int 183355b4669Sjacobs next_job_id() 184355b4669Sjacobs { 185355b4669Sjacobs int fd, result = getpid() % 1000; 186355b4669Sjacobs 187355b4669Sjacobs /* gain back enough privilege to open the id file */ 188355b4669Sjacobs #ifdef PRIV_ALLSETS 189355b4669Sjacobs if ((priv_set(PRIV_ON, PRIV_EFFECTIVE, 190355b4669Sjacobs PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL)) < 0) { 191355b4669Sjacobs syslog(LOG_ERR, "lpd_port:next_job_id:priv_set fails: : %m"); 192355b4669Sjacobs return (-1); 193355b4669Sjacobs } 194355b4669Sjacobs #else 195355b4669Sjacobs seteuid(0); 196355b4669Sjacobs #endif 197355b4669Sjacobs 198355b4669Sjacobs /* open the sequence file */ 199355b4669Sjacobs if (((fd = open(JOB_ID_FILE, O_RDWR)) < 0) && (errno == ENOENT)) 200355b4669Sjacobs fd = open(JOB_ID_FILE, O_CREAT|O_EXCL|O_RDWR, 0644); 201355b4669Sjacobs 202355b4669Sjacobs syslog(LOG_DEBUG, "sequence file fd: %d", fd); 203355b4669Sjacobs 204355b4669Sjacobs /* drop our privilege again */ 205355b4669Sjacobs #ifdef PRIV_ALLSETS 206355b4669Sjacobs /* drop file access privilege */ 207355b4669Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, 208355b4669Sjacobs PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL); 209355b4669Sjacobs #else 210355b4669Sjacobs seteuid(getuid()); 211355b4669Sjacobs #endif 212355b4669Sjacobs 213355b4669Sjacobs if (fd >= 0) { 214355b4669Sjacobs /* wait for a lock on the file */ 215355b4669Sjacobs if (lockf(fd, F_LOCK, 0) == 0) { 216355b4669Sjacobs char buf[8]; 217355b4669Sjacobs int next; 218355b4669Sjacobs 219355b4669Sjacobs /* get the current id */ 220355b4669Sjacobs (void) memset(buf, 0, sizeof (buf)); 221355b4669Sjacobs if (read(fd, buf, sizeof (buf)) > 0) 222355b4669Sjacobs result = atoi(buf); 223355b4669Sjacobs 224355b4669Sjacobs next = ((result < 999) ? (result + 1) : 0); 225355b4669Sjacobs 226355b4669Sjacobs /* store the next id in the file */ 227355b4669Sjacobs snprintf(buf, sizeof (buf), "%.3d", next); 228355b4669Sjacobs if ((lseek(fd, 0, SEEK_SET) == 0) && 229355b4669Sjacobs (ftruncate(fd, 0) == 0)) 230355b4669Sjacobs write(fd, buf, strlen(buf)); 231355b4669Sjacobs } 232c1ecd8b9Sjacobs close(fd); 233355b4669Sjacobs } 234355b4669Sjacobs syslog(LOG_DEBUG, "next_job_id() is %d", result); 235355b4669Sjacobs 236355b4669Sjacobs return (result); 237355b4669Sjacobs } 238355b4669Sjacobs 239355b4669Sjacobs static int 240355b4669Sjacobs reserved_port() 241355b4669Sjacobs { 242355b4669Sjacobs int result = -1; 243355b4669Sjacobs int port; 244355b4669Sjacobs 245355b4669Sjacobs /* gain back enough privilege to open a reserved port */ 246355b4669Sjacobs #ifdef PRIV_ALLSETS 247355b4669Sjacobs if ((priv_set( 248355b4669Sjacobs PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 249355b4669Sjacobs syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 250355b4669Sjacobs return (-1); 251355b4669Sjacobs } 252355b4669Sjacobs #else 253355b4669Sjacobs seteuid(0); 254355b4669Sjacobs #endif 255355b4669Sjacobs 256355b4669Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 257355b4669Sjacobs port = 0; /* set to 0, rresvport_af() will find us one. */ 258355b4669Sjacobs result = rresvport_af(&port, AF_INET6); 259355b4669Sjacobs #else 260355b4669Sjacobs port = IPPORT_RESERVED - 1; 261355b4669Sjacobs while (((result = rresvport(&port)) < 0) && (port >= 0)) 262355b4669Sjacobs port--; 263355b4669Sjacobs #endif 264355b4669Sjacobs 265355b4669Sjacobs /* drop our privilege again */ 266355b4669Sjacobs #ifdef PRIV_ALLSETS 267355b4669Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 268355b4669Sjacobs #else 269355b4669Sjacobs seteuid(getuid()); 270355b4669Sjacobs #endif 271355b4669Sjacobs 272355b4669Sjacobs return (result); 273355b4669Sjacobs } 274355b4669Sjacobs 275355b4669Sjacobs static char * 276355b4669Sjacobs get_user_name() 277355b4669Sjacobs { 278355b4669Sjacobs static struct passwd *p = NULL; 279355b4669Sjacobs 280355b4669Sjacobs if ((p = getpwuid(getuid())) != NULL) 281355b4669Sjacobs return (p->pw_name); 282355b4669Sjacobs else 283355b4669Sjacobs return ("unknown"); 284355b4669Sjacobs } 285355b4669Sjacobs 286355b4669Sjacobs static void 287355b4669Sjacobs add_args(int ac, char **av, char *buf, size_t len) 288355b4669Sjacobs { 289355b4669Sjacobs while (ac--) { 290355b4669Sjacobs strlcat(buf, " ", len); 291355b4669Sjacobs strlcat(buf, *(av++), len); 292355b4669Sjacobs } 293355b4669Sjacobs } 294355b4669Sjacobs 295355b4669Sjacobs static int 296355b4669Sjacobs massage_control_data(char *data, int id) 297355b4669Sjacobs { 298355b4669Sjacobs char *line, *iter = NULL; 29941232a16SJonathan Cowper-Andrewes char *ptr, *mod_ptr, *datacpy; 300355b4669Sjacobs char host[BUFSIZ]; 301e4944453SJonathan Cowper-Andrewes int host_present = 0; 302355b4669Sjacobs 303e4944453SJonathan Cowper-Andrewes if (gethostname(host, sizeof (host)) != 0) 304e4944453SJonathan Cowper-Andrewes return (-1); 305355b4669Sjacobs 306e4944453SJonathan Cowper-Andrewes if ((datacpy = strdup(data)) == NULL) { 307e4944453SJonathan Cowper-Andrewes return (-1); 308e4944453SJonathan Cowper-Andrewes } 309e4944453SJonathan Cowper-Andrewes 310e4944453SJonathan Cowper-Andrewes for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 311e4944453SJonathan Cowper-Andrewes ptr = strtok_r(NULL, "\n", &iter)) { 312355b4669Sjacobs 313355b4669Sjacobs if (ptr[0] == 'H') { 314e4944453SJonathan Cowper-Andrewes if (strncmp(++ptr, host, strlen(host)) != 0) { 315e4944453SJonathan Cowper-Andrewes free(datacpy); 316355b4669Sjacobs return (-1); 317e4944453SJonathan Cowper-Andrewes } 318e4944453SJonathan Cowper-Andrewes host_present = 1; 319355b4669Sjacobs } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 320355b4669Sjacobs /* check the user name */ 321355b4669Sjacobs uid_t uid = getuid(); 322355b4669Sjacobs struct passwd *pw; 323355b4669Sjacobs int len; 324355b4669Sjacobs 325e4944453SJonathan Cowper-Andrewes if (uid == 0) { /* let root do what they want */ 326355b4669Sjacobs continue; 327e4944453SJonathan Cowper-Andrewes } 328e4944453SJonathan Cowper-Andrewes if ((pw = getpwuid(uid)) == NULL) { 329e4944453SJonathan Cowper-Andrewes free(datacpy); 330355b4669Sjacobs return (-1); /* failed */ 331e4944453SJonathan Cowper-Andrewes } 332355b4669Sjacobs len = strlen(pw->pw_name); 333e4944453SJonathan Cowper-Andrewes if ((strncmp(++ptr, pw->pw_name, len) != 0)) { 334e4944453SJonathan Cowper-Andrewes free(datacpy); 335355b4669Sjacobs return (-1); /* failed */ 336e4944453SJonathan Cowper-Andrewes } 337355b4669Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 338355b4669Sjacobs /* check/fix df?XXXhostname */ 339355b4669Sjacobs ptr++; 340355b4669Sjacobs 341e4944453SJonathan Cowper-Andrewes if (strlen(ptr) < 6) { 342e4944453SJonathan Cowper-Andrewes free(datacpy); 343355b4669Sjacobs return (-1); 344e4944453SJonathan Cowper-Andrewes } 345355b4669Sjacobs 34641232a16SJonathan Cowper-Andrewes /* 34741232a16SJonathan Cowper-Andrewes * As ptr is a copy of the string (df?XXX...) the code 34841232a16SJonathan Cowper-Andrewes * needs to work on the original, hence the need for 34941232a16SJonathan Cowper-Andrewes * mod_ptr. No need to check for a NULL mod_ptr 35041232a16SJonathan Cowper-Andrewes * because the required string must already exist as 35141232a16SJonathan Cowper-Andrewes * ptr is a copy of the original data. 35241232a16SJonathan Cowper-Andrewes */ 35341232a16SJonathan Cowper-Andrewes 35441232a16SJonathan Cowper-Andrewes mod_ptr = strstr(data, ptr); 35541232a16SJonathan Cowper-Andrewes 35641232a16SJonathan Cowper-Andrewes if ((mod_ptr[0] == 'd') && (mod_ptr[1] == 'f') && 35741232a16SJonathan Cowper-Andrewes (mod_ptr[3] == 'X') && (mod_ptr[4] == 'X') && 35841232a16SJonathan Cowper-Andrewes (mod_ptr[5] == 'X')) { 35941232a16SJonathan Cowper-Andrewes mod_ptr[3] = '0' + (id / 100) % 10; 36041232a16SJonathan Cowper-Andrewes mod_ptr[4] = '0' + (id / 10) % 10; 36141232a16SJonathan Cowper-Andrewes mod_ptr[5] = '0' + id % 10; 36241232a16SJonathan Cowper-Andrewes 36341232a16SJonathan Cowper-Andrewes if (strncmp(&mod_ptr[6], host, strlen(host)) 36441232a16SJonathan Cowper-Andrewes != 0) { 365e4944453SJonathan Cowper-Andrewes free(datacpy); 366355b4669Sjacobs return (-1); 367e4944453SJonathan Cowper-Andrewes } 368e4944453SJonathan Cowper-Andrewes } else { 369e4944453SJonathan Cowper-Andrewes free(datacpy); 370355b4669Sjacobs return (-1); 371355b4669Sjacobs } 372355b4669Sjacobs } 373e4944453SJonathan Cowper-Andrewes 374e4944453SJonathan Cowper-Andrewes } 375e4944453SJonathan Cowper-Andrewes free(datacpy); 376e4944453SJonathan Cowper-Andrewes 377e4944453SJonathan Cowper-Andrewes if (!host_present) { 378e4944453SJonathan Cowper-Andrewes return (-1); 379e4944453SJonathan Cowper-Andrewes } 380e4944453SJonathan Cowper-Andrewes 381355b4669Sjacobs return (1); 382355b4669Sjacobs } 383355b4669Sjacobs 384355b4669Sjacobs static int 385355b4669Sjacobs send_lpd_message(int fd, char *fmt, ...) 386355b4669Sjacobs { 387355b4669Sjacobs char buf[BUFSIZ]; 388355b4669Sjacobs size_t size; 389355b4669Sjacobs va_list ap; 390355b4669Sjacobs 391355b4669Sjacobs va_start(ap, fmt); 392355b4669Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 393355b4669Sjacobs va_end(ap); 394355b4669Sjacobs if (size == 0) 395355b4669Sjacobs size = 1; 396355b4669Sjacobs 397355b4669Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 398355b4669Sjacobs 399355b4669Sjacobs if (write(fd, buf, size) != size) { 400355b4669Sjacobs errno = EIO; 401355b4669Sjacobs return (-1); 402355b4669Sjacobs } 403355b4669Sjacobs 404355b4669Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 405355b4669Sjacobs return (-1); 406355b4669Sjacobs 407355b4669Sjacobs return (0); 408355b4669Sjacobs } 409355b4669Sjacobs 410355b4669Sjacobs static int 411355b4669Sjacobs send_data_file(int sock, char *dfname, char *name) 412355b4669Sjacobs { 413355b4669Sjacobs size_t len; 414355b4669Sjacobs off_t off = 0; 415355b4669Sjacobs struct stat st; 416355b4669Sjacobs char buf[32]; 417355b4669Sjacobs int fd = -1; 418355b4669Sjacobs 419355b4669Sjacobs if (strcmp(name, "standard input") != 0) { 420355b4669Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 421355b4669Sjacobs return (-1); 422355b4669Sjacobs 423355b4669Sjacobs if (fstat(fd, &st) < 0) 424355b4669Sjacobs return (-1); 425355b4669Sjacobs } else 426355b4669Sjacobs st.st_size = MAXINT; /* should be 0 */ 427355b4669Sjacobs 428355b4669Sjacobs /* request data file transfer, read ack/nack */ 429355b4669Sjacobs errno = ENOSPC; 430355b4669Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 431355b4669Sjacobs return (-1); 432355b4669Sjacobs 433355b4669Sjacobs if (fd != -1) { 434355b4669Sjacobs /* write the data */ 435355b4669Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 436355b4669Sjacobs return (-1); 437355b4669Sjacobs close(fd); 438355b4669Sjacobs 439355b4669Sjacobs /* request ack/nack after the data transfer */ 440355b4669Sjacobs errno = EIO; 441355b4669Sjacobs if (send_lpd_message(sock, "") < 0) 442355b4669Sjacobs return (-1); 443355b4669Sjacobs } 444355b4669Sjacobs 445355b4669Sjacobs return (0); 446355b4669Sjacobs } 447355b4669Sjacobs 448355b4669Sjacobs static int 449355b4669Sjacobs send_control_file(int sock, char *data, int id) 450355b4669Sjacobs { 451355b4669Sjacobs int len; 452355b4669Sjacobs char buf[BUFSIZ]; 4530485cf53SJonathan Cowper-Andrewes char *ptr, *iter = NULL; 4540485cf53SJonathan Cowper-Andrewes char *datacpy = NULL; 4550485cf53SJonathan Cowper-Andrewes char *host = NULL; 456355b4669Sjacobs 457355b4669Sjacobs len = strlen(data); 458355b4669Sjacobs 4590485cf53SJonathan Cowper-Andrewes if ((datacpy = strdup(data)) == NULL) 4600485cf53SJonathan Cowper-Andrewes return (-1); 4610485cf53SJonathan Cowper-Andrewes 4620485cf53SJonathan Cowper-Andrewes syslog(LOG_DEBUG, "cfA: %s\n", datacpy); 4630485cf53SJonathan Cowper-Andrewes 4640485cf53SJonathan Cowper-Andrewes for (ptr = strtok_r(datacpy, "\n", &iter); ptr != NULL; 4650485cf53SJonathan Cowper-Andrewes ptr = strtok_r(NULL, "\n", &iter)) { 4660485cf53SJonathan Cowper-Andrewes 4670485cf53SJonathan Cowper-Andrewes if (ptr[0] != 'H') 4680485cf53SJonathan Cowper-Andrewes continue; 4690485cf53SJonathan Cowper-Andrewes 4700485cf53SJonathan Cowper-Andrewes ptr++; 4710485cf53SJonathan Cowper-Andrewes host = ptr; 4720485cf53SJonathan Cowper-Andrewes syslog(LOG_DEBUG, "hostname: %s\n", host); 4730485cf53SJonathan Cowper-Andrewes } 4740485cf53SJonathan Cowper-Andrewes 4750485cf53SJonathan Cowper-Andrewes free(datacpy); 4760485cf53SJonathan Cowper-Andrewes 477355b4669Sjacobs /* request data file transfer, read ack/nack */ 478355b4669Sjacobs errno = ENOSPC; 479355b4669Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 480355b4669Sjacobs return (-1); 481355b4669Sjacobs 482355b4669Sjacobs /* write the data */ 483355b4669Sjacobs if (write(sock, data, len) != len) 484355b4669Sjacobs return (-1); 485355b4669Sjacobs 486355b4669Sjacobs /* request ack/nack after the data transfer */ 487355b4669Sjacobs errno = EIO; 488355b4669Sjacobs if (send_lpd_message(sock, "") < 0) 489355b4669Sjacobs return (-1); 490355b4669Sjacobs 491355b4669Sjacobs return (0); 492355b4669Sjacobs } 493355b4669Sjacobs 494355b4669Sjacobs 495355b4669Sjacobs static int 49610144ea8Sjacobs submit_job(int sock, char *printer, int job_id, char *path) 497355b4669Sjacobs { 49810144ea8Sjacobs struct stat st; 499355b4669Sjacobs int current = 0; 500355b4669Sjacobs off_t off = 0; 501355b4669Sjacobs char *metadata = NULL; 502355b4669Sjacobs char *ptr, *iter = NULL; 503355b4669Sjacobs int fd, err; 504355b4669Sjacobs int sent_files = 0; 505355b4669Sjacobs char buf[BUFSIZ]; 506355b4669Sjacobs size_t len; 50710144ea8Sjacobs 50810144ea8Sjacobs /* open the control file */ 50910144ea8Sjacobs if ((fd = open(path, O_RDONLY)) < 0) { 51010144ea8Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 51110144ea8Sjacobs sock, printer, job_id, path); 51210144ea8Sjacobs return (-1); 51310144ea8Sjacobs } 51410144ea8Sjacobs 51510144ea8Sjacobs /* get the size of the control file */ 51610144ea8Sjacobs if (fstat(fd, &st) < 0) { 51710144ea8Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 51810144ea8Sjacobs sock, printer, job_id, path); 51910144ea8Sjacobs close(fd); 52010144ea8Sjacobs return (-1); 52110144ea8Sjacobs } 52210144ea8Sjacobs 52310144ea8Sjacobs /* allocate memory for the control file */ 52410144ea8Sjacobs if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 52510144ea8Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 52610144ea8Sjacobs sock, printer, job_id, path); 52710144ea8Sjacobs close(fd); 52810144ea8Sjacobs return (-1); 52910144ea8Sjacobs } 530355b4669Sjacobs 531355b4669Sjacobs /* read in the control file */ 532355b4669Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 53310144ea8Sjacobs syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 53410144ea8Sjacobs sock, printer, job_id, path); 535355b4669Sjacobs free(metadata); 53610144ea8Sjacobs close(fd); 537355b4669Sjacobs return (-1); 538355b4669Sjacobs } 539355b4669Sjacobs 540355b4669Sjacobs /* massage the control file */ 541355b4669Sjacobs if (massage_control_data(metadata, job_id) < 0) { 542355b4669Sjacobs /* bad control data, dump the job */ 543355b4669Sjacobs syslog(LOG_ALERT, 544355b4669Sjacobs "bad control file, possible subversion attempt"); 54510144ea8Sjacobs free(metadata); 546e4944453SJonathan Cowper-Andrewes errno = EINVAL; 54710144ea8Sjacobs close(fd); 54810144ea8Sjacobs return (-1); 549355b4669Sjacobs } 550355b4669Sjacobs 551355b4669Sjacobs /* request to transfer the job */ 552355b4669Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 553355b4669Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 554355b4669Sjacobs errno = ENOENT; 555355b4669Sjacobs return (-1); 556355b4669Sjacobs } 557355b4669Sjacobs 558355b4669Sjacobs /* send the control data */ 559355b4669Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 560355b4669Sjacobs err = errno; 561355b4669Sjacobs write(sock, "\001\n", 2); /* abort */ 562355b4669Sjacobs errno = err; 563355b4669Sjacobs return (-1); 564355b4669Sjacobs } 565355b4669Sjacobs 566355b4669Sjacobs /* walk the control file sending the data files */ 567355b4669Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 568355b4669Sjacobs ptr = strtok_r(NULL, "\n", &iter)) { 569355b4669Sjacobs char *name = NULL; 570355b4669Sjacobs 571355b4669Sjacobs if (ptr[0] != 'U') 572355b4669Sjacobs continue; 573355b4669Sjacobs 574355b4669Sjacobs name = strtok_r(NULL, "\n", &iter); 575355b4669Sjacobs if (name[0] != 'N') 576355b4669Sjacobs continue; 577355b4669Sjacobs 578355b4669Sjacobs ptr++; 579355b4669Sjacobs name++; 580355b4669Sjacobs 581355b4669Sjacobs if (send_data_file(sock, ptr, name) < 0) { 582355b4669Sjacobs err = errno; 583355b4669Sjacobs write(sock, "\001\n", 2); /* abort */ 584355b4669Sjacobs errno = err; 585355b4669Sjacobs return (-1); 586355b4669Sjacobs } 587355b4669Sjacobs if (strcmp(name, "standard input") != 0) 588355b4669Sjacobs sent_files++; 589355b4669Sjacobs } 590355b4669Sjacobs 591355b4669Sjacobs /* write back the job-id */ 592355b4669Sjacobs err = errno; 593355b4669Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 594355b4669Sjacobs ftruncate(fd, 0); 595355b4669Sjacobs write(fd, &job_id, sizeof (job_id)); 596355b4669Sjacobs close(fd); 597355b4669Sjacobs } 598355b4669Sjacobs errno = err; 599355b4669Sjacobs 600355b4669Sjacobs if (sent_files != 0) { 601355b4669Sjacobs err = errno; 602355b4669Sjacobs close(sock); 603355b4669Sjacobs errno = err; 604355b4669Sjacobs } 605355b4669Sjacobs 606355b4669Sjacobs return (0); 607355b4669Sjacobs } 608355b4669Sjacobs static int 60910144ea8Sjacobs query(int fd, char *printer, int ac, char **av) 610355b4669Sjacobs { 611355b4669Sjacobs char buf[BUFSIZ]; 612355b4669Sjacobs int rc, len; 613355b4669Sjacobs 614355b4669Sjacobs /* build the request */ 615355b4669Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 616355b4669Sjacobs add_args(ac, av, buf, sizeof (buf)); 617355b4669Sjacobs strlcat(buf, "\n", sizeof (buf)); 618355b4669Sjacobs len = strlen(buf); 619355b4669Sjacobs 620355b4669Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 621355b4669Sjacobs errno = EMSGSIZE; 622355b4669Sjacobs rc = -1; 623355b4669Sjacobs } else 624355b4669Sjacobs rc = 0; 625355b4669Sjacobs 626355b4669Sjacobs return (rc); 627355b4669Sjacobs } 628355b4669Sjacobs 629355b4669Sjacobs static int 63010144ea8Sjacobs cancel(int fd, char *printer, int ac, char **av) 631355b4669Sjacobs { 632355b4669Sjacobs char buf[BUFSIZ]; 633355b4669Sjacobs int rc, len; 634355b4669Sjacobs 635355b4669Sjacobs /* build the request */ 636355b4669Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 637355b4669Sjacobs add_args(ac, av, buf, sizeof (buf)); 638355b4669Sjacobs strlcat(buf, "\n", sizeof (buf)); 639355b4669Sjacobs len = strlen(buf); 640355b4669Sjacobs 641355b4669Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 642355b4669Sjacobs errno = EMSGSIZE; 643355b4669Sjacobs rc = -1; 644355b4669Sjacobs } else 645355b4669Sjacobs rc = 0; 646355b4669Sjacobs 647355b4669Sjacobs return (rc); 648355b4669Sjacobs } 649355b4669Sjacobs 650355b4669Sjacobs static void 651355b4669Sjacobs usage(char *program) 652355b4669Sjacobs { 653355b4669Sjacobs char *name; 654355b4669Sjacobs 655355b4669Sjacobs setreuid(getuid(), getuid()); 656355b4669Sjacobs 657355b4669Sjacobs if ((name = strrchr(program, '/')) == NULL) 658355b4669Sjacobs name = program; 659355b4669Sjacobs else 660355b4669Sjacobs name++; 661355b4669Sjacobs 66210144ea8Sjacobs fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 66310144ea8Sjacobs name); 66410144ea8Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 66510144ea8Sjacobs name); 66610144ea8Sjacobs fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 66710144ea8Sjacobs name); 668355b4669Sjacobs exit(EINVAL); 669355b4669Sjacobs } 670355b4669Sjacobs 671355b4669Sjacobs /* 672355b4669Sjacobs * The main program temporarily loses privilege while searching the command 673355b4669Sjacobs * line arguments. It then allocates any resources it need privilege for 674355b4669Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 675355b4669Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 676355b4669Sjacobs * based on destination hostname. Doing it this way reduces the potenential 677355b4669Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 678355b4669Sjacobs * unconnected reserved port, and exploitation of the remote print service 679355b4669Sjacobs * by a calling program. 680355b4669Sjacobs */ 681355b4669Sjacobs int 682355b4669Sjacobs main(int ac, char *av[]) 683355b4669Sjacobs { 684355b4669Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 685355b4669Sjacobs int fd, c, timeout = 0, exit_code = 0; 68610144ea8Sjacobs char *host = NULL, *queue = NULL; 687355b4669Sjacobs uid_t uid = getuid(); 688355b4669Sjacobs #ifdef PRIV_ALLSETS 689*634e26ecSCasper H.S. Dik priv_set_t *saveset; 690355b4669Sjacobs #endif 691355b4669Sjacobs 692355b4669Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 693355b4669Sjacobs 694355b4669Sjacobs #ifdef PRIV_ALLSETS 695355b4669Sjacobs 696355b4669Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 697355b4669Sjacobs 698*634e26ecSCasper H.S. Dik if ((saveset = priv_allocset()) == NULL) { 699*634e26ecSCasper H.S. Dik syslog(LOG_ERR, "lpd_port: priv_allocset saveset failed: %m\n"); 700355b4669Sjacobs return (-1); 701355b4669Sjacobs } 702355b4669Sjacobs 703*634e26ecSCasper H.S. Dik priv_basicset(saveset); 704*634e26ecSCasper H.S. Dik (void) priv_addset(saveset, PRIV_NET_PRIVADDR); 705*634e26ecSCasper H.S. Dik (void) priv_addset(saveset, PRIV_FILE_DAC_READ); 706*634e26ecSCasper H.S. Dik (void) priv_addset(saveset, PRIV_FILE_DAC_WRITE); 707*634e26ecSCasper H.S. Dik 708355b4669Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 709355b4669Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 710355b4669Sjacobs return (-1); 711355b4669Sjacobs } 712355b4669Sjacobs 713*634e26ecSCasper H.S. Dik priv_freeset(saveset); 714*634e26ecSCasper H.S. Dik 715355b4669Sjacobs /* 716355b4669Sjacobs * These privileges permanently dropped in next_job_id() and 717355b4669Sjacobs * reserved_port() 718355b4669Sjacobs */ 719355b4669Sjacobs 720*634e26ecSCasper H.S. Dik if (priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, 721*634e26ecSCasper H.S. Dik PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, (char *)NULL) < 0) { 722*634e26ecSCasper H.S. Dik syslog(LOG_ERR, "lpd_port:priv_set:priv_off failed: %m"); 723355b4669Sjacobs return (-1); 724355b4669Sjacobs } 725355b4669Sjacobs 726355b4669Sjacobs syslog(LOG_DEBUG, "using privs"); 727355b4669Sjacobs #else 728355b4669Sjacobs 729355b4669Sjacobs syslog(LOG_DEBUG, "no privs"); 730355b4669Sjacobs seteuid(uid); 731355b4669Sjacobs #endif 732355b4669Sjacobs 73310144ea8Sjacobs while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 734355b4669Sjacobs switch (c) { 73510144ea8Sjacobs case 'H': 73610144ea8Sjacobs host = optarg; 73710144ea8Sjacobs break; 73810144ea8Sjacobs case 't': 73910144ea8Sjacobs timeout = atoi(optarg); 74010144ea8Sjacobs break; 741355b4669Sjacobs case 'c': 742355b4669Sjacobs if (operation != OP_NONE) 743355b4669Sjacobs usage(av[0]); 744355b4669Sjacobs operation = OP_CANCEL; 74510144ea8Sjacobs queue = optarg; 746355b4669Sjacobs break; 747355b4669Sjacobs case 'q': 748355b4669Sjacobs if (operation != OP_NONE) 749355b4669Sjacobs usage(av[0]); 750355b4669Sjacobs operation = OP_QUERY; 75110144ea8Sjacobs queue = optarg; 752355b4669Sjacobs break; 753355b4669Sjacobs case 's': 754355b4669Sjacobs if (operation != OP_NONE) 755355b4669Sjacobs usage(av[0]); 756355b4669Sjacobs operation = OP_SUBMIT; 75710144ea8Sjacobs queue = optarg; 758355b4669Sjacobs break; 759355b4669Sjacobs default: 760355b4669Sjacobs usage(av[0]); 761355b4669Sjacobs /* does not return */ 762355b4669Sjacobs } 763355b4669Sjacobs } 764355b4669Sjacobs 76510144ea8Sjacobs if ((host == NULL) || (queue == NULL) || (timeout < 0) || 76610144ea8Sjacobs (operation == OP_NONE)) 767355b4669Sjacobs usage(av[0]); 768355b4669Sjacobs 769355b4669Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 770355b4669Sjacobs if ((c = next_job_id()) < 0) { 771355b4669Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 772355b4669Sjacobs return (-1); 773355b4669Sjacobs } 774355b4669Sjacobs 775355b4669Sjacobs if ((fd = reserved_port()) < 0) { 776355b4669Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 777355b4669Sjacobs return (errno); 778355b4669Sjacobs } 779355b4669Sjacobs 780355b4669Sjacobs /* 781355b4669Sjacobs * we no longer want or need any elevated privilege, lose it all 782355b4669Sjacobs * permanently. 783355b4669Sjacobs */ 784355b4669Sjacobs 785355b4669Sjacobs setreuid(uid, uid); 786355b4669Sjacobs 787355b4669Sjacobs /* connect to the print service */ 78810144ea8Sjacobs if ((fd = sock_connect(fd, host, timeout)) < 0) 789355b4669Sjacobs return (errno); 790355b4669Sjacobs 791355b4669Sjacobs /* perform the requested operation */ 792355b4669Sjacobs switch (operation) { 793355b4669Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 79410144ea8Sjacobs if (submit_job(fd, queue, c, av[optind]) < 0) 795355b4669Sjacobs exit_code = errno; 796355b4669Sjacobs break; 797355b4669Sjacobs case OP_QUERY: /* send the query string, return the fd */ 79810144ea8Sjacobs if (query(fd, queue, ac - optind, &av[optind]) < 0) 799355b4669Sjacobs exit_code = errno; 800355b4669Sjacobs break; 801355b4669Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 80210144ea8Sjacobs if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 803355b4669Sjacobs exit_code = errno; 804355b4669Sjacobs break; 805355b4669Sjacobs default: /* This should never happen */ 806355b4669Sjacobs exit_code = EINVAL; 807355b4669Sjacobs } 808355b4669Sjacobs 809355b4669Sjacobs 810355b4669Sjacobs /* if the operation succeeded, send the fd to our parent */ 811355b4669Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 812355b4669Sjacobs char buf[BUFSIZ]; 813355b4669Sjacobs 814355b4669Sjacobs exit_code = errno; 815355b4669Sjacobs 816355b4669Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 817355b4669Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 818355b4669Sjacobs write(1, buf, c); 819355b4669Sjacobs } 820355b4669Sjacobs 821355b4669Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 822355b4669Sjacobs return (exit_code); 823355b4669Sjacobs } 824