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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * 4.2BSD, 2.9BSD, or ATTSVR4 TCP/IP server for uucico
  28  * uucico's TCP channel causes this server to be run at the remote end.
  29  */
  30 
  31 #include "uucp.h"
  32 #include <netdb.h>
  33 #ifdef BSD2_9
  34 #include <sys/localopts.h>
  35 #include <sys/file.h>
  36 #endif  /* BSD2_9 */
  37 #include <signal.h>
  38 #include <errno.h>
  39 #include <sys/socket.h>
  40 #include <netinet/in.h>
  41 #include <sys/wait.h>
  42 #ifdef ATTSVTTY
  43 #include <sys/termio.h>
  44 #else
  45 #include <sys/ioctl.h>
  46 #endif
  47 #include <pwd.h>
  48 #ifdef ATTSVR4
  49 #include <shadow.h>
  50 #endif
  51 
  52 #include <security/pam_appl.h>
  53 
  54 static int uucp_conv();
  55 struct pam_conv conv = {uucp_conv, NULL };
  56 pam_handle_t    *pamh;
  57 
  58 #if !defined(BSD4_2) && !defined(BSD2_9) && !defined(ATTSVR4)
  59 --- You must have either BSD4_2, BSD2_9, or ATTSVR4 defined for this to work
  60 #endif  /* !BSD4_2 && !BSD2_9 */
  61 #if defined(BSD4_2) && defined(BSD2_9)
  62 --- You may not have both BSD4_2 and BSD2_9 defined for this to work
  63 #endif  /* check for stupidity */
  64 
  65 struct  passwd nouser = {
  66         "", "nope", (uid_t)-1, (gid_t)-1, "", "", "", "", "" };
  67 #ifdef ATTSVR4
  68 struct  spwd noupass = { "", "nope" };
  69 #endif
  70 struct  sockaddr_in hisctladdr;
  71 socklen_t hisaddrlen = (socklen_t)sizeof (hisctladdr);
  72 struct  sockaddr_in myctladdr;
  73 int nolog;              /* don't log in utmp or wtmp */
  74 
  75 char Username[64];
  76 char Loginname[64];
  77 char *nenv[] = {
  78         Username,
  79         Loginname,
  80         NULL,
  81 };
  82 extern char **environ;
  83 
  84 static void doit(struct sockaddr_in *);
  85 static void dologout(void);
  86 
  87 int
  88 main(argc, argv)
  89 int argc;
  90 char **argv;
  91 {
  92 #ifndef BSDINETD
  93         int s, tcp_socket;
  94         struct servent *sp;
  95 #endif  /* !BSDINETD */
  96         extern int errno;
  97 
  98         if (argc > 1 && strcmp(argv[1], "-n") == 0)
  99                 nolog = 1;
 100         environ = nenv;
 101 #ifdef BSDINETD
 102         close(1); close(2);
 103         dup(0); dup(0);
 104         hisaddrlen = (socklen_t)sizeof (hisctladdr);
 105         if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) {
 106                 fprintf(stderr, "%s: ", argv[0]);
 107                 perror("getpeername");
 108                 _exit(1);
 109         }
 110         if (fork() == 0)
 111                 doit(&hisctladdr);
 112         dologout();
 113         exit(1);
 114 #else   /* !BSDINETD */
 115         sp = getservbyname("uucp", "tcp");
 116         if (sp == NULL) {
 117                 perror("uucpd: getservbyname");
 118                 exit(1);
 119         }
 120         if (fork())
 121                 exit(0);
 122 #ifdef ATTSVR4
 123         setsid();
 124 #else
 125         if ((s = open("/dev/tty", 2)) >= 0) {
 126                 ioctl(s, TIOCNOTTY, (char *)0);
 127                 close(s);
 128         }
 129 #endif
 130 
 131 #ifdef ATTSVR4
 132         memset((void *)&myctladdr, 0, sizeof (myctladdr));
 133 #else
 134         bzero((char *)&myctladdr, sizeof (myctladdr));
 135 #endif
 136         myctladdr.sin_family = AF_INET;
 137         myctladdr.sin_port = sp->s_port;
 138 #if defined(BSD4_2) || defined(ATTSVR4)
 139         tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
 140         if (tcp_socket < 0) {
 141                 perror("uucpd: socket");
 142                 exit(1);
 143         }
 144         if (bind(tcp_socket, (char *)&myctladdr, sizeof (myctladdr)) < 0) {
 145                 perror("uucpd: bind");
 146                 exit(1);
 147         }
 148         listen(tcp_socket, 3);  /* at most 3 simultaneuos uucp connections */
 149         signal(SIGCHLD, dologout);
 150 
 151         for (;;) {
 152                 s = accept(tcp_socket, &hisctladdr, &hisaddrlen);
 153                 if (s < 0) {
 154                         if (errno == EINTR)
 155                                 continue;
 156                         perror("uucpd: accept");
 157                         exit(1);
 158                 }
 159                 if (fork() == 0) {
 160                         close(0); close(1); close(2);
 161                         dup(s); dup(s); dup(s);
 162                         close(tcp_socket); close(s);
 163                         doit(&hisctladdr);
 164                         exit(1);
 165                 }
 166                 close(s);
 167         }
 168 #endif  /* BSD4_2 */
 169 
 170 #ifdef BSD2_9
 171         for (;;) {
 172                 signal(SIGCHLD, dologout);
 173                 s = socket(SOCK_STREAM, 0,  &myctladdr,
 174                         SO_ACCEPTCONN|SO_KEEPALIVE);
 175                 if (s < 0) {
 176                         perror("uucpd: socket");
 177                         exit(1);
 178                 }
 179                 if (accept(s, &hisctladdr) < 0) {
 180                         if (errno == EINTR) {
 181                                 close(s);
 182                                 continue;
 183                         }
 184                         perror("uucpd: accept");
 185                         exit(1);
 186                 }
 187                 if (fork() == 0) {
 188                         close(0); close(1); close(2);
 189                         dup(s); dup(s); dup(s);
 190                         close(s);
 191                         doit(&hisctladdr);
 192                         exit(1);
 193                 }
 194         }
 195 #endif  /* BSD2_9 */
 196 #endif  /* !BSDINETD */
 197 
 198         /* NOTREACHED */
 199 }
 200 
 201 static void
 202 doit(sinp)
 203 struct sockaddr_in *sinp;
 204 {
 205         char user[64], passwd[64];
 206         struct passwd *pw, *getpwnam();
 207         int error;
 208 
 209         alarm(60);
 210         printf("login: "); fflush(stdout);
 211         if (readline(user, sizeof (user)) < 0) {
 212                 fprintf(stderr, "user read\n");
 213                 return;
 214         }
 215 
 216         /*
 217          * Call pam_start to initiate a PAM authentication operation
 218          */
 219 
 220         if ((pam_start("uucp", user, &conv, &pamh)) != PAM_SUCCESS)
 221                 return;
 222         if ((pam_set_item(pamh, PAM_TTY, ttyname(0))) != PAM_SUCCESS)
 223                 return;
 224 
 225         if (pam_authenticate(pamh, PAM_SILENT) != PAM_SUCCESS) {
 226                 /* force a delay if passwd bad */
 227                 sleep(4);
 228                 fprintf(stderr, "Login incorrect.");
 229                 pam_end(pamh, PAM_ABORT);
 230                 return;
 231         }
 232 
 233         if ((error = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS) {
 234                 switch (error) {
 235                 case PAM_NEW_AUTHTOK_REQD:
 236                         fprintf(stderr, "Password Expired.");
 237                         break;
 238                 case PAM_PERM_DENIED:
 239                         fprintf(stderr, "Account Expired.");
 240                         break;
 241                 case PAM_AUTHTOK_EXPIRED:
 242                         fprintf(stderr, "Password Expired.");
 243                         break;
 244                 default:
 245                         fprintf(stderr, "Login incorrect.");
 246                         break;
 247                 }
 248                 pam_end(pamh, PAM_ABORT);
 249                 return;
 250         }
 251 
 252         if ((pw = getpwnam(user)) == NULL || strcmp(pw->pw_shell, UUCICO)) {
 253                 /* force a delay if user bad */
 254                 sleep(4);
 255                 fprintf(stderr, "Login incorrect.");
 256                 pam_end(pamh, PAM_USER_UNKNOWN);
 257                 return;
 258         }
 259 
 260         alarm(0);
 261 
 262         sprintf(Username, "USER=%s", user);
 263         sprintf(Loginname, "LOGNAME=%s", user);
 264         if (!nolog)
 265                 if (dologin(pw, sinp)) {
 266                         pam_end(pamh, PAM_ABORT);
 267                         _exit(1);
 268                 }
 269 
 270         /* set the real (and effective) GID */
 271         if (setgid(pw->pw_gid) == -1) {
 272                 fprintf(stderr, "Login incorrect.");
 273                 pam_end(pamh, PAM_PERM_DENIED);
 274                 return;
 275         }
 276 
 277         /*
 278          * Initialize the supplementary group access list.
 279          */
 280         if (initgroups(user, pw->pw_gid) == -1) {
 281                 fprintf(stderr, "Login incorrect.");
 282                 pam_end(pamh, PAM_PERM_DENIED);
 283                 return;
 284         }
 285 
 286         if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) {
 287                 fprintf(stderr, "Login incorrect.");
 288                 pam_end(pamh, PAM_CRED_INSUFFICIENT);
 289                 return;
 290         }
 291 
 292         /* set the real (and effective) UID */
 293         if (setuid(pw->pw_uid) == -1) {
 294                 fprintf(stderr, "Login incorrect.");
 295                 pam_end(pamh, PAM_CRED_ERR);
 296                 return;
 297         }
 298 
 299         chdir(pw->pw_dir);
 300 
 301         pam_end(pamh, PAM_SUCCESS);
 302 
 303 #if defined(BSD4_2) || defined(ATTSVR4)
 304         execl(UUCICO, "uucico", "-u", user, (char *)0);
 305 #endif  /* BSD4_2 */
 306 #ifdef BSD2_9
 307         sprintf(passwd, "-h%s", inet_ntoa(sinp->sin_addr));
 308         execl(UUCICO, "uucico", passwd, (char *)0);
 309 #endif  /* BSD2_9 */
 310         perror("uucico server: execl");
 311 }
 312 
 313 int
 314 readline(p, n)
 315 char *p;
 316 int n;
 317 {
 318         char c;
 319 
 320         while (n-- > 0) {
 321                 if (read(0, &c, 1) <= 0)
 322                         return (-1);
 323                 c &= 0177;
 324                 if (c == '\n' || c == '\r') {
 325                         *p = '\0';
 326                         return (0);
 327                 }
 328                 *p++ = c;
 329         }
 330         return (-1);
 331 }
 332 
 333 #ifdef ATTSVR4
 334 #include <sac.h>  /* for SC_WILDC */
 335 #include <utmpx.h>
 336 #else   /* !ATTSVR4 */
 337 #include <utmp.h>
 338 #endif  /* !ATTSVR4 */
 339 #if defined(BSD4_2) || defined(ATTSVR4)
 340 #include <fcntl.h>
 341 #endif  /* BSD4_2 */
 342 
 343 #ifdef BSD2_9
 344 #define O_APPEND        0 /* kludge */
 345 #define wait3(a, b, c)  wait2(a, b)
 346 #endif  /* BSD2_9 */
 347 
 348 #define SCPYN(a, b)     strncpy(a, b, sizeof (a))
 349 
 350 #ifdef ATTSVR4
 351 struct  utmpx utmp;
 352 #else   /* !ATTSVR4 */
 353 struct  utmp utmp;
 354 #endif  /* !ATTSVR4 */
 355 
 356 static void
 357 dologout(void)
 358 {
 359 #ifdef ATTSVR4
 360         int status;
 361 #else   /* !ATTSVR4 */
 362         union wait status;
 363 #endif  /* !ATSVR4 */
 364         int pid, wtmp;
 365         /* the following 2 variables are needed for utmp mgmt */
 366         struct utmpx    ut;
 367 
 368 #ifdef BSDINETD
 369         while ((pid = wait(&status)) > 0) {
 370 #else   /* !BSDINETD */
 371         while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
 372 #endif  /* !BSDINETD */
 373                 if (nolog)
 374                         continue;
 375 #ifdef ATTSVR4
 376                 /* clear out any residue from utmpx buffer */
 377                 (void) memset((char *)&ut, 0, sizeof (ut));
 378 
 379                 SCPYN(utmp.ut_user, "");
 380                 ut.ut_id[0] = 'u';
 381                 ut.ut_id[1] = 'u';
 382                 ut.ut_id[2] = SC_WILDC;
 383                 ut.ut_id[3] = SC_WILDC;
 384                 sprintf(ut.ut_line, "uucp%.4d", pid);
 385                 ut.ut_pid  = getpid();
 386                 ut.ut_type = DEAD_PROCESS;
 387                 ut.ut_exit.e_termination = status & 0xFF;
 388                 ut.ut_exit.e_exit = WEXITSTATUS(status);
 389                 SCPYN(ut.ut_host, "");
 390                 ut.ut_syslen = 1;
 391                 (void) gettimeofday(&ut.ut_tv, NULL);
 392 
 393                 /*
 394                  * XXX: UUCPD does not do any pam session management.
 395                  *      There is no way for the parent process to close
 396                  *      the pam session after a child has exited.
 397                  */
 398 
 399                 updwtmpx(WTMPX_FILE, &ut);
 400 #else   /* !ATTSVR4 */
 401                 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
 402                 if (wtmp >= 0) {
 403                         sprintf(utmp.ut_line, "uucp%.4d", pid);
 404                         SCPYN(utmp.ut_name, "");
 405                         SCPYN(utmp.ut_host, "");
 406                         (void) time(&utmp.ut_time);
 407 #ifdef BSD2_9
 408                         (void) lseek(wtmp, 0L, 2);
 409 #endif  /* BSD2_9 */
 410                         (void) write(wtmp, (char *)&utmp, sizeof (utmp));
 411                         (void) close(wtmp);
 412                 }
 413 #endif  /* !ATTSVR4 */
 414         }
 415 }
 416 
 417 /*
 418  * Record login in wtmp file.
 419  */
 420 int
 421 dologin(pw, sin)
 422 struct passwd *pw;
 423 struct sockaddr_in *sin;
 424 {
 425         char line[32];
 426         char remotehost[32];
 427         int wtmp;
 428         struct hostent *hp = gethostbyaddr((const char *)&sin->sin_addr,
 429                 sizeof (struct in_addr), AF_INET);
 430         struct utmpx    ut;
 431 
 432         if (hp) {
 433                 strncpy(remotehost, hp->h_name, sizeof (remotehost));
 434                 endhostent();
 435         } else
 436                 strncpy(remotehost, (char *)inet_ntoa(sin->sin_addr),
 437                     sizeof (remotehost));
 438 #ifdef ATTSVR4
 439         /* clear wtmpx entry */
 440         (void) memset((void *)&ut, 0, sizeof (ut));
 441 
 442         SCPYN(ut.ut_user, pw->pw_name);
 443         ut.ut_id[0] = 'u';
 444         ut.ut_id[1] = 'u';
 445         ut.ut_id[2] = SC_WILDC;
 446         ut.ut_id[3] = SC_WILDC;
 447         /* hack, but must be unique and no tty line */
 448         sprintf(line, "uucp%.4d", getpid());
 449         SCPYN(ut.ut_line, line);
 450         ut.ut_pid = getpid();
 451         ut.ut_type = USER_PROCESS;
 452         ut.ut_exit.e_termination = 0;
 453         ut.ut_exit.e_exit = 0;
 454         SCPYN(ut.ut_host, remotehost);
 455         ut.ut_syslen = strlen(remotehost) + 1;
 456         (void) gettimeofday(&ut.ut_tv, 0);
 457         updwtmpx(WTMPX_FILE, &ut);
 458 
 459         /*
 460          * XXX:
 461          *      We no longer do session management in uucpd because
 462          *      there is no way to do the "pam_close_session()".
 463          *
 464          *      Processes like "init" can do a pam_close_session()
 465          *      because they can use the utmp entry to retrieve
 466          *      the proper username, ttyname, etc. --
 467          *      uucpd only writes to the wtmp file.
 468          *
 469          *      ftpd (which also only writes to the wtmp file)
 470          *      can do a pam_close_session() because it doesn't fork().
 471          *
 472          *      if (pam_set_item(pamh, PAM_RHOST, remotehost) != PAM_SUCCESS)
 473          *              return (1);
 474          *      if (pam_set_item(pamh, PAM_TTY, line) != PAM_SUCCESS)
 475          *              return (1);
 476          *      if (pam_open_session(pamh, 0) != PAM_SUCCESS) {
 477          *              return (1);
 478          *      }
 479          */
 480 
 481 #else   /* !ATTSVR4 */
 482         wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
 483         if (wtmp >= 0) {
 484                 /* hack, but must be unique and no tty line */
 485                 sprintf(line, "uucp%.4d", getpid());
 486                 SCPYN(utmp.ut_line, line);
 487                 SCPYN(utmp.ut_name, pw->pw_name);
 488                 SCPYN(utmp.ut_host, remotehost);
 489                 time(&utmp.ut_time);
 490 #ifdef BSD2_9
 491                 (void) lseek(wtmp, 0L, 2);
 492 #endif  /* BSD2_9 */
 493                 (void) write(wtmp, (char *)&utmp, sizeof (utmp));
 494                 (void) close(wtmp);
 495         }
 496 #endif  /* !ATTSVR4 */
 497 
 498         return (0);
 499 }
 500 
 501 /*
 502  * uucp_conv    - This is the conv (conversation) function called from
 503  *              a PAM authentication module to print error messages
 504  *              or garner information from the user.
 505  */
 506 
 507 static int
 508 uucp_conv(num_msg, msg, response, appdata_ptr)
 509         int num_msg;
 510         struct pam_message **msg;
 511         struct pam_response **response;
 512         void *appdata_ptr;
 513 {
 514         struct pam_message      *m;
 515         struct pam_response     *r;
 516         char                    *temp;
 517         static char             passwd[64];
 518         int                     k, i;
 519 
 520         if (num_msg <= 0)
 521                 return (PAM_CONV_ERR);
 522 
 523         *response = (struct pam_response *)calloc(num_msg,
 524                         sizeof (struct pam_response));
 525         if (*response == NULL)
 526                 return (PAM_BUF_ERR);
 527 
 528         k = num_msg;
 529         m = *msg;
 530         r = *response;
 531         while (k--) {
 532 
 533                 switch (m->msg_style) {
 534 
 535                 case PAM_PROMPT_ECHO_OFF:
 536                         /*
 537                          * we do this instead of using passed in message
 538                          * to prevent possible breakage of uucp protocol.
 539                          */
 540                         printf("Password: "); fflush(stdout);
 541                         if (readline(passwd, sizeof (passwd)) < 0) {
 542                                 fprintf(stderr, "passwd read\n");
 543                                 return (PAM_SUCCESS);
 544                         }
 545                         temp = passwd;
 546                         if (temp != NULL) {
 547                                 r->resp = strdup(temp);
 548                                 if (r->resp == NULL) {
 549                                         /* free responses */
 550                                         r = *response;
 551                                         for (i = 0; i < num_msg; i++, r++) {
 552                                                 if (r->resp)
 553                                                         free(r->resp);
 554                                         }
 555                                         free(*response);
 556                                         *response = NULL;
 557                                         return (PAM_BUF_ERR);
 558                                 }
 559                         }
 560 
 561                         m++;
 562                         r++;
 563                         break;
 564 
 565                 case PAM_PROMPT_ECHO_ON:
 566                         if (m->msg != NULL) {
 567                                 fputs(m->msg, stdout);
 568                                 fflush(stdout);
 569                         }
 570                         r->resp = (char *)malloc(PAM_MAX_RESP_SIZE);
 571                         if (r->resp == NULL) {
 572                                 /* free the response */
 573                                 r = *response;
 574                                 for (i = 0; i < num_msg; i++, r++) {
 575                                         if (r->resp)
 576                                                 free(r->resp);
 577                                 }
 578                                 free(*response);
 579                                 *response = NULL;
 580                                 return (PAM_BUF_ERR);
 581                         }
 582                         (void) fgets(r->resp, PAM_MAX_RESP_SIZE, stdin);
 583                         m++;
 584                         r++;
 585                         break;
 586 
 587                 case PAM_ERROR_MSG:
 588                         if (m->msg != NULL) {
 589                                 fputs(m->msg, stderr);
 590                                 fputs("\n", stderr);
 591                         }
 592                         m++;
 593                         r++;
 594                         break;
 595                 case PAM_TEXT_INFO:
 596                         if (m->msg != NULL) {
 597                                 fputs(m->msg, stdout);
 598                                 fputs("\n", stdout);
 599                         }
 600                         m++;
 601                         r++;
 602                         break;
 603 
 604                 default:
 605                         break;
 606                 }
 607         }
 608         return (PAM_SUCCESS);
 609 }