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