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 2008 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 close(fd); 236 } 237 syslog(LOG_DEBUG, "next_job_id() is %d", result); 238 239 return (result); 240 } 241 242 static int 243 reserved_port() 244 { 245 int result = -1; 246 int port; 247 248 /* gain back enough privilege to open a reserved port */ 249 #ifdef PRIV_ALLSETS 250 if ((priv_set( 251 PRIV_ON, PRIV_EFFECTIVE, PRIV_NET_PRIVADDR, NULL)) != 0) { 252 syslog(LOG_ERR, "priv_set fails for net_privaddr %m"); 253 return (-1); 254 } 255 #else 256 seteuid(0); 257 #endif 258 259 #if defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_RRESVPORT_AF) 260 port = 0; /* set to 0, rresvport_af() will find us one. */ 261 result = rresvport_af(&port, AF_INET6); 262 #else 263 port = IPPORT_RESERVED - 1; 264 while (((result = rresvport(&port)) < 0) && (port >= 0)) 265 port--; 266 #endif 267 268 /* drop our privilege again */ 269 #ifdef PRIV_ALLSETS 270 priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_NET_PRIVADDR, NULL); 271 #else 272 seteuid(getuid()); 273 #endif 274 275 return (result); 276 } 277 278 static char * 279 get_user_name() 280 { 281 static struct passwd *p = NULL; 282 283 if ((p = getpwuid(getuid())) != NULL) 284 return (p->pw_name); 285 else 286 return ("unknown"); 287 } 288 289 static void 290 add_args(int ac, char **av, char *buf, size_t len) 291 { 292 while (ac--) { 293 strlcat(buf, " ", len); 294 strlcat(buf, *(av++), len); 295 } 296 } 297 298 static int 299 massage_control_data(char *data, int id) 300 { 301 char *line, *iter = NULL; 302 char *ptr; 303 char host[BUFSIZ]; 304 305 gethostname(host, sizeof (host)); 306 307 for (ptr = strchr(data, '\n'); ptr != NULL; ptr = strchr(ptr, '\n')) { 308 ptr++; 309 310 if (ptr[0] == 'H') { 311 if (strncmp(++ptr, host, strlen(host)) != 0) 312 return (-1); 313 } else if ((ptr[0] == 'P') || (ptr[0] == 'L')) { 314 /* check the user name */ 315 uid_t uid = getuid(); 316 struct passwd *pw; 317 int len; 318 319 if (uid == 0) /* let root do what they want */ 320 continue; 321 if ((pw = getpwuid(uid)) == NULL) 322 return (-1); /* failed */ 323 len = strlen(pw->pw_name); 324 if ((strncmp(++ptr, pw->pw_name, len) != 0) || 325 (ptr[len] != '\n')) 326 return (-1); /* failed */ 327 } else if ((islower(ptr[0]) != 0) || (ptr[0] == 'U')) { 328 /* check/fix df?XXXhostname */ 329 ptr++; 330 331 if (strlen(ptr) < 6) 332 return (-1); 333 if ((ptr[0] == 'd') && (ptr[1] == 'f') && 334 (ptr[3] == 'X') && (ptr[4] == 'X') && 335 (ptr[5] == 'X')) { 336 ptr[3] = '0' + (id / 100) % 10; 337 ptr[4] = '0' + (id / 10) % 10; 338 ptr[5] = '0' + id % 10; 339 340 if (strncmp(&ptr[6], host, strlen(host)) != 0) 341 return (-1); 342 } else 343 return (-1); 344 } 345 } 346 return (1); 347 } 348 349 static int 350 send_lpd_message(int fd, char *fmt, ...) 351 { 352 char buf[BUFSIZ]; 353 size_t size; 354 va_list ap; 355 356 va_start(ap, fmt); 357 size = vsnprintf(buf, sizeof (buf), fmt, ap); 358 va_end(ap); 359 if (size == 0) 360 size = 1; 361 362 syslog(LOG_DEBUG, "lpd_messsage(%d, %s)", fd, buf); 363 364 if (write(fd, buf, size) != size) { 365 errno = EIO; 366 return (-1); 367 } 368 369 if ((read(fd, buf, 1) != 1) || (buf[0] != 0)) 370 return (-1); 371 372 return (0); 373 } 374 375 static int 376 send_data_file(int sock, char *dfname, char *name) 377 { 378 size_t len; 379 off_t off = 0; 380 struct stat st; 381 char buf[32]; 382 int fd = -1; 383 384 if (strcmp(name, "standard input") != 0) { 385 if ((fd = open(name, O_RDONLY)) < 0) 386 return (-1); 387 388 if (fstat(fd, &st) < 0) 389 return (-1); 390 } else 391 st.st_size = MAXINT; /* should be 0 */ 392 393 /* request data file transfer, read ack/nack */ 394 errno = ENOSPC; 395 if (send_lpd_message(sock, "\003%d %s\n", st.st_size, dfname) < 0) 396 return (-1); 397 398 if (fd != -1) { 399 /* write the data */ 400 if (sendfile(sock, fd, &off, st.st_size) != st.st_size) 401 return (-1); 402 close(fd); 403 404 /* request ack/nack after the data transfer */ 405 errno = EIO; 406 if (send_lpd_message(sock, "") < 0) 407 return (-1); 408 } 409 410 return (0); 411 } 412 413 static int 414 send_control_file(int sock, char *data, int id) 415 { 416 int len; 417 char buf[BUFSIZ]; 418 char *host = "localhost"; 419 420 len = strlen(data); 421 422 /* request data file transfer, read ack/nack */ 423 errno = ENOSPC; 424 if (send_lpd_message(sock, "\002%d cfA%.3d%s\n", len, id, host) < 0) 425 return (-1); 426 427 /* write the data */ 428 if (write(sock, data, len) != len) 429 return (-1); 430 431 /* request ack/nack after the data transfer */ 432 errno = EIO; 433 if (send_lpd_message(sock, "") < 0) 434 return (-1); 435 436 return (0); 437 } 438 439 440 static int 441 submit_job(int sock, char *printer, int job_id, char *path) 442 { 443 struct stat st; 444 int current = 0; 445 off_t off = 0; 446 char *metadata = NULL; 447 char *ptr, *iter = NULL; 448 int fd, err; 449 int sent_files = 0; 450 char buf[BUFSIZ]; 451 size_t len; 452 453 /* open the control file */ 454 if ((fd = open(path, O_RDONLY)) < 0) { 455 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): open(): %m", 456 sock, printer, job_id, path); 457 return (-1); 458 } 459 460 /* get the size of the control file */ 461 if (fstat(fd, &st) < 0) { 462 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): fstat(): %m", 463 sock, printer, job_id, path); 464 close(fd); 465 return (-1); 466 } 467 468 /* allocate memory for the control file */ 469 if ((metadata = calloc(1, st.st_size + 1)) == NULL) { 470 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): calloc(): %m", 471 sock, printer, job_id, path); 472 close(fd); 473 return (-1); 474 } 475 476 /* read in the control file */ 477 if (read(fd, metadata, st.st_size) != st.st_size) { 478 syslog(LOG_ERR, "submit_job(%d, %s, %d, %s): read(): %m", 479 sock, printer, job_id, path); 480 free(metadata); 481 close(fd); 482 return (-1); 483 } 484 485 /* massage the control file */ 486 if (massage_control_data(metadata, job_id) < 0) { 487 /* bad control data, dump the job */ 488 syslog(LOG_ALERT, 489 "bad control file, possible subversion attempt"); 490 free(metadata); 491 close(fd); 492 return (-1); 493 } 494 495 /* request to transfer the job */ 496 if (send_lpd_message(sock, "\002%s\n", printer) < 0) { 497 /* no such (or disabled) queue, got to love rfc-1179 */ 498 errno = ENOENT; 499 return (-1); 500 } 501 502 /* send the control data */ 503 if (send_control_file(sock, metadata, job_id) < 0) { 504 err = errno; 505 write(sock, "\001\n", 2); /* abort */ 506 errno = err; 507 return (-1); 508 } 509 510 /* walk the control file sending the data files */ 511 for (ptr = strtok_r(metadata, "\n", &iter); ptr != NULL; 512 ptr = strtok_r(NULL, "\n", &iter)) { 513 char *name = NULL; 514 515 if (ptr[0] != 'U') 516 continue; 517 518 name = strtok_r(NULL, "\n", &iter); 519 if (name[0] != 'N') 520 continue; 521 522 ptr++; 523 name++; 524 525 if (send_data_file(sock, ptr, name) < 0) { 526 err = errno; 527 write(sock, "\001\n", 2); /* abort */ 528 errno = err; 529 return (-1); 530 } 531 if (strcmp(name, "standard input") != 0) 532 sent_files++; 533 } 534 535 /* write back the job-id */ 536 err = errno; 537 if ((fd = open(path, O_WRONLY)) >= 0) { 538 ftruncate(fd, 0); 539 write(fd, &job_id, sizeof (job_id)); 540 close(fd); 541 } 542 errno = err; 543 544 if (sent_files != 0) { 545 err = errno; 546 close(sock); 547 errno = err; 548 } 549 550 return (0); 551 } 552 static int 553 query(int fd, char *printer, int ac, char **av) 554 { 555 char buf[BUFSIZ]; 556 int rc, len; 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, char *printer, int ac, char **av) 575 { 576 char buf[BUFSIZ]; 577 int rc, len; 578 579 /* build the request */ 580 snprintf(buf, sizeof (buf), "\05%s %s", printer, get_user_name()); 581 add_args(ac, av, buf, sizeof (buf)); 582 strlcat(buf, "\n", sizeof (buf)); 583 len = strlen(buf); 584 585 if (((rc = write(fd, buf, len)) >= 0) && (rc != len)) { 586 errno = EMSGSIZE; 587 rc = -1; 588 } else 589 rc = 0; 590 591 return (rc); 592 } 593 594 static void 595 usage(char *program) 596 { 597 char *name; 598 599 setreuid(getuid(), getuid()); 600 601 if ((name = strrchr(program, '/')) == NULL) 602 name = program; 603 else 604 name++; 605 606 fprintf(stderr, "usage:\t%s -H host [-t timeout] -s queue control ]\n", 607 name); 608 fprintf(stderr, "\t%s -H host [-t timeout] -c queue [user|job ...]\n", 609 name); 610 fprintf(stderr, "\t%s -H host [-t timeout] -q queue [user|job ...]\n", 611 name); 612 exit(EINVAL); 613 } 614 615 /* 616 * The main program temporarily loses privilege while searching the command 617 * line arguments. It then allocates any resources it need privilege for 618 * job-id, reserved port. Once it has the resources it needs, it perminently 619 * drops all elevated privilege. It ghen connects to the remote print service 620 * based on destination hostname. Doing it this way reduces the potenential 621 * opportunity for a breakout with elevated privilege, breakout with an 622 * unconnected reserved port, and exploitation of the remote print service 623 * by a calling program. 624 */ 625 int 626 main(int ac, char *av[]) 627 { 628 enum { OP_NONE, OP_SUBMIT, OP_QUERY, OP_CANCEL } operation = OP_NONE; 629 int fd, c, timeout = 0, exit_code = 0; 630 char *host = NULL, *queue = NULL; 631 uid_t uid = getuid(); 632 #ifdef PRIV_ALLSETS 633 priv_set_t *saveset = NULL; 634 #endif 635 636 openlog("lpd-port", LOG_PID, LOG_LPR); 637 638 #ifdef PRIV_ALLSETS 639 640 /* lose as much as we can perminently and temporarily drop the rest. */ 641 642 if ((saveset = priv_str_to_set("PRIV_NET_PRIVADDR," 643 "PRIV_FILE_DAC_READ,PRIV_FILE_DAC_WRITE,", 644 ",", (const char **)NULL)) == NULL) { 645 syslog(LOG_ERR, 646 "lpd_port: priv_str_to_set saveset failed: %m\n"); 647 return (-1); 648 } 649 650 if ((setppriv(PRIV_SET, PRIV_PERMITTED, saveset)) < 0) { 651 syslog(LOG_ERR, "lpd_port:setppriv:priv_set failed: %m"); 652 return (-1); 653 } 654 655 /* 656 * These privileges permanently dropped in next_job_id() and 657 * reserved_port() 658 */ 659 660 if ((setppriv(PRIV_OFF, PRIV_EFFECTIVE, saveset)) < 0) { 661 syslog(LOG_ERR, "lpd_port:setppriv:priv_off failed: %m"); 662 return (-1); 663 } 664 665 priv_freeset(saveset); 666 667 syslog(LOG_DEBUG, "using privs"); 668 #else 669 670 syslog(LOG_DEBUG, "no privs"); 671 seteuid(uid); 672 #endif 673 674 while ((c = getopt(ac, av, "H:t:c:q:s:")) != EOF) { 675 switch (c) { 676 case 'H': 677 host = optarg; 678 break; 679 case 't': 680 timeout = atoi(optarg); 681 break; 682 case 'c': 683 if (operation != OP_NONE) 684 usage(av[0]); 685 operation = OP_CANCEL; 686 queue = optarg; 687 break; 688 case 'q': 689 if (operation != OP_NONE) 690 usage(av[0]); 691 operation = OP_QUERY; 692 queue = optarg; 693 break; 694 case 's': 695 if (operation != OP_NONE) 696 usage(av[0]); 697 operation = OP_SUBMIT; 698 queue = optarg; 699 break; 700 default: 701 usage(av[0]); 702 /* does not return */ 703 } 704 } 705 706 if ((host == NULL) || (queue == NULL) || (timeout < 0) || 707 (operation == OP_NONE)) 708 usage(av[0]); 709 710 if (operation == OP_SUBMIT) /* get a job-id if we need it */ 711 if ((c = next_job_id()) < 0) { 712 syslog(LOG_ERR, "lpd_port:main:next_job_id fails"); 713 return (-1); 714 } 715 716 if ((fd = reserved_port()) < 0) { 717 syslog(LOG_ERR, "reserved_port() failed %m"); 718 return (errno); 719 } 720 721 /* 722 * we no longer want or need any elevated privilege, lose it all 723 * permanently. 724 */ 725 726 setreuid(uid, uid); 727 728 /* connect to the print service */ 729 if ((fd = sock_connect(fd, host, timeout)) < 0) 730 return (errno); 731 732 /* perform the requested operation */ 733 switch (operation) { 734 case OP_SUBMIT: /* transfer the job, close the fd */ 735 if (submit_job(fd, queue, c, av[optind]) < 0) 736 exit_code = errno; 737 break; 738 case OP_QUERY: /* send the query string, return the fd */ 739 if (query(fd, queue, ac - optind, &av[optind]) < 0) 740 exit_code = errno; 741 break; 742 case OP_CANCEL: /* send the cancel string, return the fd */ 743 if (cancel(fd, queue, ac - optind, &av[optind]) < 0) 744 exit_code = errno; 745 break; 746 default: /* This should never happen */ 747 exit_code = EINVAL; 748 } 749 750 751 /* if the operation succeeded, send the fd to our parent */ 752 if ((exit_code == 0) && (sendfd(1, fd) < 0)) { 753 char buf[BUFSIZ]; 754 755 exit_code = errno; 756 757 /* sendfd() failed, dump the socket data for the heck of it */ 758 while ((c = read(fd, buf, sizeof (buf))) > 0) 759 write(1, buf, c); 760 } 761 762 syslog(LOG_DEBUG, "exit code: %d", exit_code); 763 return (exit_code); 764 } 765