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