/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include /* creat() declaration */ #include #include #include #include #include #include #include #include "install.h" #include "libadm.h" #include "libinst.h" #include "pkginstall.h" #include "messages.h" extern char tmpdir[], instdir[]; extern int pkgverbose; static int do_exec(int update, char *script, char *output, char *inport, char *alt_user); static char path[PATH_MAX]; static char *resppath = NULL; static int fd; static int respfile_defined = 0; static int respfile_ro = 0; /* read only resp file */ /* * This informs the calling routine if a read-only response file has been * provided on the command line. */ int rdonly_respfile(void) { return (respfile_ro); } int is_a_respfile(void) { return (respfile_defined); } /* * This function creates a working copy of the checkinstall script. * This is needed in situations where the packages parent directories modes * are set too restrictively, i.e. 700. * * Returns: A pointer to the location of the copied checkinstall * script or NULL */ char * dup_chkinstall(char *script) { char *dstpath; size_t dstpathLen; int r; static char *tmpname = "checkinstallXXXXXX"; /* determine length for destination script path */ dstpathLen = strlen(tmpdir) + strlen(tmpname) + 3; /* allocate storage to hold destination script path */ dstpath = (char *)malloc(dstpathLen); if (dstpath == (char *)NULL) { return ((char *)NULL); } /* create destination script path */ (void) snprintf(dstpath, dstpathLen, "%s/%s", tmpdir, tmpname); if (mktemp(dstpath) == NULL) { progerr(ERR_TMPFILE_CHK); (void) free(dstpath); return (NULL); } /* make copy of script */ r = copyf(script, dstpath, (time_t)0); if (r != 0) { progerr(ERR_CANNOT_COPY, script, dstpath); return (NULL); } /* Make the copy of the script readable by all */ if (chmod(dstpath, 0444) != 0) { progerr(ERR_CHMOD_CHK); (void) free(dstpath); return (NULL); } return (dstpath); } /* * This function creates a temporary working copy of a read-only response * file. It changes the resppath pointer to point to the working copy. */ static int dup_respfile(void) { char tpath[PATH_MAX]; int r; (void) strlcpy(tpath, path, sizeof (tpath)); (void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir); resppath = mktemp(path); if (resppath == NULL) { progerr(ERR_TMPRESP); return (99); } /* Copy the contents of the user's response file to the working copy. */ r = copyf(tpath, resppath, (time_t)0); if (r != 0) { progerr(ERR_NORESPCOPY, tpath, resppath); return (99); } /* * Make it writable by the non-privileged installation user-id, * but readable by the world. */ if (chmod(resppath, 0644) != 0) { progerr(ERR_CHMOD, resppath); return (99); } respfile_ro = 0; return (0); } /* * This function establishes the response file passed on the command line if * it's called with a valid string. If called with NULL, it checks to see if * there's a response file already. If there isn't, it creates a temporary. */ int set_respfile(char *respfile, char *pkginst, int resp_stat) { if (respfile == NULL && !respfile_defined) { /* A temporary response file needs to be constructed. */ (void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir); resppath = mktemp(path); if (resppath == NULL) { progerr(ERR_TMPRESP); return (99); } } else { /* OK, we're being passed a response file or directory. */ if (isdir(respfile) == 0) { (void) snprintf(path, sizeof (path), "%s/%s", respfile, pkginst); } else { (void) strlcpy(path, respfile, sizeof (path)); } resppath = path; respfile_ro = resp_stat; } respfile_defined++; return (0); } /* This exposes the working response file. */ char * get_respfile(void) { return (resppath); } /* * Execute the request script if present assuming the response file * isn't read only. */ int reqexec(int update, char *script, int non_abi_scripts, boolean_t enable_root_user) { char *req_user; /* * determine which alternative user to execute the request script as * if the default user "install" is not defined. */ if (enable_root_user == B_TRUE) { /* use the root user */ req_user = CHK_USER_ROOT; } else if (non_abi_scripts != 0) { /* non-compliant package user */ req_user = CHK_USER_NON; } else { /* standard non-privileged user */ req_user = CHK_USER_ALT; } /* * If we can't get to the the script or the response file, skip this. */ if (access(script, F_OK) != 0 || respfile_ro) return (0); /* No interact means no interact. */ if (echoGetFlag() == B_FALSE) { ptext(stderr, ERR_INTR); return (5); } /* If there's no response file, create one. */ if (!respfile_defined) if (set_respfile(NULL, NULL, 0)) return (99); /* Clear out the old response file (if there is one). */ if ((access(resppath, F_OK) == 0) && unlink(resppath)) { progerr(ERR_RMRESP, resppath); return (99); } /* * Create a zero length response file which is only writable * by the non-privileged installation user-id, but is readable * by the world */ if ((fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) < 0) { progerr(ERR_CRERESP, resppath); return (99); } (void) close(fd); return (do_exec(update, script, resppath, REQ_STDIN, req_user)); } int chkexec(int update, char *script) { /* * If we're up against a read-only response file from the command * line. Create a working copy. */ if (respfile_ro) { if (dup_respfile()) return (99); /* Make sure we can get to it. */ if ((access(resppath, F_OK) != 0)) { progerr(ERR_ACCRESP, resppath); return (7); } } /* If there's no response file, create a fresh one. */ else if (!respfile_defined) { if (set_respfile(NULL, NULL, 0)) return (99); /* * create a zero length response file which is only writable * by the non-priveledged installation user-id, but is readable * by the world */ fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); if (fd < 0) { progerr(ERR_CRERESP, resppath); return (99); } (void) close(fd); } return (do_exec(update, script, resppath, CHK_STDIN, CHK_USER_ALT)); } static int do_exec(int update, char *script, char *output, char *inport, char *alt_user) { char *gname; char *tmp_script; char *uname; gid_t instgid; int retcode = 0; struct group *grp; struct passwd *pwp; uid_t instuid; /* * Determine which user to run the request script as: * - if CHK_USER is a valid user, run the script as CHK_USER * - otherwise, if alt_user is a valid user, run the script * -- as alt_user * - otherwise, output an error message and return failure */ if ((pwp = getpwnam(CHK_USER)) != (struct passwd *)NULL) { instuid = pwp->pw_uid; uname = CHK_USER; } else if ((pwp = getpwnam(alt_user)) != (struct passwd *)NULL) { instuid = pwp->pw_uid; uname = alt_user; } else { ptext(stderr, ERR_BADUSER, CHK_USER, CHK_USER_ALT); return (1); } /* * Determine which group to run the request script as: * - If CHK_GRP is a valid group, run the script as CHK_GRP * - otherwise, assume group "1" user "other" */ if ((grp = getgrnam(CHK_GRP)) != (struct group *)NULL) { instgid = grp->gr_gid; gname = CHK_GRP; } else { instgid = (gid_t)1; /* "other" group id */ gname = "other"; /* "other" group name */ } echoDebug(DBG_DO_EXEC_REQUEST_USER, script, output, uname, instuid, gname, instgid); (void) chown(output, instuid, instgid); /* * Copy the checkinstall script to tmpdir in case parent directories * are restrictive, i.e. 700. Only do this for non updates, i.e. * package installs and not patch package installs. */ if (update) { tmp_script = strdup(script); } else if ((tmp_script = dup_chkinstall(script)) == NULL) { /* Use the original checkinstall script */ tmp_script = strdup(script); } if (pkgverbose) retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL, "-x", tmp_script, output, NULL); else retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL, tmp_script, output, NULL); free(tmp_script); return (retcode); }