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
rdonly_respfile(void)66 rdonly_respfile(void)
67 {
68 return (respfile_ro);
69 }
70
71 int
is_a_respfile(void)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 *
dup_chkinstall(char * script)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
dup_respfile(void)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
set_respfile(char * respfile,char * pkginst,int resp_stat)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 *
get_respfile(void)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
reqexec(int update,char * script,int non_abi_scripts,boolean_t enable_root_user)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
chkexec(int update,char * script)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
do_exec(int update,char * script,char * output,char * inport,char * alt_user)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