예제 #1
0
/*
 * 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);
}
예제 #2
0
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*/
}