xref: /illumos-gate/usr/src/cmd/svr4pkg/pkginstall/reqexec.c (revision f6e214c7418f43af38bd8c3a557e3d0a1d311cfa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sys/wait.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <fcntl.h>	/* creat() declaration */
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <pwd.h>
40 #include <grp.h>
41 #include <locale.h>
42 #include <libintl.h>
43 #include <pkglib.h>
44 #include "install.h"
45 #include "libadm.h"
46 #include "libinst.h"
47 #include "pkginstall.h"
48 #include "messages.h"
49 
50 extern char	tmpdir[], instdir[];
51 extern int	pkgverbose;
52 
53 static int	do_exec(int update, char *script, char *output,
54 			char *inport, char *alt_user);
55 static char	path[PATH_MAX];
56 static char	*resppath = NULL;
57 static int	fd;
58 static int	respfile_defined = 0;
59 static int	respfile_ro = 0;	/* read only resp file */
60 
61 /*
62  * This informs the calling routine if a read-only response file has been
63  * provided on the command line.
64  */
65 int
66 rdonly_respfile(void)
67 {
68 	return (respfile_ro);
69 }
70 
71 int
72 is_a_respfile(void)
73 {
74 	return (respfile_defined);
75 }
76 
77 /*
78  * This function creates a working copy of the checkinstall script.
79  * This is needed in situations where the packages parent directories modes
80  * are set too restrictively, i.e. 700.
81  *
82  * Returns: A pointer to the location of the copied checkinstall
83  * script or NULL
84  */
85 
86 char *
87 dup_chkinstall(char *script)
88 {
89 	char	*dstpath;
90 	size_t	dstpathLen;
91 	int	r;
92 static	char	*tmpname = "checkinstallXXXXXX";
93 
94 	/* determine length for destination script path */
95 
96 	dstpathLen = strlen(tmpdir) + strlen(tmpname) + 3;
97 
98 	/* allocate storage to hold destination script path */
99 
100 	dstpath = (char *)malloc(dstpathLen);
101 	if (dstpath == (char *)NULL) {
102 		return ((char *)NULL);
103 	}
104 
105 	/* create destination script path */
106 
107 	(void) snprintf(dstpath, dstpathLen, "%s/%s", tmpdir, tmpname);
108 
109 	if (mktemp(dstpath) == NULL) {
110 		progerr(ERR_TMPFILE_CHK);
111 		(void) free(dstpath);
112 		return (NULL);
113 	}
114 
115 	/* make copy of script */
116 
117 	r = copyf(script, dstpath, (time_t)0);
118 	if (r != 0) {
119 		progerr(ERR_CANNOT_COPY, script, dstpath);
120 		return (NULL);
121 	}
122 
123 	/* Make the copy of the script readable by all */
124 
125 	if (chmod(dstpath, 0444) != 0) {
126 		progerr(ERR_CHMOD_CHK);
127 		(void) free(dstpath);
128 		return (NULL);
129 	}
130 
131 	return (dstpath);
132 }
133 
134 /*
135  * This function creates a temporary working copy of a read-only response
136  * file. It changes the resppath pointer to point to the working copy.
137  */
138 static int
139 dup_respfile(void)
140 {
141 	char	tpath[PATH_MAX];
142 	int	r;
143 
144 	(void) strlcpy(tpath, path, sizeof (tpath));
145 
146 	(void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir);
147 
148 	resppath = mktemp(path);
149 	if (resppath == NULL) {
150 		progerr(ERR_TMPRESP);
151 		return (99);
152 	}
153 
154 	/* Copy the contents of the user's response file to the working copy. */
155 
156 	r = copyf(tpath, resppath, (time_t)0);
157 	if (r != 0) {
158 		progerr(ERR_NORESPCOPY, tpath, resppath);
159 		return (99);
160 	}
161 
162 	/*
163 	 * Make it writable by the non-privileged installation user-id,
164 	 * but readable by the world.
165 	 */
166 
167 	if (chmod(resppath, 0644) != 0) {
168 		progerr(ERR_CHMOD, resppath);
169 		return (99);
170 	}
171 
172 	respfile_ro = 0;
173 
174 	return (0);
175 }
176 
177 /*
178  * This function establishes the response file passed on the command line if
179  * it's called with a valid string. If called with NULL, it checks to see if
180  * there's a response file already. If there isn't, it creates a temporary.
181  */
182 int
183 set_respfile(char *respfile, char *pkginst, int resp_stat)
184 {
185 	if (respfile == NULL && !respfile_defined) {
186 		/* A temporary response file needs to be constructed. */
187 		(void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir);
188 		resppath = mktemp(path);
189 		if (resppath == NULL) {
190 			progerr(ERR_TMPRESP);
191 			return (99);
192 		}
193 	} else {
194 		/* OK, we're being passed a response file or directory. */
195 		if (isdir(respfile) == 0) {
196 			(void) snprintf(path, sizeof (path),
197 				"%s/%s", respfile, pkginst);
198 		} else {
199 			(void) strlcpy(path, respfile, sizeof (path));
200 		}
201 
202 		resppath = path;
203 		respfile_ro = resp_stat;
204 	}
205 
206 	respfile_defined++;
207 
208 	return (0);
209 }
210 
211 /* This exposes the working response file. */
212 char *
213 get_respfile(void)
214 {
215 	return (resppath);
216 }
217 
218 /*
219  * Execute the request script if present assuming the response file
220  * isn't read only.
221  */
222 int
223 reqexec(int update, char *script, int non_abi_scripts,
224 	boolean_t enable_root_user)
225 {
226 	char	*req_user;
227 
228 	/*
229 	 * determine which alternative user to execute the request script as
230 	 * if the default user "install" is not defined.
231 	 */
232 
233 	if (enable_root_user == B_TRUE) {
234 		/* use the root user */
235 		req_user = CHK_USER_ROOT;
236 	} else if (non_abi_scripts != 0) {
237 		/* non-compliant package user */
238 		req_user = CHK_USER_NON;
239 	} else {
240 		/* standard non-privileged user */
241 		req_user = CHK_USER_ALT;
242 	}
243 
244 	/*
245 	 * If we can't get to the the script or the response file, skip this.
246 	 */
247 	if (access(script, F_OK) != 0 || respfile_ro)
248 		return (0);
249 
250 	/* No interact means no interact. */
251 	if (echoGetFlag() == B_FALSE) {
252 		ptext(stderr, ERR_INTR);
253 		return (5);
254 	}
255 
256 	/* If there's no response file, create one. */
257 	if (!respfile_defined)
258 		if (set_respfile(NULL, NULL, 0))
259 			return (99);
260 
261 	/* Clear out the old response file (if there is one). */
262 	if ((access(resppath, F_OK) == 0) && unlink(resppath)) {
263 		progerr(ERR_RMRESP, resppath);
264 		return (99);
265 	}
266 
267 	/*
268 	 * Create a zero length response file which is only writable
269 	 * by the non-privileged installation user-id, but is readable
270 	 * by the world
271 	 */
272 	if ((fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) < 0) {
273 		progerr(ERR_CRERESP, resppath);
274 		return (99);
275 	}
276 	(void) close(fd);
277 
278 	return (do_exec(update, script, resppath, REQ_STDIN, req_user));
279 }
280 
281 int
282 chkexec(int update, char *script)
283 {
284 	/*
285 	 * If we're up against a read-only response file from the command
286 	 * line. Create a working copy.
287 	 */
288 	if (respfile_ro) {
289 		if (dup_respfile())
290 
291 			return (99);
292 
293 		/* Make sure we can get to it. */
294 		if ((access(resppath, F_OK) != 0)) {
295 			progerr(ERR_ACCRESP, resppath);
296 			return (7);
297 		}
298 	}
299 
300 	/* If there's no response file, create a fresh one. */
301 	else if (!respfile_defined) {
302 		if (set_respfile(NULL, NULL, 0))
303 			return (99);
304 
305 		/*
306 		 * create a zero length response file which is only writable
307 		 * by the non-priveledged installation user-id, but is readable
308 		 * by the world
309 		 */
310 		fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
311 		if (fd < 0) {
312 			progerr(ERR_CRERESP, resppath);
313 			return (99);
314 		}
315 		(void) close(fd);
316 	}
317 
318 	return (do_exec(update, script, resppath, CHK_STDIN, CHK_USER_ALT));
319 }
320 
321 static int
322 do_exec(int update, char *script, char *output, char *inport, char *alt_user)
323 {
324 	char		*gname;
325 	char		*tmp_script;
326 	char		*uname;
327 	gid_t		instgid;
328 	int		retcode = 0;
329 	struct group	*grp;
330 	struct passwd	*pwp;
331 	uid_t		instuid;
332 
333 	/*
334 	 * Determine which user to run the request script as:
335 	 * - if CHK_USER is a valid user, run the script as CHK_USER
336 	 * - otherwise, if alt_user is a valid user, run the script
337 	 * -- as alt_user
338 	 * - otherwise, output an error message and return failure
339 	 */
340 
341 	if ((pwp = getpwnam(CHK_USER)) != (struct passwd *)NULL) {
342 		instuid = pwp->pw_uid;
343 		uname = CHK_USER;
344 	} else if ((pwp = getpwnam(alt_user)) != (struct passwd *)NULL) {
345 		instuid = pwp->pw_uid;
346 		uname = alt_user;
347 	} else {
348 		ptext(stderr, ERR_BADUSER, CHK_USER, CHK_USER_ALT);
349 		return (1);
350 	}
351 
352 	/*
353 	 * Determine which group to run the request script as:
354 	 * - If CHK_GRP is a valid group, run the script as CHK_GRP
355 	 * - otherwise, assume group "1" user "other"
356 	 */
357 
358 	if ((grp = getgrnam(CHK_GRP)) != (struct group *)NULL) {
359 		instgid = grp->gr_gid;
360 		gname = CHK_GRP;
361 	} else {
362 		instgid = (gid_t)1;	/* "other" group id */
363 		gname = "other";	/* "other" group name */
364 	}
365 
366 	echoDebug(DBG_DO_EXEC_REQUEST_USER, script, output, uname, instuid,
367 		gname, instgid);
368 
369 	(void) chown(output, instuid, instgid);
370 
371 	/*
372 	 * Copy the checkinstall script to tmpdir in case parent directories
373 	 * are restrictive, i.e. 700. Only do this for non updates, i.e.
374 	 * package installs and not patch package installs.
375 	 */
376 	if (update) {
377 		tmp_script = strdup(script);
378 	} else if ((tmp_script = dup_chkinstall(script)) == NULL) {
379 		/* Use the original checkinstall script */
380 		tmp_script = strdup(script);
381 	}
382 
383 	if (pkgverbose)
384 		retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL,
385 		    "-x", tmp_script, output, NULL);
386 	else
387 		retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL,
388 		    tmp_script, output, NULL);
389 
390 	free(tmp_script);
391 	return (retcode);
392 }
393