/* * processor manager for UNIX systems */ #include <dk.h> #include <dkmgr.h> #include <pwd.h> #include <stdio.h> #include <signal.h> #include <sys/ioctl.h> #include <utmp.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/param.h> #include <wait.h> #include <string.h> #include <dkwindow.h> #define CTLFILE "/etc/procctl" #define CTL2FILE "/etc/procctl.local" #define UIDFILE "/etc/procuid" #define UID2FILE "/etc/procuid.local" #define LOGLVL 1 #define SYSERR 010 /* system error, "something is wrong" */ #define BUSY 011 /* destination busy */ #define NOCMC 012 /* remote node not answering */ #define NODEST 013 /* destination not answering */ #define INTERT 014 /* unassigned number */ #define REORT 015 /* system overload */ #define PERMISSION 017 /* permission denied */ /* * format of procctl file entry -- * * field 1 - source name, or * for any, of requester for this entry * field 2 - service type requested, character string * field 3 - converted service name for local use in this program * field 4 - parameters, use depends on service type * */ struct sprocctl { char * p_sname ; /* source name */ char * p_code ; /* service type */ char * p_ncode ; /* case stmt type */ char * p_param ; /* params for sub-case */ } procctl[64] ; /* * format of procuid file entry --- * * field 1 - source name, or * for any, or requester for this entry * field 2 - converted service name requested * field 3 - uid as passed from source nachine, of * for any * field 4 - uid to use locally, or * for same as source */ struct sprocuid { char * u_sname ; /* source name */ char * u_code ; /* service type */ char * u_uid ; /* source provided uid */ char * u_nuid ; /* local uid */ } procuid[256] ; FILE *fprocctl, *f2procctl, *fprocuid, *f2procuid ; /* file descriptor for control file */ long tprocctl, t2procctl, tprocuid, t2procuid ; /* last ctime of file */ int nprocctl, nprocuid ; /* number of entries read from file */ int zprocctl, zprocuid ; /* size of file at last look */ char *bprocctl, *bprocuid ; /* where in memory the file lives */ /* * defines for a character string "switch" statement * SSWITCH(string) { * SCASE("value") * statements ; * SCASE("value") * statements ; * } */ #define SSWITCH(c) SSTR=c;if(0) #define SCASE(c) }else if (strcmp(SSTR,c)==0){ char * SSTR ; char *ncode ; /* converted service code */ char uid[16] ; /* uid if special */ char dkname[32] ; /* system name from /etc/whoami */ char *params ; /* parameters for sub-case handling */ char parmbuf[512] ; /* for additional parameters */ short parmlen ; /* length of additional stuff */ char env1[64]; extern char **environ ; static char *envinit[] = { env1, 0 } ; char *oursrv ; /* pointer to our server name */ struct mgrmsg *imsg ; /* pointer to request message from remote */ struct passwd *pwent, *pwsearch(); /* password entry */ struct passwd *getnam(); int rootable ; /* should we allow root here? */ char pwline[256]; /* line from password file */ int proctab[512]; void chdies(), rmut() ; char *logfile = "/usr/dk/LOGPROC" ; char logbuf[BUFSIZ]; int loglvl = LOGLVL ; FILE *logf ; struct sgttyb term ; char FROGS[] = "\n&;^|<>(){}"; /* disallowed argument characters */ extern int getopt(), optind; extern char *optarg; extern char *dkfilename(); main(argc, argv) char **argv ; { register short i ; register short fi ; short f2 ; FILE * fip ; extern int dkmgropen ; struct mgrmsg *dkmgr() ; extern int errno, dkp_ld, tty_ld, cdkp_ld, rmesg_ld ; register char *cp, *filename; int msg, tty, wargs; int traffic = 2; fi = open("/etc/whoami", 0) ; if (fi < 0) { perror("mgrproc: open /etc/whoami: ") ; exit(1) ; } i = read(fi, dkname, sizeof(dkname)) ; if (i <= 0) { printf("bad read of /etc/whoami\n") ; exit(1) ; } dkname[i] = '\0' ; if ((cp = strchr(dkname, '\n'))) *cp = '\0'; close(fi) ; oursrv = dkname; while ((i = getopt(argc, argv, "s:t:v:l:")) != EOF) { switch(i) { case 's': /* server */ oursrv = optarg; continue; case 't': /* traffic class */ traffic = atoi(optarg); continue; case 'v': /* verbosity of logfile comments */ loglvl = atoi(optarg); continue; case 'l': /* name of logfile */ logfile = optarg; continue; default: exit(1); } } if (i = fork()) { printf("mgrproc: starting server %s on system %s, pid %d\n", oursrv, dkname, i) ; exit(0) ; /* parent exits, child continues */ } logf = fopen(logfile, "a") ; if (logf == NULL) printf("cannot open/create log file\n") ; else setbuf(logf, logbuf); signal(SIGINT, SIG_IGN) ; signal(SIGQUIT, SIG_IGN) ; signal(SIGHUP, SIG_IGN) ; signal(SIGTERM, SIG_IGN) ; signal(SIGPIPE, SIG_IGN) ; signal(SIGALRM, SIG_IGN) ; signal(SIGCHLD, chdies) ; pwsearch("root", -1, pwline); /* prime passwd file lookup */ loadfiles() ; for (;;) { imsg = dkmgr(oursrv, traffic) ; if (imsg == NULL) { if (errno == EINTR) { # define INULL (int *)NULL while ((i = wait3(INULL, WNOHANG, INULL)) > 0) { register j; for (j=0; j<512; j++) if (proctab[j]==i) { rmut(j); proctab[j] = 0; break; } dolog(3, "CHILD DIES c=%d\n", j) ; } continue ; } perror("mgrproc error in dkmgr: ") ; exit(1) ; } if (imsg->m_service == NULL) imsg->m_service = "(NULL)" ; /* default service */ if (imsg->m_uid == NULL || imsg->m_uid[0] == 0) imsg->m_uid = imsg->m_origin ; if (imsg->m_source == NULL) imsg->m_source = "(NULL)" ; dolog(1, "REQUEST c=%d, t=%s, UID=%s, from %s traf %x\n", imsg->m_chan, imsg->m_service, imsg->m_uid, imsg->m_source, imsg->m_traffic&0xFFFF) ; for (cp=imsg->m_service; *cp; cp++) if (*cp == '.') *cp = '\0' ; loadfiles() ; for (i=0; i<nprocctl; i++) { if (strcmp(procctl[i].p_code, imsg->m_service) == 0 && cksource(procctl[i].p_sname, imsg->m_source)) { ncode = procctl[i].p_ncode ; params = procctl[i].p_param ; goto gotit ; } } dolog(0, "ILLEGAL REQUEST chan %d\n", imsg->m_chan) ; dkmgrnak(imsg->m_chan, INTERT) ; continue ; gotit: pwent = NULL; rootable = 0 ; if (strcmp(imsg->m_uid, "(NULL)")) { for (i=0; i<nprocuid; i++) { if (cksource(procuid[i].u_sname, imsg->m_source) && cksource(procuid[i].u_code, imsg->m_service) && cksource(procuid[i].u_uid, imsg->m_uid)) { if (procuid[i].u_nuid[0] != '*') { dolog(1, "UID %s.%s -> %s\n", imsg->m_source, imsg->m_uid, procuid[i].u_nuid) ; imsg->m_uid = procuid[i].u_nuid ; rootable = 1 ; } goto gotuid ; } } imsg->m_uid = "**BAD**" ; gotuid: pwent = pwsearch(imsg->m_uid, -1, pwline); } if ((i = fork()) > 0) { proctab[imsg->m_chan] = i; continue ; } else if (i < 0) { dolog(0, "ERROR can't fork"); dkmgrnak(imsg->m_chan, NODEST); continue; } filename = dkfilename(imsg->m_chan); if (filename == NULL) { dolog(0, "Can't find file for chan %d\n", imsg->m_chan); dkmgrnak(imsg->m_chan, NODEST); exit(1); } f2 = open(filename, 2) ; if (f2 < 0) { dolog(0, "ERROR cannot open %s\n", filename); dkmgrnak(imsg->m_chan, NODEST) ; /* error */ exit(1) ; } ioctl(f2, DIOCNXCL, 0); dolog(7, "DEBUG ncode %s\n", ncode) ; environ = envinit ; sprintf(environ[0], "DKSOURCE=%s.%s", imsg->m_source, imsg->m_uid); SSWITCH(ncode) { SCASE("login") if (dkproto(f2, cdkp_ld) < 0 || ioctl(f2, FIOPUSHLD, &tty_ld) < 0) { dolog(0, "FAILED PUSHLD %s\n", ncode) ; dkmgrnak(imsg->m_chan, REORT) ; exit(1) ; } dkmgrack(imsg->m_chan) ; setfd(f2) ; execl("/etc/login", "login", 0) ; execl("/bin/login", "login", 0) ; dolog(0, "FAILED EXEC login\n") ; exit(1) ; SCASE("dcon") msg = 0; goto dc; SCASE("mesgdcon") msg = 1; dc: if (dkproto(f2, dkp_ld) < 0) { dolog(0, "FAILED PUSHLD %s\n", ncode) ; dkmgrnak(imsg->m_chan, REORT) ; exit(1) ; } dkmgrack(imsg->m_chan) ; setwins(f2, imsg->m_traffic); pwent = getnam(imsg->m_uid, f2, pwent, 0) ; if (pwent == NULL) { dolog(0,"FAILED passwd %s\n",imsg->m_uid); exit(1) ; } setfd(f2) ; if (msg) ioctl(0, FIOPUSHLD, &rmesg_ld); else ioctl(0, FIOPUSHLD, &tty_ld); execl("/etc/login", "login", "-p", pwline, 0) ; execl("/bin/login", "login", "-p", pwline, 0) ; dolog(0, "FAILED EXEC login\n") ; exit(1); SCASE("mesgexec") msg = 1; tty = 0; goto ex; SCASE("exec") msg = 0; tty = 0; goto ex; SCASE("ttyexec") msg = 0; tty = 1; ex: if (dkproto(f2, dkp_ld)<0) { dolog(0, "FAILED PUSHLD %s\n", ncode) ; dkmgrnak(imsg->m_chan, REORT) ; exit(1) ; } dkmgrack(imsg->m_chan) ; setwins(f2, imsg->m_traffic); pwent = getnam(imsg->m_uid, f2, pwent, 0) ; if (pwent == NULL) exit(0) ; setfd(f2) ; if (rparm(0, "") < 0) exit(0) ; if (msg) { if (ioctl(0, FIOPUSHLD, &rmesg_ld) < 0) { dolog(0, "FAILED PUSHLD(rmesg)\n"); exit(1) ; } } if (tty) { if (ioctl(0, FIOPUSHLD, &tty_ld)<0) { dolog(0, "FAILED PUSHLD(tty)\n"); exit(1) ; } } execl("/etc/login", "login", "-p", pwline, parmbuf, 0); execl("/bin/login", "login", "-p", pwline, parmbuf, 0); dolog(0, "FAILED EXEC login\n"); exit(1) ; SCASE("cmda") /* fixed cmd, user args */ wargs = 1; goto cm; SCASE("cmd") /* fixed cmd, fixed args */ wargs = 0; goto cm; cm: /* first param is uid, rest go to sh */ dolog(7, "DEBUG cmd %s\n", params) ; if (dkproto(f2, dkp_ld)<0) { dolog(0, "FAILED PUSHLD %s\n", ncode) ; dkmgrnak(imsg->m_chan, REORT) ; exit(1) ; } cp = params ; while (*cp != ' ' && *cp != '\t' && *cp != '\0') cp++ ; *cp++ = '\0' ; while (*cp == ' ' || *cp == '\t') cp++ ; if (params[0] == '*') { params = imsg->m_uid ; if (pwent == NULL || (pwent->pw_uid==0 && rootable==0)) { dkmgrnak(imsg->m_chan, PERMISSION) ; dolog(1, "FAILED procuid %s.%s\n", imsg->m_source, imsg->m_uid) ; exit(1) ; } } else { pwent = pwsearch(params, -1, pwline); rootable = 1 ; } dkmgrack(imsg->m_chan) ; setwins(f2, imsg->m_traffic); setfd(f2) ; dolog(7, "DEBUG cmd uid %s cmd %s\n", params, cp) ; if (wargs) { register char *cp1; char tparmbuf[786]; pwent = getnam(params, 0, pwent, rootable); if (rparm(0, FROGS) < 0) { dolog(0, "bad args, cmda\n"); exit(0); } /* copy cmd name from procctl, args from parmbuf */ cp1 = tparmbuf; while (*cp != ' ' && *cp != '\t' && *cp != '\0') *cp1++ = *cp++ ; *cp1++ = ' '; cp = parmbuf; while ((*cp1++ = *cp++) != '\0') ; strcpy(parmbuf, tparmbuf); cp = parmbuf; dolog(7, "cmda <%s>\n", cp); } execl("/etc/login", "login", "-p", pwline, cp, 0) ; execl("/bin/login", "login", "-p", pwline, cp, 0) ; dolog(0, "FAILED EXEC login %s\n", cp) ; exit(1) ; } dolog(0, "ILLEGAL CODE %s\n", ncode) ; dkmgrnak(imsg->m_chan, INTERT) ; exit(1) ; } } /* VARARGS2 */ dolog(level, fmt, a1, a2, a3, a4, a5) char *fmt; { long clock ; long time() ; char *ctime() ; if (loglvl<level || logf==NULL) return; clock = time(0) ; fseek(logf, 0L, 2); fprintf(logf, "%.15s-%d(%d) ", ctime(&clock)+4, getpid(), loglvl) ; fprintf(logf, fmt, a1, a2, a3, a4, a5); fflush(logf); } /* * Interrupt routine for child death */ void chdies() { signal(SIGCHLD, chdies); } /* * delete entry from utmp file */ void rmut(i) { register f; register char *line; struct utmp wtmp; line = dkfilename(i); if (line==0) return; line += sizeof("/dev/") - 1; f = open("/etc/utmp", 2); if(f >= 0) { while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) { if (strncmp(wtmp.ut_line, line, sizeof(wtmp.ut_line))) continue; lseek(f, -(long)sizeof(wtmp), 1); strncpy(wtmp.ut_name, "", sizeof(wtmp.ut_name)); time(&wtmp.ut_time); write(f, (char *)&wtmp, sizeof(wtmp)); } close(f); } f = open("/usr/adm/wtmp", 1); if (f >= 0) { strncpy(wtmp.ut_line, line, sizeof(wtmp.ut_line)); strncpy(wtmp.ut_name, "", sizeof(wtmp.ut_name)); time(&wtmp.ut_time); lseek(f, (long)0, 2); write(f, (char *)&wtmp, sizeof(wtmp)); close(f); } } /* * check a source name against a prototype name * in the control file * return 0 if no match * return 1 if ok match */ cksource(ck, src) register char *ck, *src ; { while (*ck == *src) { if (*ck == 0) break ; ck++ ; src++ ; } if (*ck == *src) return 1 ; if (*ck == '*') return 1 ; return 0 ; } struct passwd * getnam(try1, f2, pw, root) char * try1; register struct passwd *pw ; int root ; { register char * cp ; if (pw && (pw->pw_uid!=0 || root!=0)) { write(f2, "OK", 2) ; return pw ; } write(f2, "NO", 2); while (1) { if (rparm(f2, "") < 0) { dolog(2, "HANGUP c=%d receiving uid\n", imsg->m_chan) ; exit(1) ; } for (cp = parmbuf; *cp; cp++) { if (*cp == ' ' || *cp == '.' || *cp == ',') { *cp++ = '\0'; break ; } } pw = pwsearch(parmbuf, -1, pwline) ; if (pw && pw->pw_uid != 0 && (pw->pw_passwd==NULL || strcmp(crypt(cp, pw->pw_passwd), pw->pw_passwd)==0)) break; write(f2, "NO", 2) ; } dolog(4, "TRACE UID %s\n", parmbuf) ; write(f2, "OK", 2) ; return pw ; } /* read command parameters, delete funny ones */ rparm(f, frogs) register char *frogs; { register len ; register rlen ; register char *cp ; rlen = sizeof(parmbuf) ; parmlen = 0 ; cp = parmbuf ; while (1) { len = read(f, cp, rlen) ; if (len <= 0) return -1 ; parmlen += len ; rlen -= len ; cp += len - 1 ; if (*cp == '\n' || *cp == '\r') { *cp = '\0' ; cp = parmbuf; while (*cp) { *cp &= 0177; if (strchr(frogs, *cp)) *cp = 'X'; cp++; } dolog(7, "DEBUG rparam %s\n", parmbuf) ; return 0 ; } cp++ ; } } setfd(f) { int i ; signal(SIGTERM, SIG_DFL) ; signal(SIGPIPE, SIG_DFL) ; signal(SIGQUIT, SIG_DFL) ; signal(SIGINT, SIG_DFL) ; signal(SIGALRM, SIG_DFL) ; signal(SIGHUP, SIG_DFL) ; signal(SIGCHLD, SIG_DFL) ; ioctl(f, TIOCSPGRP, 0) ; close(0) ; close(1) ; close(2) ; close(3) ; dup(f) ; dup(f) ; dup(f) ; dup(f) ; for (i=NSYSFILE; i<9; i++) if (i != fileno(logf)) close(i) ; } /* use large transmit window? */ setwins(f, traffic) { char ws[5]; long wins; if (W_VALID(traffic) && W_TRAF(traffic)) { wins = W_VALUE(W_ORIG(traffic)); /* try 3 X 1/4 */ wins >>= 2; ws[0] = wins; ws[1] = wins>>8; ws[2] = 0; ws[3] = 0; ws[4] = 3; ioctl(f, DIOCXWIN, ws); } } char * adv(cp) register char *cp ; { while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t') cp++ ; if (*cp == ' ' || *cp == '\t') *cp++ = '\0' ; while (*cp == ' ' || *cp == '\t') cp++ ; return (cp) ; } char * advn(cp) register char *cp ; { while (*cp != '\0' && *cp != '\n') cp++ ; if (*cp == '\n') *cp++ = '\0' ; while (*cp == ' ' || *cp == '\t' || *cp == '\n') cp++ ; return (cp) ; } loadfiles() { struct stat statb ; struct stat statb2 ; register char *cp ; register struct sprocctl *ctlptr ; register struct sprocuid *uidptr ; register i ; f0: if (fprocctl == NULL || f2procctl == NULL) { if (fprocctl == NULL) fprocctl = fopen(CTLFILE, "r") ; if (f2procctl == NULL) f2procctl = fopen(CTL2FILE, "r") ; if (fprocctl == NULL) { fprintf(stderr, "mgrproc, can't open %s\n", CTLFILE) ; nprocctl = 0 ; goto f2 ; } if (f2procctl == NULL) { fprintf(stderr, "mgrproc, can't open %s\n", CTL2FILE) ; nprocctl = 0 ; goto f2 ; } fstat(fileno(fprocctl), &statb) ; fstat(fileno(f2procctl), &statb2) ; } else { fstat(fileno(fprocctl), &statb) ; fstat(fileno(f2procctl), &statb2) ; if (statb.st_ctime == tprocctl && statb2.st_ctime == t2procctl) goto f2 ; fclose(fprocctl) ; fclose(f2procctl) ; fprocctl = f2procctl = NULL ; goto f0 ; } tprocctl = statb.st_ctime ; t2procctl = statb2.st_ctime ; i = statb.st_size + statb2.st_size ; if (i > zprocctl) { if (zprocctl > 0) free(bprocctl) ; bprocctl = (char *)malloc(i+4) ; zprocctl = i ; } rewind(fprocctl) ; rewind(f2procctl) ; fread(bprocctl, statb2.st_size, 1, f2procctl) ; if (statb2.st_size) fread(&bprocctl[statb2.st_size], statb.st_size, 1, fprocctl) ; bprocctl[i] = '\0' ; cp = bprocctl ; ctlptr = procctl ; while (*cp) { while (*cp == '#') cp = advn(cp) ; if (*cp == 0) break ; ctlptr->p_sname = cp ; cp = adv(cp) ; ctlptr->p_code = cp ; cp = adv(cp) ; ctlptr->p_ncode = cp ; if (*cp == '*') ctlptr->p_ncode = ctlptr->p_code ; cp = adv(cp) ; ctlptr->p_param = cp ; cp = advn(cp) ; ctlptr++ ; } nprocctl = ctlptr - procctl ; dolog(7, "Reloaded %s; %d entries\n", CTLFILE, nprocctl) ; f2: if (fprocuid == NULL || f2procuid == NULL) { if (fprocuid == NULL) fprocuid = fopen(UIDFILE, "r") ; if (f2procuid == NULL) f2procuid = fopen(UID2FILE, "r") ; if (fprocuid == NULL) { fprintf(stderr, "mgrproc: can't open %s\n", UIDFILE) ; nprocuid = 0 ; return ; } if (f2procuid == NULL) { fprintf(stderr, "mgrproc: can't open %s\n", UID2FILE) ; nprocuid = 0 ; return ; } fstat(fileno(fprocuid), &statb) ; fstat(fileno(f2procuid), &statb2) ; } else { fstat(fileno(fprocuid), &statb) ; fstat(fileno(f2procuid), &statb2) ; if (statb.st_ctime == tprocuid && statb2.st_ctime == t2procuid) return ; fclose(fprocuid) ; fclose(f2procuid) ; fprocuid = f2procuid = NULL ; goto f2 ; } tprocuid = statb.st_ctime ; t2procuid = statb2.st_ctime ; i = statb.st_size + statb2.st_size ; if (i > zprocuid) { if (zprocuid) free(bprocuid) ; bprocuid = (char *)malloc(i+4) ; zprocuid = i ; } rewind(fprocuid) ; rewind(f2procuid) ; fread(bprocuid, statb2.st_size, 1, f2procuid) ; fread(&bprocuid[statb2.st_size], statb.st_size, 1, fprocuid) ; bprocuid[i] = '\0' ; cp = bprocuid ; uidptr = procuid ; while (*cp) { while (*cp == '#') cp = advn(cp) ; if (*cp == '\0') break ; uidptr->u_sname = cp ; cp = adv(cp) ; uidptr->u_code = cp ; cp = adv(cp) ; uidptr->u_uid = cp ; cp = adv(cp) ; uidptr->u_nuid = cp ; cp = adv(cp) ; cp = advn(cp) ; uidptr++ ; } nprocuid = uidptr - procuid ; dolog(7, "Reloaded %s; %d entries\n", UIDFILE, nprocuid) ; }