security/nss/coreconf/nsinstall/nsinstall.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /*
     6 ** Netscape portable install command.
     7 */
     8 #include <stdio.h>  /* OSF/1 requires this before grp.h, so put it first */
     9 #include <assert.h>
    10 #include <fcntl.h>
    11 #include <string.h>
    12 #if defined(_WINDOWS)
    13 #include <windows.h>
    14 typedef unsigned int mode_t;
    15 #else
    16 #include <grp.h>
    17 #include <pwd.h>
    18 #include <errno.h>
    19 #include <stdlib.h>
    20 #include <unistd.h>
    21 #include <utime.h>
    22 #endif
    23 #include <sys/types.h>
    24 #include <sys/stat.h>
    25 #include "pathsub.h"
    27 #define HAVE_LCHOWN
    29 #if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) || defined(NTO) || defined(DARWIN) || defined(BEOS) || defined(__riscos__)
    30 #undef HAVE_LCHOWN
    31 #endif
    33 #define HAVE_FCHMOD
    35 #if defined(BEOS)
    36 #undef HAVE_FCHMOD
    37 #endif
    39 #ifdef LINUX
    40 #include <getopt.h>
    41 #endif
    43 #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC)
    44 #if !defined(S_ISLNK) && defined(S_IFLNK)
    45 #define S_ISLNK(a)	(((a) & S_IFMT) == S_IFLNK)
    46 #endif
    47 #endif
    49 #if defined(SNI)
    50 extern int fchmod(int fildes, mode_t mode);
    51 #endif
    54 #ifdef GETCWD_CANT_MALLOC
    55 /*
    56  * this should probably go into a utility library in case other applications
    57  * need it.
    58  */
    59 static char *
    60 getcwd_do_malloc(char *path, int len) {
    62     if (!path) {
    63 	path = malloc(PATH_MAX +1);
    64 	if (!path) return NULL;
    65     }
    66     return getcwd(path, PATH_MAX);
    67 }
    68 #define GETCWD	getcwd_do_malloc
    69 #else
    70 #define GETCWD	getcwd
    71 #endif
    74 static void
    75 usage(void)
    76 {
    77     fprintf(stderr,
    78 	"usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
    79 	"       %*s [-DdltR] file [file ...] directory\n",
    80 	program, (int)strlen(program), "");
    81     exit(2);
    82 }
    84 /* this is more-or-less equivalent to mkdir -p */
    85 static int
    86 mkdirs(char *path, mode_t mode)
    87 {
    88     char *      cp;
    89     int         rv;
    90     struct stat sb;
    92     if (!path || !path[0]) 
    93 	fail("Null pointer or empty string passed to mkdirs()");
    94     while (*path == '/' && path[1] == '/')
    95 	path++;
    96     for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--);
    97     if (cp && cp != path) {
    98 	*cp = '\0';
    99 	if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
   100 	    mkdirs(path, mode) < 0) {
   101 	    return -1;
   102 	}
   103 	*cp = '/';
   104     }
   105     rv = mkdir(path, mode);
   106     if (rv) {
   107 	if (errno != EEXIST)
   108 	    fail("mkdirs cannot make %s", path);
   109 	fprintf(stderr, "directory creation race: %s\n", path);
   110 	if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) 
   111 	    rv = 0;
   112     }
   113     return rv;
   114 }
   116 static uid_t
   117 touid(char *owner)
   118 {
   119     struct passwd *pw;
   120     uid_t uid;
   121     char *cp;
   123     if (!owner || !owner[0]) 
   124 	fail("Null pointer or empty string passed to touid()");
   125     pw = getpwnam(owner);
   126     if (pw)
   127 	return pw->pw_uid;
   128     uid = strtol(owner, &cp, 0);
   129     if (uid == 0 && cp == owner)
   130 	fail("cannot find uid for %s", owner);
   131     return uid;
   132 }
   134 static gid_t
   135 togid(char *group)
   136 {
   137     struct group *gr;
   138     gid_t gid;
   139     char *cp;
   141     if (!group || !group[0]) 
   142 	fail("Null pointer or empty string passed to togid()");
   143     gr = getgrnam(group);
   144     if (gr)
   145 	return gr->gr_gid;
   146     gid = strtol(group, &cp, 0);
   147     if (gid == 0 && cp == group)
   148 	fail("cannot find gid for %s", group);
   149     return gid;
   150 }
   152 void * const uninit = (void *)0xdeadbeef;
   154 int
   155 main(int argc, char **argv)
   156 {
   157     char *	base		= uninit;
   158     char *	bp		= uninit;
   159     char *	cp		= uninit;
   160     char *	cwd		= 0;
   161     char *	group		= 0;
   162     char *	linkname	= 0;
   163     char *	linkprefix	= 0;
   164     char *	name		= uninit;
   165     char *	owner		= 0;
   166     char *	todir		= uninit;
   167     char *	toname		= uninit;
   169     int 	bnlen		= -1;
   170     int 	cc		= 0;
   171     int 	dodir		= 0;
   172     int 	dolink		= 0;
   173     int 	dorelsymlink	= 0;
   174     int 	dotimes		= 0;
   175     int 	exists		= 0;
   176     int 	fromfd		= -1;
   177     int 	len		= -1;
   178     int 	lplen		= 0;
   179     int		onlydir		= 0;
   180     int 	opt		= -1;
   181     int 	tdlen		= -1;
   182     int 	tofd		= -1;
   183     int 	wc		= -1;
   185     mode_t 	mode		= 0755;
   187     uid_t 	uid		= -1;
   188     gid_t 	gid		= -1;
   190     struct stat sb;
   191     struct stat tosb;
   192     struct utimbuf utb;
   193     char 	buf[BUFSIZ];
   195     program = strrchr(argv[0], '/');
   196     if (!program)
   197 	program = strrchr(argv[0], '\\');
   198     program = program ? program+1 : argv[0];
   201     while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
   202 	switch (opt) {
   203 	  case 'C': cwd = optarg;	break;
   204 	  case 'D': onlydir = 1; 	break;
   205 	  case 'd': dodir = 1; 		break;
   206 	  case 'l': dolink = 1;		break;
   207 	  case 'L':
   208 	    linkprefix = optarg;
   209 	    lplen = strlen(linkprefix);
   210 	    dolink = 1;
   211 	    break;
   212 	  case 'R': dolink = dorelsymlink = 1; break;
   213 	  case 'm':
   214 	    mode = strtoul(optarg, &cp, 8);
   215 	    if (mode == 0 && cp == optarg)
   216 		usage();
   217 	    break;
   218 	  case 'o': owner = optarg; 	break;
   219 	  case 'g': group = optarg; 	break;
   220 	  case 't': dotimes = 1; 	break;
   221 	  default:  usage();
   222 	}
   223     }
   225     argc -= optind;
   226     argv += optind;
   227     if (argc < 2 - onlydir)
   228 	usage();
   230     todir = argv[argc-1];
   231     if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
   232 	mkdirs(todir, 0777) < 0) {
   233 	fail("cannot mkdir -p %s", todir);
   234     }
   235     if (onlydir)
   236 	return 0;
   238     if (!cwd) {
   239 	cwd = GETCWD(0, PATH_MAX);
   240 	if (!cwd)
   241 	    fail("could not get CWD");
   242     }
   244     /* make sure we can get into todir. */
   245     xchdir(todir);
   246     todir = GETCWD(0, PATH_MAX);
   247     if (!todir)
   248 	fail("could not get CWD in todir");
   249     tdlen = strlen(todir);
   251     /* back to original directory. */
   252     xchdir(cwd);
   254     uid = owner ? touid(owner) : -1;
   255     gid = group ? togid(group) : -1;
   257     while (--argc > 0) {
   258 	name   = *argv++;
   259 	len    = strlen(name);
   260 	base   = xbasename(name);
   261 	bnlen  = strlen(base);
   262 	toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
   263 	sprintf(toname, "%s/%s", todir, base);
   264 retry:
   265 	exists = (lstat(toname, &tosb) == 0);
   267 	if (dodir) {
   268 	    /* -d means create a directory, always */
   269 	    if (exists && !S_ISDIR(tosb.st_mode)) {
   270 		int rv = unlink(toname);
   271 		if (rv)
   272 		    fail("cannot unlink %s", toname);
   273 		exists = 0;
   274 	    }
   275 	    if (!exists && mkdir(toname, mode) < 0) {
   276 	    	/* we probably have two nsinstall programs in a race here. */
   277 		if (errno == EEXIST && !stat(toname, &sb) && 
   278 		    S_ISDIR(sb.st_mode)) {
   279 		    fprintf(stderr, "directory creation race: %s\n", toname);
   280 		    goto retry;
   281 	    	}
   282 		fail("cannot make directory %s", toname);
   283 	    }
   284 	    if ((owner || group) && chown(toname, uid, gid) < 0)
   285 		fail("cannot change owner of %s", toname);
   286 	} else if (dolink) {
   287 	    if (*name == '/') {
   288 		/* source is absolute pathname, link to it directly */
   289 		linkname = 0;
   290 	    } else {
   291 		if (linkprefix) {
   292 		    /* -L implies -l and prefixes names with a $cwd arg. */
   293 		    len += lplen + 1;
   294 		    linkname = (char*)xmalloc(len + 1);
   295 		    sprintf(linkname, "%s/%s", linkprefix, name);
   296 		} else if (dorelsymlink) {
   297 		    /* Symlink the relative path from todir to source name. */
   298 		    linkname = (char*)xmalloc(PATH_MAX);
   300 		    if (*todir == '/') {
   301 			/* todir is absolute: skip over common prefix. */
   302 			lplen = relatepaths(todir, cwd, linkname);
   303 			strcpy(linkname + lplen, name);
   304 		    } else {
   305 			/* todir is named by a relative path: reverse it. */
   306 			reversepath(todir, name, len, linkname);
   307 			xchdir(cwd);
   308 		    }
   310 		    len = strlen(linkname);
   311 		}
   312 		name = linkname;
   313 	    }
   315 	    /* Check for a pre-existing symlink with identical content. */
   316 	    if (exists &&
   317 		(!S_ISLNK(tosb.st_mode) ||
   318 		 readlink(toname, buf, sizeof buf) != len ||
   319 		 strncmp(buf, name, len) != 0)) {
   320 		int rmrv;
   321 		rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
   322 		if (rmrv < 0) {
   323 		    fail("destination exists, cannot remove %s", toname);
   324 		}
   325 		exists = 0;
   326 	    }
   327 	    if (!exists && symlink(name, toname) < 0) {
   328 		if (errno == EEXIST) {
   329 		    fprintf(stderr, "symlink creation race: %s\n", toname);
   330                     fail("symlink was attempted in working directory %s "
   331                          "from %s to %s.\n", cwd, name, toname);
   332 		    goto retry;
   333 		}
   334 		diagnosePath(toname);
   335 		fail("cannot make symbolic link %s", toname);
   336 	    }
   337 #ifdef HAVE_LCHOWN
   338 	    if ((owner || group) && lchown(toname, uid, gid) < 0)
   339 		fail("cannot change owner of %s", toname);
   340 #endif
   342 	    if (linkname) {
   343 		free(linkname);
   344 		linkname = 0;
   345 	    }
   346 	} else {
   347 	    /* Copy from name to toname, which might be the same file. */
   348 	    fromfd = open(name, O_RDONLY);
   349 	    if (fromfd < 0 || fstat(fromfd, &sb) < 0)
   350 		fail("cannot access %s", name);
   351 	    if (exists && 
   352 	        (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) {
   353 		int rmrv;
   354 		rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
   355 		if (rmrv < 0) {
   356 		    fail("destination exists, cannot remove %s", toname);
   357 		}
   358 	    }
   359 	    tofd = open(toname, O_CREAT | O_WRONLY, 0666);
   360 	    if (tofd < 0)
   361 		fail("cannot create %s", toname);
   363 	    bp = buf;
   364 	    while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
   365 		while ((wc = write(tofd, bp, cc)) > 0) {
   366 		    if ((cc -= wc) == 0)
   367 			break;
   368 		    bp += wc;
   369 		}
   370 		if (wc < 0)
   371 		    fail("cannot write to %s", toname);
   372 	    }
   373 	    if (cc < 0)
   374 		fail("cannot read from %s", name);
   376 	    if (ftruncate(tofd, sb.st_size) < 0)
   377 		fail("cannot truncate %s", toname);
   378 	    if (dotimes) {
   379 		utb.actime = sb.st_atime;
   380 		utb.modtime = sb.st_mtime;
   381 		if (utime(toname, &utb) < 0)
   382 		    fail("cannot set times of %s", toname);
   383 	    }
   384 #ifdef HAVE_FCHMOD
   385 	    if (fchmod(tofd, mode) < 0)
   386 #else
   387 	    if (chmod(toname, mode) < 0)
   388 #endif
   389 		fail("cannot change mode of %s", toname);
   391 	    if ((owner || group) && fchown(tofd, uid, gid) < 0)
   392 		fail("cannot change owner of %s", toname);
   394 	    /* Must check for delayed (NFS) write errors on close. */
   395 	    if (close(tofd) < 0)
   396 		fail("close reports write error on %s", toname);
   397 	    close(fromfd);
   398 	}
   400 	free(toname);
   401     }
   403     free(cwd);
   404     free(todir);
   405     return 0;
   406 }

mercurial