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