1*2264Sjacobs /* 2*2264Sjacobs * CDDL HEADER START 3*2264Sjacobs * 4*2264Sjacobs * The contents of this file are subject to the terms of the 5*2264Sjacobs * Common Development and Distribution License (the "License"). 6*2264Sjacobs * You may not use this file except in compliance with the License. 7*2264Sjacobs * 8*2264Sjacobs * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*2264Sjacobs * or http://www.opensolaris.org/os/licensing. 10*2264Sjacobs * See the License for the specific language governing permissions 11*2264Sjacobs * and limitations under the License. 12*2264Sjacobs * 13*2264Sjacobs * When distributing Covered Code, include this CDDL HEADER in each 14*2264Sjacobs * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*2264Sjacobs * If applicable, add the following below this CDDL HEADER, with the 16*2264Sjacobs * fields enclosed by brackets "[]" replaced with your own identifying 17*2264Sjacobs * information: Portions Copyright [yyyy] [name of copyright owner] 18*2264Sjacobs * 19*2264Sjacobs * CDDL HEADER END 20*2264Sjacobs */ 21*2264Sjacobs 22*2264Sjacobs /* 23*2264Sjacobs * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*2264Sjacobs * Use is subject to license terms. 25*2264Sjacobs * 26*2264Sjacobs */ 27*2264Sjacobs 28*2264Sjacobs /* $Id: lpd-port.c 155 2006-04-26 02:34:54Z ktou $ */ 29*2264Sjacobs 30*2264Sjacobs #pragma ident "%Z%%M% %I% %E% SMI" 31*2264Sjacobs 32*2264Sjacobs #include <config-site.h> 33*2264Sjacobs 34*2264Sjacobs #include <stdio.h> 35*2264Sjacobs #include <stdlib.h> 36*2264Sjacobs #include <unistd.h> 37*2264Sjacobs #include <sys/types.h> 38*2264Sjacobs #include <sys/stat.h> 39*2264Sjacobs #include <fcntl.h> 40*2264Sjacobs #include <stdarg.h> 41*2264Sjacobs #include <string.h> 42*2264Sjacobs #include <signal.h> 43*2264Sjacobs #include <sys/socket.h> 44*2264Sjacobs #include <netinet/in.h> 45*2264Sjacobs #include <arpa/inet.h> 46*2264Sjacobs #include <netdb.h> 47*2264Sjacobs #include <errno.h> 48*2264Sjacobs #include <syslog.h> 49*2264Sjacobs #include <values.h> 50*2264Sjacobs #include <stropts.h> /* for sendfd */ 51*2264Sjacobs #include <sys/uio.h> /* for sendmsg stuff */ 52*2264Sjacobs #include <pwd.h> 53*2264Sjacobs #include <sys/sendfile.h> 54*2264Sjacobs #include <ctype.h> 55*2264Sjacobs #include <alloca.h> 56*2264Sjacobs #ifdef HAVE_PRIV_H 57*2264Sjacobs #include <priv.h> 58*2264Sjacobs #endif 59*2264Sjacobs #include <papi_impl.h> 60*2264Sjacobs 61*2264Sjacobs #ifndef JOB_ID_FILE 62*2264Sjacobs #define JOB_ID_FILE "/var/run/rfc-1179.seq" 63*2264Sjacobs #endif /* JOB_ID_FILE */ 64*2264Sjacobs 65*2264Sjacobs static int 66*2264Sjacobs sendfd(int sockfd, int fd) 67*2264Sjacobs { 68*2264Sjacobs syslog(LOG_DEBUG, "sendfd(%d, %d)", sockfd, fd); 69*2264Sjacobs 70*2264Sjacobs #if defined(sun) && defined(unix) && defined(I_SENDFD) 71*2264Sjacobs return (ioctl(sockfd, I_SENDFD, fd)); 72*2264Sjacobs #else 73*2264Sjacobs struct iovec iov[1]; 74*2264Sjacobs struct msghdr msg; 75*2264Sjacobs #ifdef CMSG_DATA 76*2264Sjacobs struct cmsghdr cmp[1]; 77*2264Sjacobs char buf[2]; /* send/recv 2 byte protocol */ 78*2264Sjacobs 79*2264Sjacobs iov[0].iov_base = buf; 80*2264Sjacobs iov[0].iov_len = 2; 81*2264Sjacobs 82*2264Sjacobs cmp[0].cmsg_level = SOL_SOCKET; 83*2264Sjacobs cmp[0].cmsg_type = SCM_RIGHTS; 84*2264Sjacobs cmp[0].cmsg_len = sizeof (struct cmsghdr) + sizeof (int); 85*2264Sjacobs * (int *)CMSG_DATA(cmp) = fd; 86*2264Sjacobs 87*2264Sjacobs buf[1] = 0; 88*2264Sjacobs buf[0] = 0; 89*2264Sjacobs msg.msg_control = cmp; 90*2264Sjacobs msg.msg_controllen = sizeof (struct cmsghdr) + sizeof (int); 91*2264Sjacobs #else 92*2264Sjacobs iov[0].iov_base = NULL; 93*2264Sjacobs iov[0].iov_len = 0; 94*2264Sjacobs msg.msg_accrights = (caddr_t)&fd; 95*2264Sjacobs msg.msg_accrights = sizeof (fd); 96*2264Sjacobs #endif 97*2264Sjacobs msg.msg_iov = iov; 98*2264Sjacobs msg.msg_iovlen = 1; 99*2264Sjacobs msg.msg_name = NULL; 100*2264Sjacobs msg.msg_namelen = 0; 101*2264Sjacobs 102*2264Sjacobs return (sendmsg(sockfd, &msg, 0)); 103*2264Sjacobs #endif 104*2264Sjacobs } 105*2264Sjacobs 106*2264Sjacobs static void 107*2264Sjacobs null(int i) 108*2264Sjacobs { 109*2264Sjacobs } 110*2264Sjacobs 111*2264Sjacobs static int 112*2264Sjacobs sock_connect(int sock, uri_t *uri, int timeout) 113*2264Sjacobs { 114*2264Sjacobs struct hostent *hp; 115*2264Sjacobs struct servent *sp; 116*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 117*2264Sjacobs struct sockaddr_in6 sin; 118*2264Sjacobs #else 119*2264Sjacobs struct sockaddr_in sin; 120*2264Sjacobs #endif 121*2264Sjacobs static void (*old_handler)(); 122*2264Sjacobs int err, 123*2264Sjacobs error_num; 124*2264Sjacobs unsigned timo = 1; 125*2264Sjacobs int port = -1; 126*2264Sjacobs 127*2264Sjacobs 128*2264Sjacobs /* 129*2264Sjacobs * Get the host address and port number to connect to. 130*2264Sjacobs */ 131*2264Sjacobs if ((uri == NULL) || (uri->host == NULL)) { 132*2264Sjacobs return (-1); 133*2264Sjacobs } 134*2264Sjacobs 135*2264Sjacobs /* linux style NULL usage */ 136*2264Sjacobs (void) memset((char *)&sin, (int)NULL, sizeof (sin)); 137*2264Sjacobs 138*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 139*2264Sjacobs if ((hp = getipnodebyname(uri->host, AF_INET6, AI_DEFAULT, 140*2264Sjacobs &error_num)) == NULL) { 141*2264Sjacobs errno = ENOENT; 142*2264Sjacobs return (-1); 143*2264Sjacobs } 144*2264Sjacobs (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); 145*2264Sjacobs sin.sin6_family = hp->h_addrtype; 146*2264Sjacobs #else 147*2264Sjacobs if ((hp = gethostbyname(uri->host)) == NULL) { 148*2264Sjacobs errno = ENOENT; 149*2264Sjacobs return (-1); 150*2264Sjacobs } 151*2264Sjacobs 152*2264Sjacobs (void) memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length); 153*2264Sjacobs sin.sin_family = hp->h_addrtype; 154*2264Sjacobs #endif 155*2264Sjacobs 156*2264Sjacobs if ((sp = getservbyname("printer", "tcp")) == NULL) { 157*2264Sjacobs errno = ENOENT; 158*2264Sjacobs return (-1); 159*2264Sjacobs } 160*2264Sjacobs 161*2264Sjacobs if (uri->port != NULL) 162*2264Sjacobs port = atoi(uri->port); 163*2264Sjacobs if (port < 0) 164*2264Sjacobs port = sp->s_port; 165*2264Sjacobs 166*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 167*2264Sjacobs sin.sin6_port = port; 168*2264Sjacobs #else 169*2264Sjacobs sin.sin_port = port; 170*2264Sjacobs #endif 171*2264Sjacobs 172*2264Sjacobs retry: 173*2264Sjacobs old_handler = signal(SIGALRM, null); 174*2264Sjacobs (void) alarm(timeout); 175*2264Sjacobs 176*2264Sjacobs if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 177*2264Sjacobs (void) alarm(0); 178*2264Sjacobs (void) signal(SIGALRM, old_handler); 179*2264Sjacobs 180*2264Sjacobs if (errno == ECONNREFUSED && timo <= 16) { 181*2264Sjacobs (void) sleep(timo); 182*2264Sjacobs timo *= 2; 183*2264Sjacobs goto retry; 184*2264Sjacobs } 185*2264Sjacobs 186*2264Sjacobs return (-1); 187*2264Sjacobs } 188*2264Sjacobs 189*2264Sjacobs (void) alarm(0); 190*2264Sjacobs (void) signal(SIGALRM, old_handler); 191*2264Sjacobs return (sock); 192*2264Sjacobs } 193*2264Sjacobs 194*2264Sjacobs static int 195*2264Sjacobs next_job_id() 196*2264Sjacobs { 197*2264Sjacobs int fd, result = getpid() % 1000; 198*2264Sjacobs 199*2264Sjacobs /* gain back enough privilege to open the id file */ 200*2264Sjacobs #ifdef PRIV_ALLSETS 201*2264Sjacobs if ((priv_set(PRIV_ON, PRIV_EFFECTIVE, 202*2264Sjacobs PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL)) < 0) { 203*2264Sjacobs syslog(LOG_ERR, "lpd_port:next_job_id:priv_set fails: : %m"); 204*2264Sjacobs return (-1); 205*2264Sjacobs } 206*2264Sjacobs #else 207*2264Sjacobs seteuid(0); 208*2264Sjacobs #endif 209*2264Sjacobs 210*2264Sjacobs /* open the sequence file */ 211*2264Sjacobs if (((fd = open(JOB_ID_FILE, O_RDWR)) < 0) && (errno == ENOENT)) 212*2264Sjacobs fd = open(JOB_ID_FILE, O_CREAT|O_EXCL|O_RDWR, 0644); 213*2264Sjacobs 214*2264Sjacobs syslog(LOG_DEBUG, "sequence file fd: %d", fd); 215*2264Sjacobs 216*2264Sjacobs /* drop our privilege again */ 217*2264Sjacobs #ifdef PRIV_ALLSETS 218*2264Sjacobs /* drop file access privilege */ 219*2264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, 220*2264Sjacobs PRIV_FILE_DAC_READ, PRIV_FILE_DAC_WRITE, NULL); 221*2264Sjacobs #else 222*2264Sjacobs seteuid(getuid()); 223*2264Sjacobs #endif 224*2264Sjacobs 225*2264Sjacobs if (fd >= 0) { 226*2264Sjacobs /* wait for a lock on the file */ 227*2264Sjacobs if (lockf(fd, F_LOCK, 0) == 0) { 228*2264Sjacobs char buf[8]; 229*2264Sjacobs int next; 230*2264Sjacobs 231*2264Sjacobs /* get the current id */ 232*2264Sjacobs (void) memset(buf, 0, sizeof (buf)); 233*2264Sjacobs if (read(fd, buf, sizeof (buf)) > 0) 234*2264Sjacobs result = atoi(buf); 235*2264Sjacobs 236*2264Sjacobs next = ((result < 999) ? (result + 1) : 0); 237*2264Sjacobs 238*2264Sjacobs /* store the next id in the file */ 239*2264Sjacobs snprintf(buf, sizeof (buf), "%.3d", next); 240*2264Sjacobs if ((lseek(fd, 0, SEEK_SET) == 0) && 241*2264Sjacobs (ftruncate(fd, 0) == 0)) 242*2264Sjacobs write(fd, buf, strlen(buf)); 243*2264Sjacobs } 244*2264Sjacobs } 245*2264Sjacobs syslog(LOG_DEBUG, "next_job_id() is %d", result); 246*2264Sjacobs 247*2264Sjacobs return (result); 248*2264Sjacobs } 249*2264Sjacobs 250*2264Sjacobs static int 251*2264Sjacobs reserved_port() 252*2264Sjacobs { 253*2264Sjacobs int result = -1; 254*2264Sjacobs int port; 255*2264Sjacobs 256*2264Sjacobs /* gain back enough privilege to open a reserved port */ 257*2264Sjacobs #ifdef PRIV_ALLSETS 258*2264Sjacobs if ((priv_set( 259*2264Sjacobs PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 260*2264Sjacobs syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 261*2264Sjacobs return (-1); 262*2264Sjacobs } 263*2264Sjacobs #else 264*2264Sjacobs seteuid(0); 265*2264Sjacobs #endif 266*2264Sjacobs 267*2264Sjacobs #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 268*2264Sjacobs port = 0; /* set to 0, rresvport_af() will find us one. */ 269*2264Sjacobs result = rresvport_af(&port, AF_INET6); 270*2264Sjacobs #else 271*2264Sjacobs port = IPPORT_RESERVED - 1; 272*2264Sjacobs while (((result = rresvport(&port)) < 0) && (port >= 0)) 273*2264Sjacobs port--; 274*2264Sjacobs #endif 275*2264Sjacobs 276*2264Sjacobs /* drop our privilege again */ 277*2264Sjacobs #ifdef PRIV_ALLSETS 278*2264Sjacobs priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 279*2264Sjacobs #else 280*2264Sjacobs seteuid(getuid()); 281*2264Sjacobs #endif 282*2264Sjacobs 283*2264Sjacobs return (result); 284*2264Sjacobs } 285*2264Sjacobs 286*2264Sjacobs static char * 287*2264Sjacobs get_user_name() 288*2264Sjacobs { 289*2264Sjacobs static struct passwd *p = NULL; 290*2264Sjacobs 291*2264Sjacobs if ((p = getpwuid(getuid())) != NULL) 292*2264Sjacobs return (p->pw_name); 293*2264Sjacobs else 294*2264Sjacobs return ("unknown"); 295*2264Sjacobs } 296*2264Sjacobs 297*2264Sjacobs static void 298*2264Sjacobs add_args(int ac, char **av, char *buf, size_t len) 299*2264Sjacobs { 300*2264Sjacobs while (ac--) { 301*2264Sjacobs strlcat(buf, " ", len); 302*2264Sjacobs strlcat(buf, *(av++), len); 303*2264Sjacobs } 304*2264Sjacobs } 305*2264Sjacobs 306*2264Sjacobs static int 307*2264Sjacobs massage_control_data(char *data, int id) 308*2264Sjacobs { 309*2264Sjacobs char *line, *iter = NULL; 310*2264Sjacobs char *ptr; 311*2264Sjacobs char host[BUFSIZ]; 312*2264Sjacobs 313*2264Sjacobs gethostname(host, sizeof (host)); 314*2264Sjacobs 315*2264Sjacobs for (ptr = strchr(data, '\n'); ptr != NULL; ptr = strchr(ptr, '\n')) { 316*2264Sjacobs ptr++; 317*2264Sjacobs 318*2264Sjacobs if (ptr[0] == 'H') { 319*2264Sjacobs if (strncmp(++ptr, host, strlen(host)) != 0) 320*2264Sjacobs return (-1); 321*2264Sjacobs } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 322*2264Sjacobs /* check the user name */ 323*2264Sjacobs uid_t uid = getuid(); 324*2264Sjacobs struct passwd *pw; 325*2264Sjacobs int len; 326*2264Sjacobs 327*2264Sjacobs if (uid == 0) /* let root do what they want */ 328*2264Sjacobs continue; 329*2264Sjacobs if ((pw = getpwuid(uid)) == NULL) 330*2264Sjacobs return (-1); /* failed */ 331*2264Sjacobs len = strlen(pw->pw_name); 332*2264Sjacobs if ((strncmp(++ptr, pw->pw_name, len) != 0) || 333*2264Sjacobs (ptr[len] != '\n')) 334*2264Sjacobs return (-1); /* failed */ 335*2264Sjacobs } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 336*2264Sjacobs /* check/fix df?XXXhostname */ 337*2264Sjacobs ptr++; 338*2264Sjacobs 339*2264Sjacobs if (strlen(ptr) < 6) 340*2264Sjacobs return (-1); 341*2264Sjacobs if ((ptr[0] == 'd') && (ptr[1] == 'f') && 342*2264Sjacobs (ptr[3] == 'X') && (ptr[4] == 'X') && 343*2264Sjacobs (ptr[5] == 'X')) { 344*2264Sjacobs ptr[3] = '0' + (id / 100) % 10; 345*2264Sjacobs ptr[4] = '0' + (id / 10) % 10; 346*2264Sjacobs ptr[5] = '0' + id % 10; 347*2264Sjacobs 348*2264Sjacobs if (strncmp(&ptr[6], host, strlen(host)) != 0) 349*2264Sjacobs return (-1); 350*2264Sjacobs } else 351*2264Sjacobs return (-1); 352*2264Sjacobs } 353*2264Sjacobs } 354*2264Sjacobs return (1); 355*2264Sjacobs } 356*2264Sjacobs 357*2264Sjacobs static int 358*2264Sjacobs send_lpd_message(int fd, char *fmt, ...) 359*2264Sjacobs { 360*2264Sjacobs char buf[BUFSIZ]; 361*2264Sjacobs size_t size; 362*2264Sjacobs va_list ap; 363*2264Sjacobs 364*2264Sjacobs va_start(ap, fmt); 365*2264Sjacobs size = vsnprintf(buf, sizeof (buf), fmt, ap); 366*2264Sjacobs va_end(ap); 367*2264Sjacobs if (size == 0) 368*2264Sjacobs size = 1; 369*2264Sjacobs 370*2264Sjacobs syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 371*2264Sjacobs 372*2264Sjacobs if (write(fd, buf, size) != size) { 373*2264Sjacobs errno = EIO; 374*2264Sjacobs return (-1); 375*2264Sjacobs } 376*2264Sjacobs 377*2264Sjacobs if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 378*2264Sjacobs return (-1); 379*2264Sjacobs 380*2264Sjacobs return (0); 381*2264Sjacobs } 382*2264Sjacobs 383*2264Sjacobs static int 384*2264Sjacobs send_data_file(int sock, char *dfname, char *name) 385*2264Sjacobs { 386*2264Sjacobs size_t len; 387*2264Sjacobs off_t off = 0; 388*2264Sjacobs struct stat st; 389*2264Sjacobs char buf[32]; 390*2264Sjacobs int fd = -1; 391*2264Sjacobs 392*2264Sjacobs if (strcmp(name, "standard input") != 0) { 393*2264Sjacobs if ((fd = open(name, O_RDONLY)) < 0) 394*2264Sjacobs return (-1); 395*2264Sjacobs 396*2264Sjacobs if (fstat(fd, &st) < 0) 397*2264Sjacobs return (-1); 398*2264Sjacobs } else 399*2264Sjacobs st.st_size = MAXINT; /* should be 0 */ 400*2264Sjacobs 401*2264Sjacobs /* request data file transfer, read ack/nack */ 402*2264Sjacobs errno = ENOSPC; 403*2264Sjacobs if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 404*2264Sjacobs return (-1); 405*2264Sjacobs 406*2264Sjacobs if (fd != -1) { 407*2264Sjacobs /* write the data */ 408*2264Sjacobs if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 409*2264Sjacobs return (-1); 410*2264Sjacobs close(fd); 411*2264Sjacobs 412*2264Sjacobs /* request ack/nack after the data transfer */ 413*2264Sjacobs errno = EIO; 414*2264Sjacobs if (send_lpd_message(sock, "") < 0) 415*2264Sjacobs return (-1); 416*2264Sjacobs } 417*2264Sjacobs 418*2264Sjacobs return (0); 419*2264Sjacobs } 420*2264Sjacobs 421*2264Sjacobs static int 422*2264Sjacobs send_control_file(int sock, char *data, int id) 423*2264Sjacobs { 424*2264Sjacobs int len; 425*2264Sjacobs char buf[BUFSIZ]; 426*2264Sjacobs char *host = "localhost"; 427*2264Sjacobs 428*2264Sjacobs len = strlen(data); 429*2264Sjacobs 430*2264Sjacobs /* request data file transfer, read ack/nack */ 431*2264Sjacobs errno = ENOSPC; 432*2264Sjacobs if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 433*2264Sjacobs return (-1); 434*2264Sjacobs 435*2264Sjacobs /* write the data */ 436*2264Sjacobs if (write(sock, data, len) != len) 437*2264Sjacobs return (-1); 438*2264Sjacobs 439*2264Sjacobs /* request ack/nack after the data transfer */ 440*2264Sjacobs errno = EIO; 441*2264Sjacobs if (send_lpd_message(sock, "") < 0) 442*2264Sjacobs return (-1); 443*2264Sjacobs 444*2264Sjacobs return (0); 445*2264Sjacobs } 446*2264Sjacobs 447*2264Sjacobs 448*2264Sjacobs static int 449*2264Sjacobs submit_job(int sock, uri_t *uri, int job_id, char *path) 450*2264Sjacobs { 451*2264Sjacobs int current = 0; 452*2264Sjacobs off_t off = 0; 453*2264Sjacobs char *metadata = NULL; 454*2264Sjacobs char *ptr, *iter = NULL; 455*2264Sjacobs int fd, err; 456*2264Sjacobs int sent_files = 0; 457*2264Sjacobs char buf[BUFSIZ]; 458*2264Sjacobs size_t len; 459*2264Sjacobs char *printer = queue_name_from_uri(uri); 460*2264Sjacobs 461*2264Sjacobs /* read in the control file */ 462*2264Sjacobs if ((fd = open(path, O_RDONLY)) >= 0) { 463*2264Sjacobs struct stat st; 464*2264Sjacobs 465*2264Sjacobs if (fstat(fd, &st) < 0) { 466*2264Sjacobs close(fd); 467*2264Sjacobs return (-1); 468*2264Sjacobs } 469*2264Sjacobs 470*2264Sjacobs metadata = alloca(st.st_size + 1); 471*2264Sjacobs memset(metadata, 0, st.st_size + 1); 472*2264Sjacobs 473*2264Sjacobs if (read(fd, metadata, st.st_size) != st.st_size) { 474*2264Sjacobs close(fd); 475*2264Sjacobs free(metadata); 476*2264Sjacobs metadata = NULL; 477*2264Sjacobs return (-1); 478*2264Sjacobs } 479*2264Sjacobs 480*2264Sjacobs } else { 481*2264Sjacobs syslog(LOG_ERR, 482*2264Sjacobs "lpd-port:submit_job:open failed : %m path %s", path); 483*2264Sjacobs return (-1); 484*2264Sjacobs } 485*2264Sjacobs 486*2264Sjacobs /* massage the control file */ 487*2264Sjacobs if (massage_control_data(metadata, job_id) < 0) { 488*2264Sjacobs /* bad control data, dump the job */ 489*2264Sjacobs syslog(LOG_ALERT, 490*2264Sjacobs "bad control file, possible subversion attempt"); 491*2264Sjacobs } 492*2264Sjacobs 493*2264Sjacobs /* request to transfer the job */ 494*2264Sjacobs if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 495*2264Sjacobs /* no such (or disabled) queue, got to love rfc-1179 */ 496*2264Sjacobs errno = ENOENT; 497*2264Sjacobs return (-1); 498*2264Sjacobs } 499*2264Sjacobs 500*2264Sjacobs /* send the control data */ 501*2264Sjacobs if (send_control_file(sock, metadata, job_id) < 0) { 502*2264Sjacobs err = errno; 503*2264Sjacobs write(sock, "\001\n", 2); /* abort */ 504*2264Sjacobs errno = err; 505*2264Sjacobs return (-1); 506*2264Sjacobs } 507*2264Sjacobs 508*2264Sjacobs /* walk the control file sending the data files */ 509*2264Sjacobs for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 510*2264Sjacobs ptr = strtok_r(NULL, "\n", &iter)) { 511*2264Sjacobs char *name = NULL; 512*2264Sjacobs 513*2264Sjacobs if (ptr[0] != 'U') 514*2264Sjacobs continue; 515*2264Sjacobs 516*2264Sjacobs name = strtok_r(NULL, "\n", &iter); 517*2264Sjacobs if (name[0] != 'N') 518*2264Sjacobs continue; 519*2264Sjacobs 520*2264Sjacobs ptr++; 521*2264Sjacobs name++; 522*2264Sjacobs 523*2264Sjacobs if (send_data_file(sock, ptr, name) < 0) { 524*2264Sjacobs err = errno; 525*2264Sjacobs write(sock, "\001\n", 2); /* abort */ 526*2264Sjacobs errno = err; 527*2264Sjacobs return (-1); 528*2264Sjacobs } 529*2264Sjacobs if (strcmp(name, "standard input") != 0) 530*2264Sjacobs sent_files++; 531*2264Sjacobs } 532*2264Sjacobs 533*2264Sjacobs /* write back the job-id */ 534*2264Sjacobs err = errno; 535*2264Sjacobs if ((fd = open(path, O_WRONLY)) >= 0) { 536*2264Sjacobs ftruncate(fd, 0); 537*2264Sjacobs write(fd, &job_id, sizeof (job_id)); 538*2264Sjacobs close(fd); 539*2264Sjacobs } 540*2264Sjacobs errno = err; 541*2264Sjacobs 542*2264Sjacobs if (sent_files != 0) { 543*2264Sjacobs err = errno; 544*2264Sjacobs close(sock); 545*2264Sjacobs errno = err; 546*2264Sjacobs } 547*2264Sjacobs 548*2264Sjacobs return (0); 549*2264Sjacobs } 550*2264Sjacobs 551*2264Sjacobs static int 552*2264Sjacobs query(int fd, uri_t *uri, int ac, char **av) 553*2264Sjacobs { 554*2264Sjacobs char buf[BUFSIZ]; 555*2264Sjacobs int rc, len; 556*2264Sjacobs char *printer = queue_name_from_uri(uri); 557*2264Sjacobs 558*2264Sjacobs /* build the request */ 559*2264Sjacobs snprintf(buf, sizeof (buf), "\04%s", printer); 560*2264Sjacobs add_args(ac, av, buf, sizeof (buf)); 561*2264Sjacobs strlcat(buf, "\n", sizeof (buf)); 562*2264Sjacobs len = strlen(buf); 563*2264Sjacobs 564*2264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 565*2264Sjacobs errno = EMSGSIZE; 566*2264Sjacobs rc = -1; 567*2264Sjacobs } else 568*2264Sjacobs rc = 0; 569*2264Sjacobs 570*2264Sjacobs return (rc); 571*2264Sjacobs } 572*2264Sjacobs 573*2264Sjacobs static int 574*2264Sjacobs cancel(int fd, uri_t *uri, int ac, char **av) 575*2264Sjacobs { 576*2264Sjacobs char buf[BUFSIZ]; 577*2264Sjacobs int rc, len; 578*2264Sjacobs char *printer = queue_name_from_uri(uri); 579*2264Sjacobs 580*2264Sjacobs /* build the request */ 581*2264Sjacobs snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 582*2264Sjacobs add_args(ac, av, buf, sizeof (buf)); 583*2264Sjacobs strlcat(buf, "\n", sizeof (buf)); 584*2264Sjacobs len = strlen(buf); 585*2264Sjacobs 586*2264Sjacobs if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 587*2264Sjacobs errno = EMSGSIZE; 588*2264Sjacobs rc = -1; 589*2264Sjacobs } else 590*2264Sjacobs rc = 0; 591*2264Sjacobs 592*2264Sjacobs return (rc); 593*2264Sjacobs } 594*2264Sjacobs 595*2264Sjacobs static void 596*2264Sjacobs usage(char *program) 597*2264Sjacobs { 598*2264Sjacobs char *name; 599*2264Sjacobs 600*2264Sjacobs setreuid(getuid(), getuid()); 601*2264Sjacobs 602*2264Sjacobs if ((name = strrchr(program, '/')) == NULL) 603*2264Sjacobs name = program; 604*2264Sjacobs else 605*2264Sjacobs name++; 606*2264Sjacobs 607*2264Sjacobs fprintf(stderr, "usage:\t%s -u uri [-t timeout] " 608*2264Sjacobs "[-s control ]\n", name); 609*2264Sjacobs fprintf(stderr, "\t%s -u uri [-t timeout] " 610*2264Sjacobs "[-c user|job ...]\n", name); 611*2264Sjacobs fprintf(stderr, "\t%s -u uri [-t timeout] " 612*2264Sjacobs "[-q user|job ...]\n", name); 613*2264Sjacobs exit(EINVAL); 614*2264Sjacobs } 615*2264Sjacobs 616*2264Sjacobs /* 617*2264Sjacobs * The main program temporarily loses privilege while searching the command 618*2264Sjacobs * line arguments. It then allocates any resources it need privilege for 619*2264Sjacobs * job-id, reserved port. Once it has the resources it needs, it perminently 620*2264Sjacobs * drops all elevated privilege. It ghen connects to the remote print service 621*2264Sjacobs * based on destination hostname. Doing it this way reduces the potenential 622*2264Sjacobs * opportunity for a breakout with elevated privilege, breakout with an 623*2264Sjacobs * unconnected reserved port, and exploitation of the remote print service 624*2264Sjacobs * by a calling program. 625*2264Sjacobs */ 626*2264Sjacobs int 627*2264Sjacobs main(int ac, char *av[]) 628*2264Sjacobs { 629*2264Sjacobs enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 630*2264Sjacobs int fd, c, timeout = 0, exit_code = 0; 631*2264Sjacobs uri_t *uri = NULL; 632*2264Sjacobs uid_t uid = getuid(); 633*2264Sjacobs #ifdef PRIV_ALLSETS 634*2264Sjacobs priv_set_t *saveset = NULL; 635*2264Sjacobs #endif 636*2264Sjacobs 637*2264Sjacobs openlog("lpd-port", LOG_PID, LOG_LPR); 638*2264Sjacobs 639*2264Sjacobs #ifdef PRIV_ALLSETS 640*2264Sjacobs 641*2264Sjacobs /* lose as much as we can perminently and temporarily drop the rest. */ 642*2264Sjacobs 643*2264Sjacobs if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 644*2264Sjacobs "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 645*2264Sjacobs ",", (const char **)NULL)) == NULL) { 646*2264Sjacobs syslog(LOG_ERR, 647*2264Sjacobs "lpd_port: priv_str_to_set saveset failed: %m\n"); 648*2264Sjacobs return (-1); 649*2264Sjacobs } 650*2264Sjacobs 651*2264Sjacobs if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 652*2264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 653*2264Sjacobs return (-1); 654*2264Sjacobs } 655*2264Sjacobs 656*2264Sjacobs /* 657*2264Sjacobs * These privileges permanently dropped in next_job_id() and 658*2264Sjacobs * reserved_port() 659*2264Sjacobs */ 660*2264Sjacobs 661*2264Sjacobs if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 662*2264Sjacobs syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 663*2264Sjacobs return (-1); 664*2264Sjacobs } 665*2264Sjacobs 666*2264Sjacobs priv_freeset(saveset); 667*2264Sjacobs 668*2264Sjacobs syslog(LOG_DEBUG, "using privs"); 669*2264Sjacobs #else 670*2264Sjacobs 671*2264Sjacobs syslog(LOG_DEBUG, "no privs"); 672*2264Sjacobs seteuid(uid); 673*2264Sjacobs #endif 674*2264Sjacobs 675*2264Sjacobs 676*2264Sjacobs while ((c = getopt(ac, av, "cqst:u:")) != EOF) { 677*2264Sjacobs switch (c) { 678*2264Sjacobs case 'c': 679*2264Sjacobs if (operation != OP_NONE) 680*2264Sjacobs usage(av[0]); 681*2264Sjacobs operation = OP_CANCEL; 682*2264Sjacobs break; 683*2264Sjacobs case 'q': 684*2264Sjacobs if (operation != OP_NONE) 685*2264Sjacobs usage(av[0]); 686*2264Sjacobs operation = OP_QUERY; 687*2264Sjacobs break; 688*2264Sjacobs case 's': 689*2264Sjacobs if (operation != OP_NONE) 690*2264Sjacobs usage(av[0]); 691*2264Sjacobs operation = OP_SUBMIT; 692*2264Sjacobs break; 693*2264Sjacobs case 't': 694*2264Sjacobs timeout = atoi(optarg); 695*2264Sjacobs break; 696*2264Sjacobs case 'u': 697*2264Sjacobs if (uri_from_string(optarg, &uri) < 0) 698*2264Sjacobs usage(av[0]); 699*2264Sjacobs break; 700*2264Sjacobs default: 701*2264Sjacobs usage(av[0]); 702*2264Sjacobs /* does not return */ 703*2264Sjacobs } 704*2264Sjacobs } 705*2264Sjacobs 706*2264Sjacobs if ((uri == NULL) || (timeout < 0) || (operation == OP_NONE)) 707*2264Sjacobs usage(av[0]); 708*2264Sjacobs 709*2264Sjacobs if ((strcasecmp(uri->scheme, "lpd") != 0) && 710*2264Sjacobs (strcasecmp(uri->scheme, "rfc-1179") != 0)) 711*2264Sjacobs usage(av[0]); 712*2264Sjacobs 713*2264Sjacobs if (operation == OP_SUBMIT) /* get a job-id if we need it */ 714*2264Sjacobs if ((c = next_job_id()) < 0) { 715*2264Sjacobs syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 716*2264Sjacobs return (-1); 717*2264Sjacobs } 718*2264Sjacobs 719*2264Sjacobs if ((fd = reserved_port()) < 0) { 720*2264Sjacobs syslog(LOG_ERR, "reserved_port() failed %m"); 721*2264Sjacobs return (errno); 722*2264Sjacobs } 723*2264Sjacobs 724*2264Sjacobs /* 725*2264Sjacobs * we no longer want or need any elevated privilege, lose it all 726*2264Sjacobs * permanently. 727*2264Sjacobs */ 728*2264Sjacobs 729*2264Sjacobs setreuid(uid, uid); 730*2264Sjacobs 731*2264Sjacobs 732*2264Sjacobs /* connect to the print service */ 733*2264Sjacobs if ((fd = sock_connect(fd, uri, timeout)) < 0) 734*2264Sjacobs return (errno); 735*2264Sjacobs 736*2264Sjacobs /* perform the requested operation */ 737*2264Sjacobs switch (operation) { 738*2264Sjacobs case OP_SUBMIT: /* transfer the job, close the fd */ 739*2264Sjacobs if (submit_job(fd, uri, c, av[optind]) < 0) 740*2264Sjacobs exit_code = errno; 741*2264Sjacobs break; 742*2264Sjacobs case OP_QUERY: /* send the query string, return the fd */ 743*2264Sjacobs if (query(fd, uri, ac - optind, &av[optind]) < 0) 744*2264Sjacobs exit_code = errno; 745*2264Sjacobs break; 746*2264Sjacobs case OP_CANCEL: /* send the cancel string, return the fd */ 747*2264Sjacobs if (cancel(fd, uri, ac - optind, &av[optind]) < 0) 748*2264Sjacobs exit_code = errno; 749*2264Sjacobs break; 750*2264Sjacobs default: /* This should never happen */ 751*2264Sjacobs exit_code = EINVAL; 752*2264Sjacobs } 753*2264Sjacobs 754*2264Sjacobs 755*2264Sjacobs /* if the operation succeeded, send the fd to our parent */ 756*2264Sjacobs if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 757*2264Sjacobs char buf[BUFSIZ]; 758*2264Sjacobs 759*2264Sjacobs exit_code = errno; 760*2264Sjacobs 761*2264Sjacobs /* sendfd() failed, dump the socket data for the heck of it */ 762*2264Sjacobs while ((c = read(fd, buf, sizeof (buf))) > 0) 763*2264Sjacobs write(1, buf, c); 764*2264Sjacobs } 765*2264Sjacobs 766*2264Sjacobs syslog(LOG_DEBUG, "exit code: %d", exit_code); 767*2264Sjacobs return (exit_code); 768*2264Sjacobs } 769