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