diff options
Diffstat (limited to '')
-rw-r--r-- | fileio.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/fileio.c b/fileio.c new file mode 100644 index 0000000..d488166 --- /dev/null +++ b/fileio.c @@ -0,0 +1,788 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, see + * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <pwd.h> + +#ifndef SIGINT +# include <signal.h> +#endif + +#include "config.h" +#include "screen.h" +#include "extern.h" + +extern struct display *display, *displays; +extern struct win *fore; +extern struct layer *flayer; +extern int ServerSocket; +extern int real_uid, eff_uid; +extern int real_gid, eff_gid; +extern char *extra_incap, *extra_outcap; +extern char *home, *RcFileName; +extern char SockPath[], *SockName; +#ifdef COPY_PASTE +extern char *BufferFile; +#endif +extern int hardcopy_append; +extern char *hardcopydir; + +static char *CatExtra __P((char *, char *)); +static char *findrcfile __P((char *)); + +char *rc_name = ""; +int rc_recursion = 0; + +static char * +CatExtra(register char *str1, register char *str2) +{ + register char *cp; + register int len1, len2, add_colon; + + len1 = strlen(str1); + if (len1 == 0) + return str2; + add_colon = (str1[len1 - 1] != ':'); + if (str2) { + len2 = strlen(str2); + if ((cp = realloc(str2, (unsigned)len1 + len2 + add_colon + 1)) == NULL) + Panic(0, "%s", strnomem); + bcopy(cp, cp + len1 + add_colon, len2 + 1); + } else { + if ((cp = malloc((unsigned)len1 + add_colon + 1)) == NULL) + Panic(0, "%s", strnomem); + cp[len1 + add_colon] = '\0'; + } + bcopy(str1, cp, len1); + if (add_colon) + cp[len1] = ':'; + return cp; +} + +static char * +findrcfile(char *rcfile) +{ + char buf[256]; + char *p; + + /* Tilde prefix support courtesy <hesso@pool.math.tu-berlin.de>, + * taken from a Debian patch. */ + if (rcfile && *rcfile == '~') { + static char rcfilename_tilde_exp[MAXPATHLEN + 1]; + char *slash_position = strchr(rcfile, '/'); + + if (slash_position == rcfile + 1) { + char *home = getenv("HOME"); + if (!home) { + Msg(0, "%s: source: tilde expansion failed", rc_name); + return NULL; + } + snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", home, rcfile + 2); + } else if (slash_position) { + struct passwd *p; + *slash_position = 0; + p = getpwnam(rcfile + 1); + if (!p) { + Msg(0, "%s: source: tilde expansion failed for user %s", rc_name, rcfile + 1); + return NULL; + } + snprintf(rcfilename_tilde_exp, MAXPATHLEN, "%s/%s", p->pw_dir, slash_position + 1); + } else { + Msg(0, "%s: source: illegal tilde expression.", rc_name); + return NULL; + } + rcfile = rcfilename_tilde_exp; + } + + if (rcfile) { + char *rcend = rindex(rc_name, '/'); + if (*rcfile != '/' && rcend && (rcend - rc_name) + strlen(rcfile) + 2 < sizeof(buf)) { + strncpy(buf, rc_name, rcend - rc_name + 1); + strcpy(buf + (rcend - rc_name) + 1, rcfile); + if (access(buf, R_OK) == 0) + return SaveStr(buf); + } + debug1("findrcfile: you specified '%s'\n", rcfile); + return SaveStr(rcfile); + } + + debug("findrcfile: you specified nothing...\n"); + if ((p = getenv("SCREENRC")) != NULL && *p != '\0') { + debug1(" $SCREENRC has: '%s'\n", p); + return SaveStr(p); + } else { + debug(" ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n"); + if (strlen(home) > sizeof(buf) - 12) + Panic(0, "Rc: home too large"); + sprintf(buf, "%s/.screenrc", home); + return SaveStr(buf); + } +} + +/* + * this will be called twice: + * 1) rcfilename = "/etc/screenrc" + * 2) rcfilename = RcFileName + */ +int +StartRc(char *rcfilename, int nopanic) +{ + register int argc, len; + register char *p, *cp; + char buf[2048]; + char *args[MAXARGS]; + int argl[MAXARGS]; + FILE *fp; + char *oldrc_name = rc_name; + + /* always fix termcap/info capabilities */ + extra_incap = CatExtra("TF", extra_incap); + + /* Special settings for vt100 and others */ + if (display && (!strncmp(D_termname, "vt", 2) || !strncmp(D_termname, "xterm", 5))) + extra_incap = CatExtra("xn:f0=\033Op:f1=\033Oq:f2=\033Or:f3=\033Os:f4=\033Ot:f5=\033Ou:f6=\033Ov:f7=\033Ow:f8=\033Ox:f9=\033Oy:f.=\033On:f,=\033Ol:fe=\033OM:f+=\033Ok:f-=\033Om:f*=\033Oj:f/=\033Oo:fq=\033OX", extra_incap); + + rc_name = findrcfile(rcfilename); + if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL) { + const char *rc_nonnull = rc_name ? rc_name : rcfilename; + if (!rc_recursion && RcFileName && !strcmp(RcFileName, rc_nonnull)) { + /* + * User explicitly gave us that name, + * this is the only case, where we get angry, if we can't read + * the file. + */ + debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename); + if (!nopanic) + Panic(0, "Unable to open \"%s\".", rc_nonnull); + /* possibly NOTREACHED */ + } + + debug1("StartRc: '%s' no good. ignored\n", rc_nonnull); + if (rc_name) + Free(rc_name); + rc_name = oldrc_name; + return 1; + } + while (fgets(buf, sizeof buf, fp) != NULL) { + if ((p = rindex(buf, '\n')) != NULL) + *p = '\0'; + + if ((argc = Parse(buf, sizeof buf, args, argl)) == 0) + continue; + + if (strcmp(args[0], "echo") == 0) { + if (!display) + continue; + if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3) { + Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name); + continue; + } + AddStr(args[argc - 1]); + if (argc != 3) { + AddStr("\r\n"); + Flush(0); + } + } else if (strcmp(args[0], "sleep") == 0) { + if (!display) + continue; + debug("sleeeeeeep\n"); + if (argc != 2) { + Msg(0, "%s: sleep: one numeric argument expected.", rc_name); + continue; + } + DisplaySleep1000(1000 * atoi(args[1]), 1); + } +#ifdef TERMINFO + else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "terminfo")) { +#else + else if (!strcmp(args[0], "termcapinfo") || !strcmp(args[0], "termcap")) { +#endif + if (!display) + continue; + if (argc < 3 || argc > 4) { + Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]); + continue; + } + + for (p = args[1]; p && *p; p = cp) { + if ((cp = index(p, '|')) != 0) + *cp++ = '\0'; + len = strlen(p); + if (p[len - 1] == '*') { + if (!(len - 1) || !strncmp(p, D_termname, len - 1)) + break; + } else if (!strcmp(p, D_termname)) + break; + } + if (!(p && *p)) + continue; + extra_incap = CatExtra(args[2], extra_incap); + if (argc == 4) + extra_outcap = CatExtra(args[3], extra_outcap); + } else if (!strcmp(args[0], "source")) { + if (rc_recursion <= 10) { + rc_recursion++; + (void)StartRc(args[1], 0); + rc_recursion--; + } + } + } + fclose(fp); + Free(rc_name); + rc_name = oldrc_name; + return 0; +} + +void +FinishRc(char *rcfilename) +{ + char buf[2048]; + FILE *fp; + char *oldrc_name = rc_name; + + rc_name = findrcfile(rcfilename); + + if (rc_name == NULL || (fp = secfopen(rc_name, "r")) == NULL) { + const char *rc_nonnull = rc_name ? rc_name : rcfilename; + if (rc_recursion) + Msg(errno, "%s: source %s", oldrc_name, rc_nonnull); + else if (RcFileName && !strcmp(RcFileName, rc_nonnull)) { + /* + * User explicitly gave us that name, + * this is the only case, where we get angry, if we can't read + * the file. + */ + debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name ? rc_name : "(null)", rcfilename); + Panic(0, "Unable to open \"%s\".", rc_nonnull); + /* NOTREACHED */ + } + debug1("FinishRc: '%s' no good. ignored\n", rc_nonnull); + if (rc_name) + Free(rc_name); + rc_name = oldrc_name; + return; + } + + debug("finishrc is going...\n"); + while (fgets(buf, sizeof buf, fp) != NULL) + RcLine(buf, sizeof buf); + (void)fclose(fp); + Free(rc_name); + rc_name = oldrc_name; +} + +void +do_source(char *rcfilename) +{ + if (rc_recursion > 10) { + Msg(0, "%s: source: recursion limit reached", rc_name); + return; + } + rc_recursion++; + FinishRc(rcfilename); + rc_recursion--; +} + +/* + * Running a Command Line in the environment determined by the display. + * The fore window is taken from the display as well as the user. + * This is bad when we run detached. + */ +void +RcLine(char *ubuf, int ubufl) +{ + char *args[MAXARGS]; + int argl[MAXARGS]; +#ifdef MULTIUSER + extern struct acluser *EffectiveAclUser; /* acl.c */ + extern struct acluser *users; /* acl.c */ +#endif + + if (display) { + fore = D_fore; + flayer = D_forecv->c_layer; + } else + flayer = fore ? fore->w_savelayer : 0; + if (Parse(ubuf, ubufl, args, argl) <= 0) + return; + +#ifdef MULTIUSER + if (!display) { + /* the session owner does it, when there is no display here */ + EffectiveAclUser = users; + debug("RcLine: WARNING, no display no user! Session owner executes command\n"); + } +#endif + + DoCommand(args, argl); + +#ifdef MULTIUSER + EffectiveAclUser = 0; +#endif +} + +/* needs display for copybuffer access and termcap dumping */ +void +WriteFile(struct acluser *user, char *fn, int dump) +{ + /* dump==0: create .termcap, + * dump==1: hardcopy, + * #ifdef COPY_PASTE + * dump==2: BUFFERFILE + * #endif COPY_PASTE + * dump==1: scrollback, + */ + register int i, j, k; + register char *p; + register FILE *f; + char fnbuf[1024]; + char *mode = "w"; + +#ifdef COPY_PASTE + int public = 0; +# ifdef HAVE_LSTAT + struct stat stb, stb2; + int fd, exists = 0; +# endif +#endif + + switch (dump) { + case DUMP_TERMCAP: + if (fn == 0) { + i = SockName - SockPath; + if (i > (int)sizeof(fnbuf) - 9) + i = 0; + strncpy(fnbuf, SockPath, i); + strcpy(fnbuf + i, ".termcap"); + fn = fnbuf; + } + break; + + case DUMP_HARDCOPY: + case DUMP_SCROLLBACK: + if (fn == 0) { + if (fore == 0) + return; + if (hardcopydir && *hardcopydir && strlen(hardcopydir) < sizeof(fnbuf) - 21) + sprintf(fnbuf, "%s/hardcopy.%d", hardcopydir, fore->w_number); + else + sprintf(fnbuf, "hardcopy.%d", fore->w_number); + fn = fnbuf; + } + if (hardcopy_append && !access(fn, W_OK)) + mode = "a"; + break; + +#ifdef COPY_PASTE + case DUMP_EXCHANGE: + if (fn == 0) { + strncpy(fnbuf, BufferFile, sizeof(fnbuf) - 1); + fnbuf[sizeof(fnbuf) - 1] = 0; + fn = fnbuf; + } + public = !strcmp(fn, DEFAULT_BUFFERFILE); +# ifdef HAVE_LSTAT + exists = !lstat(fn, &stb); + if (public && exists && (S_ISLNK(stb.st_mode) || stb.st_nlink > 1)) { + Msg(0, "No write to links, please."); + return; + } +# endif + break; +#endif + } + + debug2("WriteFile(%d) %s\n", dump, fn); + if (UserContext() > 0) { + debug("Writefile: usercontext\n"); +#ifdef COPY_PASTE + if (dump == DUMP_EXCHANGE && public) { +# ifdef HAVE_LSTAT + if (exists) { + if ((fd = open(fn, O_WRONLY, 0666)) >= 0) { + if (fstat(fd, &stb2) == 0 && stb.st_dev == stb2.st_dev && stb.st_ino == stb2.st_ino) + ftruncate(fd, 0); + else { + close(fd); + fd = -1; + } + } + } else + fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0666); + f = fd >= 0 ? fdopen(fd, mode) : 0; +# else + f = fopen(fn, mode); +# endif + } else +#endif /* COPY_PASTE */ + f = fopen(fn, mode); + if (f == NULL) { + debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode); + UserReturn(0); + } else { + switch (dump) { + case DUMP_HARDCOPY: + case DUMP_SCROLLBACK: + if (!fore) + break; + if (*mode == 'a') { + putc('>', f); + for (j = fore->w_width - 2; j > 0; j--) + putc('=', f); + fputs("<\n", f); + } + if (dump == DUMP_SCROLLBACK) { +#ifdef COPY_PASTE + for (i = fore->w_histheight - fore->w_scrollback_height; i < fore->w_histheight; i++) { + p = (char *)(WIN(i)->image); + for (k = fore->w_width - 1; + k >= 0 && p[k] == ' '; k--) + ; + for (j = 0; j <= k; j++) + putc(p[j], f); + putc('\n', f); + } +#endif + } + for (i = 0; i < fore->w_height; i++) { + p = (char *)fore->w_mlines[i].image; + for (k = fore->w_width - 1; + k >= 0 && p[k] == ' '; k--) + ; + for (j = 0; j <= k; j++) + putc(p[j], f); + putc('\n', f); + } + break; + + case DUMP_TERMCAP: + DumpTermcap(fore->w_aflag, f); + break; + +#ifdef COPY_PASTE + case DUMP_EXCHANGE: + p = user->u_plop.buf; + for (i = user->u_plop.len; i-- > 0; p++) + if (*p == '\r' && (i == 0 || p[1] != '\n')) + putc('\n', f); + else + putc(*p, f); + break; +#endif + } + (void)fclose(f); + UserReturn(1); + } + } + if (UserStatus() <= 0) + Msg(0, "Cannot open \"%s\"", fn); + else if (display && !*rc_name) { + switch (dump) { + case DUMP_TERMCAP: + Msg(0, "Termcap entry written to \"%s\".", fn); + break; + case DUMP_HARDCOPY: + case DUMP_SCROLLBACK: + Msg(0, "Screen image %s to \"%s\".", (*mode == 'a') ? "appended" : "written", fn); + break; +#ifdef COPY_PASTE + case DUMP_EXCHANGE: + Msg(0, "Copybuffer written to \"%s\".", fn); +#endif + } + } +} + +#ifdef COPY_PASTE + +/* + * returns an allocated buffer which holds a copy of the file named fn. + * lenp (if nonzero) points to a location, where the buffer size should be + * stored. + */ +char * +ReadFile(char *fn, int *lenp) +{ + int i, l, size; + char c, *bp, *buf; + struct stat stb; + + ASSERT(lenp); + debug1("ReadFile(%s)\n", fn); + + if ((i = secopen(fn, O_RDONLY, 0)) < 0) { + Msg(errno, "no %s -- no slurp", fn); + return NULL; + } + + if (fstat(i, &stb)) { + Msg(errno, "no good %s -- no slurp", fn); + close(i); + return NULL; + } + size = stb.st_size; + + if ((buf = malloc(size)) == NULL) { + close(i); + Msg(0, "%s", strnomem); + return NULL; + } + errno = 0; + + if ((l = read(i, buf, size)) != size) { + if (l < 0) + l = 0; + Msg(errno, "Got only %d bytes from %s", l, fn); + } else { + if (read(i, &c, 1) > 0) + Msg(0, "Slurped only %d characters (of %d) into buffer - try again", l, size); + else + Msg(0, "Slurped %d characters into buffer", l); + } + close(i); + *lenp = l; + for (bp = buf; l-- > 0; bp++) + if (*bp == '\n' && (bp == buf || bp[-1] != '\r')) + *bp = '\r'; + return buf; +} + +void +KillBuffers() +{ + if (UserContext() > 0) + UserReturn(unlink(BufferFile) ? errno : 0); + errno = UserStatus(); + Msg(errno, "%s %sremoved", BufferFile, errno ? "not " : ""); +} +#endif /* COPY_PASTE */ + + +/* (Almost) secure open and fopen... */ + +FILE * +secfopen(char *name, char *mode) +{ + FILE *fi; +#ifndef USE_SETEUID + int flags, fd; +#endif + + debug2("secfopen(%s, %s)\n", name, mode); +#ifdef USE_SETEUID + xseteuid(real_uid); + xsetegid(real_gid); + fi = fopen(name, mode); + xseteuid(eff_uid); + xsetegid(eff_gid); + return fi; +#else + if (eff_uid == real_uid) + return fopen(name, mode); + if (mode[0] && mode[1] == '+') + flags = O_RDWR; + else + flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY; + if (mode[0] == 'w') + flags |= O_CREAT | O_TRUNC; + else if (mode[0] == 'a') + flags |= O_CREAT | O_APPEND; + else if (mode[0] != 'r') { + errno = EINVAL; + return 0; + } + if ((fd = secopen(name, flags, 0666)) < 0) + return 0; + if ((fi = fdopen(fd, mode)) == 0) { + close(fd); + return 0; + } + return fi; +#endif +} + +int +secopen(char *name, int flags, int mode) +{ + int fd; +#ifndef USE_SETEUID + int q; + struct stat stb; +#endif + + debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode); +#ifdef USE_SETEUID + xseteuid(real_uid); + xsetegid(real_gid); + fd = open(name, flags, mode); + xseteuid(eff_uid); + xsetegid(eff_gid); + return fd; +#else + if (eff_uid == real_uid) + return open(name, flags, mode); + + /* Truncation/creation is done in UserContext */ + if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK))) { + if (UserContext() > 0) { + if ((fd = open(name, flags, mode)) >= 0) { + close(fd); + UserReturn(0); + } + if (errno == 0) + errno = EACCES; + UserReturn(errno); + } + if ((q = UserStatus())) { + if (q > 0) + errno = q; + return -1; + } + } + if (access(name, F_OK)) + return -1; + if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0) + return -1; + debug("open successful\n"); + if (fstat(fd, &stb)) { + close(fd); + return -1; + } + debug("fstat successful\n"); + if (stb.st_uid != real_uid) { + switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) { + case O_RDONLY: + q = 0004; + break; + case O_WRONLY: + q = 0002; + break; + default: + q = 0006; + break; + } + if ((stb.st_mode & q) != q) { + debug1("secopen: permission denied (%03o)\n", stb.st_mode & 07777); + close(fd); + errno = EACCES; + return -1; + } + } + debug1("secopen ok - returning %d\n", fd); + return fd; +#endif +} + +int +printpipe(struct win *p, char *cmd) +{ + int pi[2]; + if (pipe(pi)) { + WMsg(p, errno, "printing pipe"); + return -1; + } + switch (fork()) { + case -1: + WMsg(p, errno, "printing fork"); + return -1; + case 0: + display = p->w_pdisplay; + displays = 0; + ServerSocket = -1; +#ifdef DEBUG + if (dfp && dfp != stderr) + fclose(dfp); +#endif + close(0); + dup(pi[0]); + closeallfiles(0); + if (setgid(real_gid) || setuid(real_uid)) + Panic(errno, "printpipe setuid"); + eff_uid = real_uid; + eff_gid = real_gid; + +#ifdef SIGPIPE + signal(SIGPIPE, SIG_DFL); +#endif + execl("/bin/sh", "sh", "-c", cmd, (char *)0); + Panic(errno, "/bin/sh"); + default: + break; + } + close(pi[0]); + return pi[1]; +} + +int +readpipe(char **cmdv) +{ + int pi[2]; + + if (pipe(pi)) { + Msg(errno, "pipe"); + return -1; + } + + switch (fork()) { + case -1: + Msg(errno, "fork"); + return -1; + case 0: + displays = 0; + ServerSocket = -1; +#ifdef DEBUG + if (dfp && dfp != stderr) + fclose(dfp); +#endif + close(1); + if (dup(pi[1]) != 1) { + close(pi[1]); + Panic(0, "dup"); + } + closeallfiles(1); + + if (setgid(real_gid) || setuid(real_uid)) { + close(1); + Panic(errno, "setuid/setgid"); + } + eff_uid = real_uid; + eff_gid = real_gid; +#ifdef SIGPIPE + signal(SIGPIPE, SIG_DFL); +#endif + execvp(*cmdv, cmdv); + close(1); + Panic(errno, "%s", *cmdv); + default: + break; + } + close(pi[1]); + return pi[0]; +} |