1 /* $NetBSD: pcnfsd_misc.c,v 1.17 2020/04/22 23:46:02 joerg Exp $ */ 2 3 /* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_misc.c 1.5 92/01/24 19:59:13 SMI */ 4 /* 5 **===================================================================== 6 ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc. 7 ** @(#)pcnfsd_misc.c 1.5 1/24/92 8 **===================================================================== 9 */ 10 /* 11 **===================================================================== 12 ** I N C L U D E F I L E S E C T I O N * 13 ** * 14 ** If your port requires different include files, add a suitable * 15 ** #define in the customization section, and make the inclusion or * 16 ** exclusion of the files conditional on this. * 17 **===================================================================== 18 */ 19 20 #include <sys/file.h> 21 #include <sys/ioctl.h> 22 #include <sys/socket.h> 23 #include <sys/stat.h> 24 #include <sys/time.h> 25 #include <sys/wait.h> 26 27 #include <netinet/in.h> 28 #include <arpa/inet.h> 29 30 #include <ctype.h> 31 #include <errno.h> 32 #include <netdb.h> 33 #include <pwd.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <util.h> 40 #ifdef SUPPORT_UTMPX 41 #include <utmpx.h> 42 #endif 43 44 #ifdef ISC_2_0 45 #include <sys/fcntl.h> 46 #endif 47 48 #ifdef SHADOW_SUPPORT 49 #include <shadow.h> 50 #endif 51 52 #ifdef WTMP 53 int wtmp_enabled = 1; 54 #endif 55 56 #include "common.h" 57 #include "pcnfsd.h" 58 #include "extern.h" 59 60 /* 61 **--------------------------------------------------------------------- 62 ** Other #define's 63 **--------------------------------------------------------------------- 64 */ 65 66 #define zchar 0x5b 67 68 char *mapfont(char, char, char); 69 void myhandler(int); 70 void start_watchdog(int); 71 void stop_watchdog(void); 72 73 /* 74 **===================================================================== 75 ** C O D E S E C T I O N * 76 **===================================================================== 77 */ 78 /* 79 **--------------------------------------------------------------------- 80 ** Support procedures 81 **--------------------------------------------------------------------- 82 */ 83 84 85 void 86 scramble(char *s1, char *s2) 87 { 88 while (*s1) { 89 *s2++ = (*s1 ^ zchar) & 0x7f; 90 s1++; 91 } 92 *s2 = 0; 93 } 94 95 96 97 struct passwd * 98 get_password(char *usrnam) 99 { 100 struct passwd *p; 101 static struct passwd localp; 102 __aconst char *pswd, *ushell; 103 104 105 #ifdef SHADOW_SUPPORT 106 struct spwd *sp; 107 int shadowfile; 108 #endif 109 110 #ifdef SHADOW_SUPPORT 111 /* 112 **-------------------------------------------------------------- 113 ** Check the existence of SHADOW. If it is there, then we are 114 ** running a two-password-file system. 115 **-------------------------------------------------------------- 116 */ 117 if (access(SHADOW, 0)) 118 shadowfile = 0; /* SHADOW is not there */ 119 else 120 shadowfile = 1; 121 122 setpwent(); 123 if (shadowfile) 124 (void) setspent(); /* Setting the shadow password file */ 125 if ((p = getpwnam(usrnam)) == NULL || 126 (shadowfile && (sp = getspnam(usrnam)) == NULL)) 127 return (NULL); 128 129 if (shadowfile) { 130 pswd = sp->sp_pwdp; 131 (void) endspent(); 132 } else 133 pswd = p->pw_passwd; 134 135 #else 136 p = getpwnam(usrnam); 137 if (p == NULL) 138 return (NULL); 139 pswd = p->pw_passwd; 140 #endif 141 142 #ifdef ISC_2_0 143 /* *----------------------------------------------------------- * We 144 * may have an 'x' in which case look in /etc/shadow .. 145 * *----------------------------------------------------------- */ 146 if (((strlen(pswd)) == 1) && pswd[0] == 'x') { 147 struct spwd *shadow = getspnam(usrnam); 148 149 if (!shadow) 150 return (NULL); 151 pswd = shadow->sp_pwdp; 152 } 153 #endif 154 localp = *p; 155 localp.pw_passwd = pswd; 156 #ifdef USE_GETUSERSHELL 157 158 setusershell(); 159 while (ushell = getusershell()) { 160 if (!strcmp(ushell, localp.pw_shell)) { 161 ok = 1; 162 break; 163 } 164 } 165 endusershell(); 166 if (!ok) 167 return (NULL); 168 #else 169 /* 170 * the best we can do is to ensure that the shell ends in "sh" 171 */ 172 ushell = localp.pw_shell; 173 if (strlen(ushell) < 2) 174 return (NULL); 175 ushell += strlen(ushell) - 2; 176 if (strcmp(ushell, "sh")) 177 return (NULL); 178 179 #endif 180 return (&localp); 181 } 182 183 184 185 /* 186 **--------------------------------------------------------------------- 187 ** Print support procedures 188 **--------------------------------------------------------------------- 189 */ 190 191 192 char * 193 mapfont(char f, char i, char b) 194 { 195 static char fontname[64]; 196 197 fontname[0] = 0; /* clear it out */ 198 199 switch (f) { 200 case 'c': 201 (void) strlcpy(fontname, "Courier", sizeof(fontname)); 202 break; 203 case 'h': 204 (void) strlcpy(fontname, "Helvetica", sizeof(fontname)); 205 break; 206 case 't': 207 (void) strlcpy(fontname, "Times", sizeof(fontname)); 208 break; 209 default: 210 (void) strlcpy(fontname, "Times-Roman", sizeof(fontname)); 211 goto finis; 212 } 213 if (i != 'o' && b != 'b') { /* no bold or oblique */ 214 if (f == 't') /* special case Times */ 215 (void) strlcat(fontname, "-Roman", sizeof(fontname)); 216 goto finis; 217 } 218 (void) strlcat(fontname, "-", sizeof(fontname)); 219 if (b == 'b') 220 (void) strlcat(fontname, "Bold", sizeof(fontname)); 221 if (i == 'o') /* o-blique */ 222 (void) strlcat(fontname, f == 't' ? "Italic" : "Oblique", 223 sizeof(fontname)); 224 225 finis: return (&fontname[0]); 226 } 227 /* 228 * run_ps630 performs the Diablo 630 emulation filtering process. ps630 229 * was broken in certain Sun releases: it would not accept point size or 230 * font changes. If your version is fixed, undefine the symbol 231 * PS630_IS_BROKEN and rebuild pc-nfsd. 232 */ 233 /* #define PS630_IS_BROKEN 1 */ 234 235 void 236 run_ps630(char *f, char *opts) 237 { 238 char temp_file[256]; 239 char commbuf[256]; 240 int i; 241 242 (void) strlcpy(temp_file, f, sizeof(temp_file)); 243 (void) strlcat(temp_file, "X", sizeof(temp_file)); /* intermediate file name */ 244 245 #ifndef PS630_IS_BROKEN 246 (void) snprintf(commbuf, sizeof(commbuf), "ps630 -s %c%c -p %s -f ", 247 opts[2], opts[3], temp_file); 248 (void) strlcat(commbuf, mapfont(opts[4], opts[5], opts[6]), 249 sizeof(commbuf)); 250 (void) strlcat(commbuf, " -F ", sizeof(commbuf)); 251 (void) strlcat(commbuf, mapfont(opts[7], opts[8], opts[9]), 252 sizeof(commbuf)); 253 (void) strlcat(commbuf, " ", sizeof(commbuf)); 254 (void) strlcat(commbuf, f, sizeof(commbuf)); 255 #else /* PS630_IS_BROKEN */ 256 /* 257 * The pitch and font features of ps630 appear to be broken at 258 * this time. 259 */ 260 (void) snprintf(commbuf, sizeof(commbuf), "ps630 -p %s %s", 261 temp_file, f); 262 #endif /* PS630_IS_BROKEN */ 263 264 265 if ((i = system(commbuf)) != 0) { 266 /* 267 * Under (un)certain conditions, ps630 may return -1 even 268 * if it worked. Hence the commenting out of this error 269 * report. 270 */ 271 /* (void)fprintf(stderr, "\n\nrun_ps630 rc = %d\n", i) */ ; 272 /* exit(1); */ 273 } 274 if (rename(temp_file, f)) { 275 perror("run_ps630: rename"); 276 exit(1); 277 } 278 return; 279 } 280 281 282 283 284 285 /* 286 **--------------------------------------------------------------------- 287 ** WTMP update support 288 **--------------------------------------------------------------------- 289 */ 290 291 292 #ifdef WTMP 293 void 294 wlogin(char *name, struct svc_req *req) 295 { 296 struct sockaddr_in *who; 297 struct hostent *hp; 298 char *host; 299 300 if (!wtmp_enabled) 301 return; 302 303 /* Get network address of client. */ 304 who = &req->rq_xprt->xp_raddr; 305 306 /* Get name of connected client */ 307 hp = gethostbyaddr((char *) &who->sin_addr, 308 sizeof(struct in_addr), 309 who->sin_family); 310 311 if (hp) { 312 host = hp->h_name; 313 } else { 314 host = inet_ntoa(who->sin_addr); 315 } 316 317 #ifdef SUPPORT_UTMP 318 logwtmp("PC-NFS", name, host); 319 #endif 320 #ifdef SUPPORT_UTMPX 321 logwtmpx("PC-NFS", name, host, 0, USER_PROCESS); 322 #endif 323 } 324 #endif /* WTMP */ 325 326 327 /* 328 **--------------------------------------------------------------------- 329 ** Run-process-as-user procedures 330 **--------------------------------------------------------------------- 331 */ 332 333 334 #define READER_FD 0 335 #define WRITER_FD 1 336 337 static int child_pid; 338 339 static char cached_user[64] = ""; 340 static uid_t cached_uid; 341 static gid_t cached_gid; 342 343 static struct sigaction old_action; 344 static struct sigaction new_action; 345 static struct itimerval timer; 346 347 int interrupted = 0; 348 static FILE *pipe_handle; 349 350 void 351 myhandler(int dummy) 352 { 353 interrupted = 1; 354 fclose(pipe_handle); 355 kill(child_pid, SIGKILL); 356 msg_out("rpc.pcnfsd: su_popen timeout - killed child process"); 357 } 358 359 void 360 start_watchdog(int n) 361 { 362 /* 363 * Setup SIGALRM handler, force interrupt of ongoing syscall 364 */ 365 366 new_action.sa_handler = myhandler; 367 sigemptyset(&(new_action.sa_mask)); 368 new_action.sa_flags = 0; 369 #ifdef SA_INTERRUPT 370 new_action.sa_flags |= SA_INTERRUPT; 371 #endif 372 sigaction(SIGALRM, &new_action, &old_action); 373 374 /* 375 * Set interval timer for n seconds 376 */ 377 timer.it_interval.tv_sec = 0; 378 timer.it_interval.tv_usec = 0; 379 timer.it_value.tv_sec = n; 380 timer.it_value.tv_usec = 0; 381 setitimer(ITIMER_REAL, &timer, NULL); 382 interrupted = 0; 383 384 } 385 386 void 387 stop_watchdog() 388 { 389 /* 390 * Cancel timer 391 */ 392 393 timer.it_interval.tv_sec = 0; 394 timer.it_interval.tv_usec = 0; 395 timer.it_value.tv_sec = 0; 396 timer.it_value.tv_usec = 0; 397 setitimer(ITIMER_REAL, &timer, NULL); 398 399 /* 400 * restore old signal handling 401 */ 402 sigaction(SIGALRM, &old_action, NULL); 403 } 404 405 FILE * 406 su_popen(char *user, char *cmd, int maxtime) 407 { 408 int p[2]; 409 int parent_fd, child_fd, pid; 410 struct passwd *pw; 411 412 if (strcmp(cached_user, user)) { 413 pw = getpwnam(user); 414 if (!pw) 415 pw = getpwnam("nobody"); 416 if (pw) { 417 cached_uid = pw->pw_uid; 418 cached_gid = pw->pw_gid; 419 strlcpy(cached_user, user, sizeof(cached_user)); 420 } else { 421 cached_uid = (uid_t) (-2); 422 cached_gid = (gid_t) (-2); 423 cached_user[0] = '\0'; 424 } 425 } 426 if (pipe(p) < 0) { 427 msg_out("rpc.pcnfsd: unable to create pipe in su_popen"); 428 return (NULL); 429 } 430 parent_fd = p[READER_FD]; 431 child_fd = p[WRITER_FD]; 432 if ((pid = fork()) == 0) { 433 int i; 434 435 for (i = 0; i < 10; i++) 436 if (i != child_fd) 437 (void) close(i); 438 if (child_fd != 1) { 439 (void) dup2(child_fd, 1); 440 (void) close(child_fd); 441 } 442 dup2(1, 2); /* let's get stderr as well */ 443 444 (void) setgid(cached_gid); 445 (void) setuid(cached_uid); 446 447 (void) execl("/bin/sh", "sh", "-c", cmd, (char *) NULL); 448 _exit(255); 449 } 450 if (pid == -1) { 451 msg_out("rpc.pcnfsd: fork failed"); 452 close(parent_fd); 453 close(child_fd); 454 return (NULL); 455 } 456 child_pid = pid; 457 close(child_fd); 458 start_watchdog(maxtime); 459 pipe_handle = fdopen(parent_fd, "r"); 460 return (pipe_handle); 461 } 462 463 int 464 su_pclose(FILE *ptr) 465 { 466 int pid, status; 467 468 stop_watchdog(); 469 470 fclose(ptr); 471 if (child_pid == -1) 472 return (-1); 473 while ((pid = wait(&status)) != child_pid && pid != -1); 474 return (pid == -1 ? -1 : status); 475 } 476 477 478 479 /* 480 ** The following routine reads a file "/etc/pcnfsd.conf" if present, 481 ** and uses it to replace certain builtin elements, like the 482 ** name of the print spool directory. The configuration file 483 ** Is the usual kind: Comments begin with '#', blank lines are ignored, 484 ** and valid lines are of the form 485 ** 486 ** <keyword><whitespace><value> 487 ** 488 ** The following keywords are recognized: 489 ** 490 ** spooldir 491 ** printer name alias-for command 492 ** wtmp yes|no 493 */ 494 static void 495 config_from_file(void) 496 { 497 FILE *fd; 498 char buff[1024]; 499 char *cp; 500 char *kw; 501 char *val; 502 char *arg1; 503 char *arg2; 504 505 if ((fd = fopen("/etc/pcnfsd.conf", "r")) == NULL) 506 return; 507 while (fgets(buff, 1024, fd)) { 508 cp = strchr(buff, '\n'); 509 *cp = '\0'; 510 cp = strchr(buff, '#'); 511 if (cp) 512 *cp = '\0'; 513 kw = strtok(buff, " \t"); 514 if (kw == NULL) 515 continue; 516 val = strtok(NULL, " \t"); 517 if (val == NULL) 518 continue; 519 if (!strcasecmp(kw, "spooldir")) { 520 strlcpy(sp_name, val, sizeof(sp_name)); 521 continue; 522 } 523 #ifdef WTMP 524 if (!strcasecmp(kw, "wtmp")) { 525 /* assume default is YES, just look for negatives */ 526 if (!strcasecmp(val, "no") || 527 !strcasecmp(val, "off") || 528 !strcasecmp(val, "disable") || 529 !strcmp(val, "0")) 530 wtmp_enabled = 0; 531 continue; 532 } 533 #endif 534 if (!strcasecmp(kw, "printer")) { 535 arg1 = strtok(NULL, " \t"); 536 arg2 = strtok(NULL, ""); 537 (void) add_printer_alias(val, arg1, arg2); 538 continue; 539 } 540 /* 541 ** Add new cases here 542 */ 543 } 544 fclose(fd); 545 } 546 547 /* 548 ** hack for main() - call config_from_file() then the real main 549 ** in the rpcgen output, which is hacked by CPPFLAGS to be "mymain" 550 */ 551 #undef main 552 553 int mymain(int argc, char *argv[]); 554 555 int 556 main(int argc, char *argv[]) 557 { 558 config_from_file(); 559 return mymain(argc, argv); 560 } 561 562 /* 563 ** strembedded - returns true if s1 is embedded (in any case) in s2 564 */ 565 566 int 567 strembedded(const char *s1, const char *s2) 568 { 569 while (*s2) { 570 if (!strcasecmp(s1, s2)) 571 return 1; 572 s2++; 573 } 574 return 0; 575 } 576