/* * Name: averify * Description: This function verifies and (if fix > 0) fixes the attributes * of the file at the path provided. * Arguments: fix - 0 - do not fix entries, 1 - fix entries * ftype - single character "type" the entry is supposed to be * path - path to file * ainfo - attribute info structure representing the attributes * the entry is supposed to be * NOTE: attributes are links and permissions * Possible return values: * - 0 = successful * - VE_EXIST = path name does not exist * - VE_FTYPE = path file type is not recognized, is not supported, * or is not what was expected * - VE_ATTR = path mode/group/user is not what was expected * - VE_CONT = mod time/link target/major/minor/size/file system type/current * directory is not what was expected * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/ * chown failed */ int averify(int fix, char *ftype, char *path, struct ainfo *ainfo) { struct group *grp; /* group entry buffer */ struct passwd *pwd; int n; int setval; int uid, gid; int dochown; int retcode; int statError = 0; int targ_is_dir = 0; /* replacing a directory */ char myftype; char buf[PATH_MAX]; ino_t my_ino; dev_t my_dev; char cwd[MAXPATHLEN]; char *cd; char *c; setval = (*ftype == '?'); retcode = 0; reperr(NULL); if (get_disable_attribute_check()) { return (0); } if (*ftype == 'l') { if (stat(path, &status) < 0) { retcode = VE_EXIST; reperr(pkg_gt(ERR_EXIST)); } my_ino = status.st_ino; my_dev = status.st_dev; /* Get copy of the current working directory */ if (getcwd(cwd, MAXPATHLEN) == NULL) { reperr(pkg_gt(ERR_GETWD)); return (VE_FAIL); } /* * Change to the directory in which the hard * link is to be created. */ cd = strdup(path); c = strrchr(cd, '/'); if (c) { /* bugid 4247895 */ if (strcmp(cd, c) == 0) (void) strcpy(cd, "/"); else *c = NULL; if (chdir(cd) != 0) { reperr(pkg_gt(ERR_CHDIR), cd); return (VE_FAIL); } } free(cd); if (retcode || (status.st_nlink < 2) || (stat(ainfo->local, &status) < 0) || (my_dev != status.st_dev) || (my_ino != status.st_ino)) { if (fix) { /* * Don't want to do a hard link to a * directory. */ if (!isdir(ainfo->local)) { (void) chdir(cwd); reperr(pkg_gt(ERR_LINKISDIR), ainfo->local); return (VE_FAIL); } /* Now do the link. */ if (!clear_target(path, ftype, targ_is_dir)) return (VE_FAIL); if (link(ainfo->local, path)) { (void) chdir(cwd); reperr(pkg_gt(ERR_LINKFAIL), ainfo->local); return (VE_FAIL); } retcode = 0; } else { /* Go back to previous working directory */ if (chdir(cwd) != 0) reperr(pkg_gt(ERR_CHDIR), cwd); reperr(pkg_gt(ERR_LINK), ainfo->local); return (VE_CONT); } } /* Go back to previous working directory */ if (chdir(cwd) != 0) { reperr(pkg_gt(ERR_CHDIR), cwd); return (VE_CONT); } return (retcode); } retcode = 0; /* If we are to process symlinks the old way then we follow the link */ if (nonABI_symlinks()) { if ((*ftype == 's') ? lstat(path, &status) : stat(path, &status)) { reperr(pkg_gt(ERR_EXIST)); retcode = VE_EXIST; myftype = '?'; statError++; } /* If not then we inspect the target of the link */ } else { if ((n = lstat(path, &status)) == -1) { reperr(pkg_gt(ERR_EXIST)); retcode = VE_EXIST; myftype = '?'; statError++; } } if (!statError) { /* determining actual type of existing object */ switch (status.st_mode & S_IFMT) { case S_IFLNK: myftype = 's'; break; case S_IFIFO: myftype = 'p'; break; case S_IFCHR: myftype = 'c'; break; case S_IFDIR: myftype = 'd'; targ_is_dir = 1; break; case S_IFBLK: myftype = 'b'; break; case S_IFREG: case 0: myftype = 'f'; break; case S_IFDOOR: myftype = 'D'; break; default: reperr(pkg_gt(ERR_UNKNOWN)); return (VE_FTYPE); } } if (setval) { /* * Check to make sure that a package or an installf that uses * wild cards '?' to assume the ftype of an object on the * system is not assuming a door ftype. Doors are not supported * but should be ignored. */ if (myftype == 'D') { reperr(pkg_gt(ERR_FTYPED), path); retcode = VE_FTYPE; return (VE_FTYPE); } else { *ftype = myftype; } } else if (!retcode && (*ftype != myftype) && ((myftype != 'f') || !strchr("ilev", *ftype)) && ((myftype != 'd') || (*ftype != 'x'))) { reperr(pkg_gt(ERR_FTYPE), *ftype, myftype); retcode = VE_FTYPE; } if (!retcode && (*ftype == 's')) { /* make sure that symbolic link is correct */ n = readlink(path, buf, PATH_MAX); if (n < 0) { reperr(pkg_gt(ERR_SLINK), ainfo->local); retcode = VE_CONT; } else if (ainfo->local != NULL) { buf[n] = '\0'; if (strcmp(buf, ainfo->local)) { reperr(pkg_gt(ERR_SLINK), ainfo->local); retcode = VE_CONT; } } else if (ainfo->local == NULL) { /* * Since a sym link target exists, insert it * into the ainfo structure */ buf[n] = '\0'; ainfo->local = strdup(buf); } } if (retcode) { /* The path doesn't exist or is different than it should be. */ if (fix) { /* * Clear the way for the write. If it won't clear, * there's nothing we can do. */ if (!clear_target(path, ftype, targ_is_dir)) return (VE_FAIL); if ((*ftype == 'd') || (*ftype == 'x')) { char *pt, *p; /* Try to make it the easy way */ if (mkdir(path, ainfo->mode)) { /* * Failing that, walk through the * parent directories creating * whatever is needed. */ p = strdup(path); pt = (*p == '/') ? p+1 : p; do { if (pt = strchr(pt, '/')) *pt = '\0'; if (access(p, 0) && mkdir(p, ainfo->mode)) break; if (pt) *pt++ = '/'; } while (pt); free(p); } if (stat(path, &status) < 0) { reperr(pkg_gt(ERR_DIRFAIL)); return (VE_FAIL); } } else if (*ftype == 's') { if (symlink(ainfo->local, path)) { reperr(pkg_gt(ERR_SLINKFAIL), ainfo->local); return (VE_FAIL); } } else if (*ftype == 'c') { int wilddevno = 0; /* * The next three if's support 2.4 and older * packages that use "?" as device numbers. * This should be considered for removal by * release 2.7 or so. */ if (ainfo->major == BADMAJOR) { ainfo->major = 0; wilddevno = 1; } if (ainfo->minor == BADMINOR) { ainfo->minor = 0; wilddevno = 1; } if (wilddevno) { wilddevno = 0; logerr(MSG_WLDDEVNO, path, ainfo->major, ainfo->minor); } if (mknod(path, ainfo->mode | S_IFCHR, makedev(ainfo->major, ainfo->minor)) || (stat(path, &status) < 0)) { reperr(pkg_gt(ERR_CDEVFAIL)); return (VE_FAIL); } } else if (*ftype == 'b') { int wilddevno = 0; /* * The next three if's support 2.4 and older * packages that use "?" as device numbers. * This should be considered for removal by * release 2.7 or so. */ if (ainfo->major == BADMAJOR) { ainfo->major = 0; wilddevno = 1; } if (ainfo->minor == BADMINOR) { ainfo->minor = 0; wilddevno = 1; } if (wilddevno) { wilddevno = 0; logerr(MSG_WLDDEVNO, path, ainfo->major, ainfo->minor); } if (mknod(path, ainfo->mode | S_IFBLK, makedev(ainfo->major, ainfo->minor)) || (stat(path, &status) < 0)) { reperr(pkg_gt(ERR_BDEVFAIL)); return (VE_FAIL); } } else if (*ftype == 'p') { if (mknod(path, ainfo->mode | S_IFIFO, NULL) || (stat(path, &status) < 0)) { reperr(pkg_gt(ERR_PIPEFAIL)); return (VE_FAIL); } } else return (retcode); } else return (retcode); } if (*ftype == 's') return (0); /* don't check anything else */ if (*ftype == 'i') return (0); /* don't check anything else */ retcode = 0; if ((myftype == 'c') || (myftype == 'b')) { if (setval || (ainfo->major == BADMAJOR)) ainfo->major = major(status.st_rdev); if (setval || (ainfo->minor == BADMINOR)) ainfo->minor = minor(status.st_rdev); /* check major & minor */ if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) { reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor, major(status.st_rdev), minor(status.st_rdev)); retcode = VE_CONT; } } /* compare specified mode w/ actual mode excluding sticky bit */ if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD)) ainfo->mode = status.st_mode & 07777; else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) { if (fix) { if ((ainfo->mode == BADMODE) || (chmod(path, ainfo->mode) < 0)) retcode = VE_FAIL; } else { reperr(pkg_gt(ERR_PERM), ainfo->mode, status.st_mode & 07777); if (!retcode) retcode = VE_ATTR; } } dochown = 0; /* get group entry for specified group */ if (setval || strcmp(ainfo->group, BADGROUP) == 0) { grp = cgrgid(status.st_gid); if (grp) (void) strcpy(ainfo->group, grp->gr_name); else { if (!retcode) retcode = VE_ATTR; reperr(pkg_gt(ERR_BADGRPID), status.st_gid); } gid = status.st_gid; } else if ((grp = cgrnam(ainfo->group)) == NULL) { reperr(pkg_gt(ERR_BADGRPNM), ainfo->group); if (!retcode) retcode = VE_ATTR; } else if ((gid = grp->gr_gid) != status.st_gid) { if (fix) { /* save specified GID */ gid = grp->gr_gid; dochown++; } else { if ((grp = cgrgid((int)status.st_gid)) == (struct group *)NULL) { reperr(pkg_gt(ERR_GROUP), ainfo->group, "(null)"); } else { reperr(pkg_gt(ERR_GROUP), ainfo->group, grp->gr_name); } if (!retcode) retcode = VE_ATTR; } } /* get password entry for specified owner */ if (setval || strcmp(ainfo->owner, BADOWNER) == 0) { pwd = cpwuid((int)status.st_uid); if (pwd) (void) strcpy(ainfo->owner, pwd->pw_name); else { if (!retcode) retcode = VE_ATTR; reperr(pkg_gt(ERR_BADUSRID), status.st_uid); } uid = status.st_uid; } else if ((pwd = cpwnam(ainfo->owner)) == NULL) { /* UID does not exist in password file */ reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner); if (!retcode) retcode = VE_ATTR; } else if ((uid = pwd->pw_uid) != status.st_uid) { /* get owner name for actual UID */ if (fix) { uid = pwd->pw_uid; dochown++; } else { pwd = cpwuid((int)status.st_uid); if (pwd == NULL) reperr(pkg_gt(ERR_BADUSRID), (int)status.st_uid); else reperr(pkg_gt(ERR_OWNER), ainfo->owner, pwd->pw_name); if (!retcode) retcode = VE_ATTR; } } if (statvfs(path, &vfsstatus) < 0) { reperr(pkg_gt(ERR_EXIST)); retcode = VE_FAIL; } else { if (dochown) { /* pcfs doesn't support file ownership */ if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 && chown(path, uid, gid) < 0) { retcode = VE_FAIL; /* chown failed */ } } } if (retcode == VE_FAIL) reperr(pkg_gt(ERR_ATTRFAIL)); return (retcode); }
int pkgexecv(char *filein, char *fileout, char *uname, char *gname, char *arg[]) { int exit_no; int n; int status; pid_t pid; pid_t waitstat; struct group *grp; struct passwd *pwp; struct sigaction nact; struct sigaction oact; void (*funcSighup)(int); void (*funcSigint)(int); /* flush standard i/o before creating new process */ (void) fflush(stdout); (void) fflush(stderr); /* * hold SIGINT/SIGHUP signals and reset signal received counter; * after the vfork() the parent and child need to setup their respective * interrupt handling and release the hold on the signals */ (void) sighold(SIGINT); (void) sighold(SIGHUP); sig_received = 0; /* * create new process to execute command in; * vfork() is being used to avoid duplicating the parents * memory space - this means that the child process may * not modify any of the parents memory including the * standard i/o descriptors - all the child can do is * adjust interrupts and open files as a prelude to a * call to exec(). */ pid = vfork(); if (pid < 0) { /* * ************************************************************* * fork failed! * ************************************************************* */ progerr(pkg_gt(ERR_FORK_FAILED), errno, strerror(errno)); /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); return (-1); } if (pid > 0) { /* * ************************************************************* * This is the forking (parent) process * ************************************************************* */ /* close datastream if any portion read */ if (ds_curpartcnt >= 0) { if (ds_close(0) != 0) { /* kill child process */ (void) sigsend(P_PID, pid, SIGKILL); /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); return (-1); } } /* * setup signal handlers for SIGINT and SIGHUP and release hold */ /* hook SIGINT to sig_trap() */ nact.sa_handler = sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGINT, &nact, &oact) < 0) { funcSigint = SIG_DFL; } else { funcSigint = oact.sa_handler; } /* hook SIGHUP to sig_trap() */ nact.sa_handler = sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGHUP, &nact, &oact) < 0) { funcSighup = SIG_DFL; } else { funcSighup = oact.sa_handler; } /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); /* * wait for the process to exit, reap child exit status */ for (;;) { status = 0; waitstat = waitpid(pid, (int *)&status, 0); if (waitstat < 0) { /* waitpid returned error */ if (errno == EAGAIN) { /* try again */ continue; } if (errno == EINTR) { continue; } /* error from waitpid: bail */ break; } else if (waitstat == pid) { /* child exit status available */ break; } } /* * reset signal handlers */ /* reset SIGINT */ nact.sa_handler = funcSigint; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL); /* reset SIGHUP */ nact.sa_handler = funcSighup; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL); /* error if child process does not match */ if (waitstat != pid) { progerr(pkg_gt(ERR_WAIT_FAILED), pid, waitstat, status, errno, strerror(errno)); return (-1); } /* * determine final exit code: * - if signal received, then return interrupted (3) * - if child exit status is available, return exit child status * - otherwise return error (-1) */ if (sig_received != 0) { exit_no = 3; /* interrupted */ } else if (WIFEXITED(status)) { exit_no = WEXITSTATUS(status); } else { exit_no = -1; /* exec() or other process error */ } return (exit_no); } /* * ********************************************************************* * This is the forked (child) process * ********************************************************************* */ /* reset all signals to default */ for (n = 0; n < NSIG; n++) { (void) sigset(n, SIG_DFL); } /* release hold on signals held by parent before fork() */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); /* * The caller wants to have stdin connected to filein. */ if (filein && *filein) { /* * If input is supposed to be connected to /dev/tty */ if (strncmp(filein, "/dev/tty", 8) == 0) { /* * If stdin is connected to a tty device. */ if (isatty(STDIN_FILENO)) { /* * Reopen it to /dev/tty. */ n = open(filein, O_RDONLY); if (n >= 0) { (void) dup2(n, STDIN_FILENO); } } } else { /* * If we did not want to be connected to /dev/tty, we * connect input to the requested file no questions. */ n = open(filein, O_RDONLY); if (n >= 0) { (void) dup2(n, STDIN_FILENO); } } } /* * The caller wants to have stdout and stderr connected to fileout. * If "fileout" is "/dev/tty" then reconnect stdout to "/dev/tty" * only if /dev/tty is not already associated with "a tty". */ if (fileout && *fileout) { /* * If output is supposed to be connected to /dev/tty */ if (strncmp(fileout, "/dev/tty", 8) == 0) { /* * If stdout is connected to a tty device. */ if (isatty(STDOUT_FILENO)) { /* * Reopen it to /dev/tty if /dev/tty available. */ n = open(fileout, O_WRONLY); if (n >= 0) { /* * /dev/tty is available - close the * current standard output stream, and * reopen it on /dev/tty */ (void) dup2(n, STDOUT_FILENO); } } /* * not connected to tty device - probably redirect to * file - preserve existing output device */ } else { /* * If we did not want to be connected to /dev/tty, we * connect output to the requested file no questions. */ /* LINTED O_CREAT without O_EXCL specified in call to */ n = open(fileout, O_WRONLY|O_CREAT|O_APPEND, 0666); if (n >= 0) { (void) dup2(n, STDOUT_FILENO); } } /* * Dup stderr from stdout. */ (void) dup2(STDOUT_FILENO, STDERR_FILENO); } /* * do NOT close all file descriptors except stdio * file descriptors are passed in to some subcommands * (see dstream:ds_getinfo() and dstream:ds_putinfo()) */ /* set group/user i.d. if requested */ if (gname && *gname && (grp = cgrnam(gname)) != NULL) { if (setgid(grp->gr_gid) == -1) { progerr(pkg_gt(ERR_SETGID), grp->gr_gid); } } if (uname && *uname && (pwp = cpwnam(uname)) != NULL) { if (setuid(pwp->pw_uid) == -1) { progerr(pkg_gt(ERR_SETUID), pwp->pw_uid); } } /* execute target executable */ (void) execve(arg[0], arg, environ); progerr(pkg_gt(ERR_EX_FAIL), arg[0], errno); _exit(99); /*NOTREACHED*/ }