1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/coreconf/nsinstall/nsinstall.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,407 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 +** Netscape portable install command. 1.10 +*/ 1.11 +#include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */ 1.12 +#include <assert.h> 1.13 +#include <fcntl.h> 1.14 +#include <string.h> 1.15 +#if defined(_WINDOWS) 1.16 +#include <windows.h> 1.17 +typedef unsigned int mode_t; 1.18 +#else 1.19 +#include <grp.h> 1.20 +#include <pwd.h> 1.21 +#include <errno.h> 1.22 +#include <stdlib.h> 1.23 +#include <unistd.h> 1.24 +#include <utime.h> 1.25 +#endif 1.26 +#include <sys/types.h> 1.27 +#include <sys/stat.h> 1.28 +#include "pathsub.h" 1.29 + 1.30 +#define HAVE_LCHOWN 1.31 + 1.32 +#if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) || defined(NTO) || defined(DARWIN) || defined(BEOS) || defined(__riscos__) 1.33 +#undef HAVE_LCHOWN 1.34 +#endif 1.35 + 1.36 +#define HAVE_FCHMOD 1.37 + 1.38 +#if defined(BEOS) 1.39 +#undef HAVE_FCHMOD 1.40 +#endif 1.41 + 1.42 +#ifdef LINUX 1.43 +#include <getopt.h> 1.44 +#endif 1.45 + 1.46 +#if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC) 1.47 +#if !defined(S_ISLNK) && defined(S_IFLNK) 1.48 +#define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK) 1.49 +#endif 1.50 +#endif 1.51 + 1.52 +#if defined(SNI) 1.53 +extern int fchmod(int fildes, mode_t mode); 1.54 +#endif 1.55 + 1.56 + 1.57 +#ifdef GETCWD_CANT_MALLOC 1.58 +/* 1.59 + * this should probably go into a utility library in case other applications 1.60 + * need it. 1.61 + */ 1.62 +static char * 1.63 +getcwd_do_malloc(char *path, int len) { 1.64 + 1.65 + if (!path) { 1.66 + path = malloc(PATH_MAX +1); 1.67 + if (!path) return NULL; 1.68 + } 1.69 + return getcwd(path, PATH_MAX); 1.70 +} 1.71 +#define GETCWD getcwd_do_malloc 1.72 +#else 1.73 +#define GETCWD getcwd 1.74 +#endif 1.75 + 1.76 + 1.77 +static void 1.78 +usage(void) 1.79 +{ 1.80 + fprintf(stderr, 1.81 + "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n" 1.82 + " %*s [-DdltR] file [file ...] directory\n", 1.83 + program, (int)strlen(program), ""); 1.84 + exit(2); 1.85 +} 1.86 + 1.87 +/* this is more-or-less equivalent to mkdir -p */ 1.88 +static int 1.89 +mkdirs(char *path, mode_t mode) 1.90 +{ 1.91 + char * cp; 1.92 + int rv; 1.93 + struct stat sb; 1.94 + 1.95 + if (!path || !path[0]) 1.96 + fail("Null pointer or empty string passed to mkdirs()"); 1.97 + while (*path == '/' && path[1] == '/') 1.98 + path++; 1.99 + for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--); 1.100 + if (cp && cp != path) { 1.101 + *cp = '\0'; 1.102 + if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) && 1.103 + mkdirs(path, mode) < 0) { 1.104 + return -1; 1.105 + } 1.106 + *cp = '/'; 1.107 + } 1.108 + rv = mkdir(path, mode); 1.109 + if (rv) { 1.110 + if (errno != EEXIST) 1.111 + fail("mkdirs cannot make %s", path); 1.112 + fprintf(stderr, "directory creation race: %s\n", path); 1.113 + if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) 1.114 + rv = 0; 1.115 + } 1.116 + return rv; 1.117 +} 1.118 + 1.119 +static uid_t 1.120 +touid(char *owner) 1.121 +{ 1.122 + struct passwd *pw; 1.123 + uid_t uid; 1.124 + char *cp; 1.125 + 1.126 + if (!owner || !owner[0]) 1.127 + fail("Null pointer or empty string passed to touid()"); 1.128 + pw = getpwnam(owner); 1.129 + if (pw) 1.130 + return pw->pw_uid; 1.131 + uid = strtol(owner, &cp, 0); 1.132 + if (uid == 0 && cp == owner) 1.133 + fail("cannot find uid for %s", owner); 1.134 + return uid; 1.135 +} 1.136 + 1.137 +static gid_t 1.138 +togid(char *group) 1.139 +{ 1.140 + struct group *gr; 1.141 + gid_t gid; 1.142 + char *cp; 1.143 + 1.144 + if (!group || !group[0]) 1.145 + fail("Null pointer or empty string passed to togid()"); 1.146 + gr = getgrnam(group); 1.147 + if (gr) 1.148 + return gr->gr_gid; 1.149 + gid = strtol(group, &cp, 0); 1.150 + if (gid == 0 && cp == group) 1.151 + fail("cannot find gid for %s", group); 1.152 + return gid; 1.153 +} 1.154 + 1.155 +void * const uninit = (void *)0xdeadbeef; 1.156 + 1.157 +int 1.158 +main(int argc, char **argv) 1.159 +{ 1.160 + char * base = uninit; 1.161 + char * bp = uninit; 1.162 + char * cp = uninit; 1.163 + char * cwd = 0; 1.164 + char * group = 0; 1.165 + char * linkname = 0; 1.166 + char * linkprefix = 0; 1.167 + char * name = uninit; 1.168 + char * owner = 0; 1.169 + char * todir = uninit; 1.170 + char * toname = uninit; 1.171 + 1.172 + int bnlen = -1; 1.173 + int cc = 0; 1.174 + int dodir = 0; 1.175 + int dolink = 0; 1.176 + int dorelsymlink = 0; 1.177 + int dotimes = 0; 1.178 + int exists = 0; 1.179 + int fromfd = -1; 1.180 + int len = -1; 1.181 + int lplen = 0; 1.182 + int onlydir = 0; 1.183 + int opt = -1; 1.184 + int tdlen = -1; 1.185 + int tofd = -1; 1.186 + int wc = -1; 1.187 + 1.188 + mode_t mode = 0755; 1.189 + 1.190 + uid_t uid = -1; 1.191 + gid_t gid = -1; 1.192 + 1.193 + struct stat sb; 1.194 + struct stat tosb; 1.195 + struct utimbuf utb; 1.196 + char buf[BUFSIZ]; 1.197 + 1.198 + program = strrchr(argv[0], '/'); 1.199 + if (!program) 1.200 + program = strrchr(argv[0], '\\'); 1.201 + program = program ? program+1 : argv[0]; 1.202 + 1.203 + 1.204 + while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) { 1.205 + switch (opt) { 1.206 + case 'C': cwd = optarg; break; 1.207 + case 'D': onlydir = 1; break; 1.208 + case 'd': dodir = 1; break; 1.209 + case 'l': dolink = 1; break; 1.210 + case 'L': 1.211 + linkprefix = optarg; 1.212 + lplen = strlen(linkprefix); 1.213 + dolink = 1; 1.214 + break; 1.215 + case 'R': dolink = dorelsymlink = 1; break; 1.216 + case 'm': 1.217 + mode = strtoul(optarg, &cp, 8); 1.218 + if (mode == 0 && cp == optarg) 1.219 + usage(); 1.220 + break; 1.221 + case 'o': owner = optarg; break; 1.222 + case 'g': group = optarg; break; 1.223 + case 't': dotimes = 1; break; 1.224 + default: usage(); 1.225 + } 1.226 + } 1.227 + 1.228 + argc -= optind; 1.229 + argv += optind; 1.230 + if (argc < 2 - onlydir) 1.231 + usage(); 1.232 + 1.233 + todir = argv[argc-1]; 1.234 + if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) && 1.235 + mkdirs(todir, 0777) < 0) { 1.236 + fail("cannot mkdir -p %s", todir); 1.237 + } 1.238 + if (onlydir) 1.239 + return 0; 1.240 + 1.241 + if (!cwd) { 1.242 + cwd = GETCWD(0, PATH_MAX); 1.243 + if (!cwd) 1.244 + fail("could not get CWD"); 1.245 + } 1.246 + 1.247 + /* make sure we can get into todir. */ 1.248 + xchdir(todir); 1.249 + todir = GETCWD(0, PATH_MAX); 1.250 + if (!todir) 1.251 + fail("could not get CWD in todir"); 1.252 + tdlen = strlen(todir); 1.253 + 1.254 + /* back to original directory. */ 1.255 + xchdir(cwd); 1.256 + 1.257 + uid = owner ? touid(owner) : -1; 1.258 + gid = group ? togid(group) : -1; 1.259 + 1.260 + while (--argc > 0) { 1.261 + name = *argv++; 1.262 + len = strlen(name); 1.263 + base = xbasename(name); 1.264 + bnlen = strlen(base); 1.265 + toname = (char*)xmalloc(tdlen + 1 + bnlen + 1); 1.266 + sprintf(toname, "%s/%s", todir, base); 1.267 +retry: 1.268 + exists = (lstat(toname, &tosb) == 0); 1.269 + 1.270 + if (dodir) { 1.271 + /* -d means create a directory, always */ 1.272 + if (exists && !S_ISDIR(tosb.st_mode)) { 1.273 + int rv = unlink(toname); 1.274 + if (rv) 1.275 + fail("cannot unlink %s", toname); 1.276 + exists = 0; 1.277 + } 1.278 + if (!exists && mkdir(toname, mode) < 0) { 1.279 + /* we probably have two nsinstall programs in a race here. */ 1.280 + if (errno == EEXIST && !stat(toname, &sb) && 1.281 + S_ISDIR(sb.st_mode)) { 1.282 + fprintf(stderr, "directory creation race: %s\n", toname); 1.283 + goto retry; 1.284 + } 1.285 + fail("cannot make directory %s", toname); 1.286 + } 1.287 + if ((owner || group) && chown(toname, uid, gid) < 0) 1.288 + fail("cannot change owner of %s", toname); 1.289 + } else if (dolink) { 1.290 + if (*name == '/') { 1.291 + /* source is absolute pathname, link to it directly */ 1.292 + linkname = 0; 1.293 + } else { 1.294 + if (linkprefix) { 1.295 + /* -L implies -l and prefixes names with a $cwd arg. */ 1.296 + len += lplen + 1; 1.297 + linkname = (char*)xmalloc(len + 1); 1.298 + sprintf(linkname, "%s/%s", linkprefix, name); 1.299 + } else if (dorelsymlink) { 1.300 + /* Symlink the relative path from todir to source name. */ 1.301 + linkname = (char*)xmalloc(PATH_MAX); 1.302 + 1.303 + if (*todir == '/') { 1.304 + /* todir is absolute: skip over common prefix. */ 1.305 + lplen = relatepaths(todir, cwd, linkname); 1.306 + strcpy(linkname + lplen, name); 1.307 + } else { 1.308 + /* todir is named by a relative path: reverse it. */ 1.309 + reversepath(todir, name, len, linkname); 1.310 + xchdir(cwd); 1.311 + } 1.312 + 1.313 + len = strlen(linkname); 1.314 + } 1.315 + name = linkname; 1.316 + } 1.317 + 1.318 + /* Check for a pre-existing symlink with identical content. */ 1.319 + if (exists && 1.320 + (!S_ISLNK(tosb.st_mode) || 1.321 + readlink(toname, buf, sizeof buf) != len || 1.322 + strncmp(buf, name, len) != 0)) { 1.323 + int rmrv; 1.324 + rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); 1.325 + if (rmrv < 0) { 1.326 + fail("destination exists, cannot remove %s", toname); 1.327 + } 1.328 + exists = 0; 1.329 + } 1.330 + if (!exists && symlink(name, toname) < 0) { 1.331 + if (errno == EEXIST) { 1.332 + fprintf(stderr, "symlink creation race: %s\n", toname); 1.333 + fail("symlink was attempted in working directory %s " 1.334 + "from %s to %s.\n", cwd, name, toname); 1.335 + goto retry; 1.336 + } 1.337 + diagnosePath(toname); 1.338 + fail("cannot make symbolic link %s", toname); 1.339 + } 1.340 +#ifdef HAVE_LCHOWN 1.341 + if ((owner || group) && lchown(toname, uid, gid) < 0) 1.342 + fail("cannot change owner of %s", toname); 1.343 +#endif 1.344 + 1.345 + if (linkname) { 1.346 + free(linkname); 1.347 + linkname = 0; 1.348 + } 1.349 + } else { 1.350 + /* Copy from name to toname, which might be the same file. */ 1.351 + fromfd = open(name, O_RDONLY); 1.352 + if (fromfd < 0 || fstat(fromfd, &sb) < 0) 1.353 + fail("cannot access %s", name); 1.354 + if (exists && 1.355 + (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) { 1.356 + int rmrv; 1.357 + rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); 1.358 + if (rmrv < 0) { 1.359 + fail("destination exists, cannot remove %s", toname); 1.360 + } 1.361 + } 1.362 + tofd = open(toname, O_CREAT | O_WRONLY, 0666); 1.363 + if (tofd < 0) 1.364 + fail("cannot create %s", toname); 1.365 + 1.366 + bp = buf; 1.367 + while ((cc = read(fromfd, bp, sizeof buf)) > 0) { 1.368 + while ((wc = write(tofd, bp, cc)) > 0) { 1.369 + if ((cc -= wc) == 0) 1.370 + break; 1.371 + bp += wc; 1.372 + } 1.373 + if (wc < 0) 1.374 + fail("cannot write to %s", toname); 1.375 + } 1.376 + if (cc < 0) 1.377 + fail("cannot read from %s", name); 1.378 + 1.379 + if (ftruncate(tofd, sb.st_size) < 0) 1.380 + fail("cannot truncate %s", toname); 1.381 + if (dotimes) { 1.382 + utb.actime = sb.st_atime; 1.383 + utb.modtime = sb.st_mtime; 1.384 + if (utime(toname, &utb) < 0) 1.385 + fail("cannot set times of %s", toname); 1.386 + } 1.387 +#ifdef HAVE_FCHMOD 1.388 + if (fchmod(tofd, mode) < 0) 1.389 +#else 1.390 + if (chmod(toname, mode) < 0) 1.391 +#endif 1.392 + fail("cannot change mode of %s", toname); 1.393 + 1.394 + if ((owner || group) && fchown(tofd, uid, gid) < 0) 1.395 + fail("cannot change owner of %s", toname); 1.396 + 1.397 + /* Must check for delayed (NFS) write errors on close. */ 1.398 + if (close(tofd) < 0) 1.399 + fail("close reports write error on %s", toname); 1.400 + close(fromfd); 1.401 + } 1.402 + 1.403 + free(toname); 1.404 + } 1.405 + 1.406 + free(cwd); 1.407 + free(todir); 1.408 + return 0; 1.409 +} 1.410 +