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