Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/5/84; site cray.UUCP Path: utzoo!watmath!clyde!burl!ulysses!mhuxr!mhuxn!ihnp4!stolaf!cray!prb From: prb@cray.UUCP (Paul Borman) Newsgroups: net.unix-wizards Subject: Re: Re: Strange behavior of su Message-ID: <288@cray.UUCP> Date: Fri, 27-Sep-85 23:22:55 EDT Article-I.D.: cray.288 Posted: Fri Sep 27 23:22:55 1985 Date-Received: Mon, 30-Sep-85 01:12:02 EDT References: <314@aphasia.UUCP>, <323@uwvax.UUCP> <667@bu-cs.UUCP> Organization: Cray Research Inc., Mendota Heights, MN Lines: 624 No one is crazy. This is a bug. It can be fixed (without chdiring to / (which wont work on a system which has / as 711)). Solution: change sh.dir.c so that if you are not superuser it will call /bin/pwd to find your current working directory. make /bin/pwd suid to root. This is NOT a security problem. Enhancement: put your current working dir into a environ var called CWD and your inode/device into CWDINO. Then, when the csh starts up, it stats . and sees if CWDINO is right, if so, then just use CWD, else call pwd. Hell, I'll just put the sources in here.... The #ifdef SYSV is the stuff you have to add to make it work. I ported 4.2 csh to System V and fixed the bug (again) while I was doing it. The 3rd file is just along for the ride. -Paul R Borman Cray Research, Inc. ihnp4!cray!prb ----sh.dir.c---- static char *sccsid = "@(#)sh.dir.c 4.2 2/3/83"; #include "sh.h" #include "sh.dir.h" /* * C Shell - directory management */ struct directory *dfind(); char *dfollow(); struct directory dhead; /* "head" of loop */ int printd; /* force name to be printed */ static char *fakev[] = { "dirs", NOSTR }; /* * dinit - initialize current working directory */ dinit(hp) char *hp; { register char *cp; register struct directory *dp; char path[BUFSIZ]; if (loginsh && hp) cp = hp; else { #ifdef SYSV cp = getwd_up(path); #else cp = getwd(path); #endif if (cp == NULL) { write(2, path, strlen(path)); exit(1); } } dp = (struct directory *)calloc(sizeof (struct directory), 1); dp->di_name = savestr(cp); dp->di_count = 0; dhead.di_next = dhead.di_prev = dp; dp->di_next = dp->di_prev = &dhead; printd = 0; dnewcwd(dp); } /* * dodirs - list all directories in directory loop */ dodirs(v) char **v; { register struct directory *dp; bool lflag; char *hp = value("home"); if (*hp == '\0') hp = NOSTR; if (*++v != NOSTR) if (eq(*v, "-l") && *++v == NOSTR) lflag = 1; else error("Usage: dirs [ -l ]"); else lflag = 0; dp = dcwd; do { if (dp == &dhead) continue; if (!lflag && hp != NOSTR) { dtildepr(hp, dp->di_name); } else printf("%s", dp->di_name); printf(" "); } while ((dp = dp->di_prev) != dcwd); printf("\n"); } dtildepr(home, dir) register char *home, *dir; { if (!eq(home, "/") && prefix(home, dir)) printf("~%s", dir + strlen(home)); else printf("%s", dir); } /* * dochngd - implement chdir command. */ dochngd(v) char **v; { register char *cp; register struct directory *dp; printd = 0; if (*++v == NOSTR) { if ((cp = value("home")) == NOSTR || *cp == 0) bferr("No home directory"); if (chdir(cp) < 0) bferr("Can't change to home directory"); cp = savestr(cp); } else if ((dp = dfind(*v)) != 0) { printd = 1; if (chdir(dp->di_name) < 0) Perror(dp->di_name); dcwd->di_prev->di_next = dcwd->di_next; dcwd->di_next->di_prev = dcwd->di_prev; goto flushcwd; } else cp = dfollow(*v); dp = (struct directory *)calloc(sizeof (struct directory), 1); dp->di_name = cp; dp->di_count = 0; dp->di_next = dcwd->di_next; dp->di_prev = dcwd->di_prev; dp->di_prev->di_next = dp; dp->di_next->di_prev = dp; flushcwd: dfree(dcwd); dnewcwd(dp); } /* * dfollow - change to arg directory; fall back on cdpath if not valid */ char * dfollow(cp) register char *cp; { register char **cdp; struct varent *c; cp = globone(cp); if (chdir(cp) == 0) goto gotcha; if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp) && (c = adrof("cdpath"))) { for (cdp = c->vec; *cdp; cdp++) { char buf[BUFSIZ]; strcpy(buf, *cdp); strcat(buf, "/"); strcat(buf, cp); if (chdir(buf) >= 0) { printd = 1; xfree(cp); cp = savestr(buf); goto gotcha; } } } if (adrof(cp)) { char *dp = value(cp); if (dp[0] == '/' || dp[0] == '.') if (chdir(dp) >= 0) { xfree(cp); cp = savestr(dp); printd = 1; goto gotcha; } } xfree(cp); Perror(cp); gotcha: if (*cp != '/') { char *dp = calloc(strlen(cp) + strlen(dcwd->di_name) + 2, 1); strcpy(dp, dcwd->di_name); strcat(dp, "/"); strcat(dp, cp); xfree(cp); cp = dp; } dcanon(cp); return (cp); } /* * dopushd - push new directory onto directory stack. * with no arguments exchange top and second. * with numeric argument (+n) bring it to top. */ dopushd(v) char **v; { register struct directory *dp; printd = 1; if (*++v == NOSTR) { if ((dp = dcwd->di_prev) == &dhead) dp = dhead.di_prev; if (dp == dcwd) bferr("No other directory"); if (chdir(dp->di_name) < 0) Perror(dp->di_name); dp->di_prev->di_next = dp->di_next; dp->di_next->di_prev = dp->di_prev; dp->di_next = dcwd->di_next; dp->di_prev = dcwd; dcwd->di_next->di_prev = dp; dcwd->di_next = dp; } else if (dp = dfind(*v)) { if (chdir(dp->di_name) < 0) Perror(dp->di_name); } else { register char *cp; cp = dfollow(*v); dp = (struct directory *)calloc(sizeof (struct directory), 1); dp->di_name = cp; dp->di_count = 0; dp->di_prev = dcwd; dp->di_next = dcwd->di_next; dcwd->di_next = dp; dp->di_next->di_prev = dp; } dnewcwd(dp); } /* * dfind - find a directory if specified by numeric (+n) argument */ struct directory * dfind(cp) register char *cp; { register struct directory *dp; register int i; register char *ep; if (*cp++ != '+') return (0); for (ep = cp; digit(*ep); ep++) continue; if (*ep) return (0); i = getn(cp); if (i <= 0) return (0); for (dp = dcwd; i != 0; i--) { if ((dp = dp->di_prev) == &dhead) dp = dp->di_prev; if (dp == dcwd) bferr("Directory stack not that deep"); } return (dp); } /* * dopopd - pop a directory out of the directory stack * with a numeric argument just discard it. */ dopopd(v) char **v; { register struct directory *dp, *p; printd = 1; if (*++v == NOSTR) dp = dcwd; else if ((dp = dfind(*v)) == 0) bferr("Bad directory"); if (dp->di_prev == &dhead && dp->di_next == &dhead) bferr("Directory stack empty"); if (dp == dcwd) { if ((p = dp->di_prev) == &dhead) p = dhead.di_prev; if (chdir(p->di_name) < 0) Perror(p->di_name); } dp->di_prev->di_next = dp->di_next; dp->di_next->di_prev = dp->di_prev; if (dp == dcwd) dnewcwd(p); else dodirs(fakev); dfree(dp); } /* * dfree - free the directory (or keep it if it still has ref count) */ dfree(dp) register struct directory *dp; { if (dp->di_count != 0) dp->di_next = dp->di_prev = 0; else xfree(dp->di_name), xfree((char *)dp); } /* * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. * we are of course assuming that the file system is standardly * constructed (always have ..'s, directories have links) */ dcanon(cp) char *cp; { register char *p, *sp; register bool slash; if (*cp != '/') abort(); for (p = cp; *p; ) { /* for each component */ sp = p; /* save slash address */ while(*++p == '/') /* flush extra slashes */ ; if (p != ++sp) strcpy(sp, p); p = sp; /* save start of component */ slash = 0; while(*++p) /* find next slash or end of path */ if (*p == '/') { slash = 1; *p = 0; break; } if (*sp == '\0') /* if component is null */ if (--sp == cp) /* if path is one char (i.e. /) */ break; else *sp = '\0'; else if (eq(".", sp)) { if (slash) { strcpy(sp, ++p); p = --sp; } else if (--sp != cp) *sp = '\0'; } else if (eq("..", sp)) { if (--sp != cp) while (*--sp != '/') ; if (slash) { strcpy(++sp, ++p); p = --sp; } else if (cp == sp) *++sp = '\0'; else *sp = '\0'; } else if (slash) *p = '/'; } } /* * dnewcwd - make a new directory in the loop the current one */ dnewcwd(dp) register struct directory *dp; { dcwd = dp; set("cwd", savestr(dcwd->di_name)); if (printd) dodirs(fakev); #ifdef SYSV dnewCWD(); #endif } ----sh.getwd_up.c---- #ifdef SYSV #include "sh.h" #define NULL 0 #define STREQ(a,b) (!strcmp(a,b)) /* * returns the current working directory ala pwd(1) * exits if it can't find it. the actual getwd(3J) * spits out a error message on stderr, but since csh(1) * doesn't have filedes 2 open, it makes no sense. * * up => unprivilaged */ char * getwd_up(s) char *s; { int p[2]; int ppid = getpid(); int cpid; int status; int r; char *s1; if (checkCWD(s)) return(s); if (pipe(p) < 0) exit(1); switch(cpid = fork()) { case -1: exit(1); case 0: close(p[0]); /* * This is actually the case in the csh(1). * csh(1) doesn't have 0 or 1 open, so when you do * a pipe, you end up with p[0] == 0 && p[1] == 1. */ if (p[1] != 1) { if (dup2(p[1], 1) < 0) exit(1); close(p[1]); } execl("/bin/pwd", "pwd", 0); exit(1); default: close(p[1]); /* * make sure we get the right kid */ while ((r = wait(&status)) != -1 && r != cpid) ; if ( r == -1) exit(1); break; } /* * we exit if the kid exited un-nornal like */ if (status & 0377) exit(1); if ((r = read(p[0], s, BUFSIZ)) <= 0) exit(1); close(p[0]); s[r] = '\0'; s1 = s; /* * Also, we only want up to and not including the first newline */ while (*s1) if (*s1++ == '\n') { *--s1 = '\0'; break; } return(s); } dnewCWD() { struct stat st; if (stat(".", &st) < 0) return; setenv("CWD", savestr(value("cwd"))); setenv("CWDINO", savestr(ltoo(((long)st.st_dev << 16) | ((long)st.st_ino)))); } char * checkCWD(s) char *s; { char *CWD; char *CWDINO; struct stat st; if (((CWD = getenv("CWD")) == NULL) || ((CWDINO = getenv("CWDINO")) == NULL) || (stat(".", &st) < 0)) return(NULL); if (STREQ(ltoo(((long)st.st_dev << 16) | (long)st.st_ino),CWDINO)) return(strcpy(s, CWD)); return(NULL); } char * ltoo(v) long v; { static char rbuf[16]; char *p = rbuf; while (v > 0) { *p++ = (v & 07) + '0'; v = v >> 3; } return(reverse(rbuf)); } char * reverse(s) char *s; { char *os = s; char *p = s; char c; while (*p) ++p; --p; while (s < p) { c = *s; *s++ = *p; *p-- = c; } return(os); } #endif ----sh.which.c--- #ifdef MOD_WHICH #include "sh.h" dowhich(v) char **v; { struct varent *vp; struct biltins *bp; int found; while (*++v) { found = 0; for (vp = aliases.link; vp; vp = vp->link) { if (!strcmp(vp->name, *v)) { palias(vp); ++found; } } #ifdef MOD_ASSIGN for (vp = assigns.link; vp; vp = vp->link) { if (!strcmp(vp->name, *v)) { passign(vp); ++found; } } #endif MOD_ASSIGN if (!found ) for (bp = bfunc; bp->bname; ++bp) { if (!strcmp(bp->bname, *v)) { printf("%s: is built into the csh\n", *v); ++found; } } if (!found) which(*v); } } palias(vp) struct varent *vp; { printf(vp->name); printf(": aliased to "); blkpr(vp->vec); printf("\n"); } #ifdef MOD_ASSIGN passign(vp) struct varent *vp; { printf(vp->name); printf(": assigned to "); blkpr(vp->vec); printf("\n"); } #endif MOD_ASSIGN which(file) char *file; { char tmp[100]; char *t = tmp; struct varent *vp; char **p; if (vp = adrof("path")) { p = vp->vec; for (p = vp->vec ; *p; ++p) { strcpy(tmp, *p); strcat(tmp, "/"); strcat(tmp, file); if (access(tmp, 1) == 0) { printf("%s\n",tmp); return; } } } printf("no %s in ", file); blkpr(adrof("path")->vec); printf("\n"); } #endif -- -Paul R Borman Cray Research, Inc. ...ihnp4!cray!prb Brought to you by Super Global Mega Corp .com