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