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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 #include <errno.h>
31 #include <pwd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 
41 #include <tsol/label.h>
42 #include <zone.h>
43 #include <sys/stat.h>
44 
45 #include "setupfiles.h"
46 
47 #define	dperror(s) if (flags & DIAG) perror(s)
48 #define	dprintf(s, v) if (flags & DBUG) (void) printf(s, v)
49 #define	dprintf2(s, v1, v2) if (flags & DBUG) (void) printf(s, v1, v2)
50 
51 static int mkdirs(const char *dir, const char *target, int flags);
52 static int copyfile(const char *min_home, const char *home, const char *target,
53     int flags);
54 static int linkfile(const char *min_home, const char *home, const char *target,
55     int flags);
56 
57 
58 /*
59  *	__setupfiles - Process copy and link files directions in min $HOME.
60  *
61  *	Entry	pwd = user's password file entry.
62  *		min_sl = user's minimum SL.
63  *		flags = DBUG, if print debug messages.
64  *			DIAG, if print diagnostics (perrors).
65  *			IGNE, continue rather than abort on failures.
66  *			REPC, if replace existing file.
67  *			REPL, if replace existing symbolic link.
68  *		process is running as user at correct label.
69  *
70  *	Exit	None.
71  *
72  *	Returns	0, if success.
73  *		errno, if failure.
74  *
75  *	Uses	COPY, CP, LINK, MAXPATHLEN.
76  *
77  *	Calls	blequal, copyfile, feof, fgets, fopen, getcmwplabel, stobsl,
78  *		mkdirs, getzoneid, getzonelabelbyid, linkfile, strcat, strcpy,
79  *		strlen.
80  *
81  *	This program assumes the /zone is the autofs mountpoint for
82  *	cross-zone mounts.
83  *
84  *	It also assumes that the user's home directory path is the
85  *	the same in each zone, relative to the zone's root.
86  *
87  *	At this point, the cross-zone automounter only supports home
88  * 	directories starting with /home
89  */
90 
91 int
92 __setupfiles(const struct passwd *pwd, const bslabel_t *min_sl, int flags)
93 {
94 	bslabel_t *plabel;		/* process label */
95 	char	home[MAXPATHLEN];	/* real path to current $HOME */
96 	char	min_home[MAXPATHLEN];	/* real path to min $HOME */
97 	char	cl_file[MAXPATHLEN];	/* real path to .copy/.link_files */
98 	char	file[MAXPATHLEN];	/* file to copy/link */
99 	FILE	*clf;			/* .copy/.link_file stream */
100 	char	zoneroot[MAXPATHLEN];
101 	zoneid_t zoneid;
102 	zoneid_t min_zoneid;
103 
104 	zoneid = getzoneid();
105 	if ((plabel = getzonelabelbyid(zoneid)) == NULL) {
106 
107 		dperror("setupfiles can't get process label");
108 		return (errno);
109 	}
110 
111 	if (blequal(plabel, min_sl)) {
112 		/* at min SL no files to setup */
113 
114 		return (0);
115 	}
116 
117 	/* get current home real path */
118 
119 	(void) strlcpy(home, pwd->pw_dir, MAXPATHLEN);
120 
121 	/* Get zone id from min_sl */
122 
123 	if ((min_zoneid = getzoneidbylabel(min_sl)) == -1) {
124 
125 		dperror("setupfiles can't get zoneid for min sl");
126 		return (errno);
127 	}
128 
129 	/*
130 	 * Since the global zone home directories aren't public
131 	 * information, we don't support copy and link files there.
132 	 */
133 	if (min_zoneid == GLOBAL_ZONEID)
134 		return (0);
135 
136 	/*
137 	 * Get zone root path from zone id
138 	 *
139 	 * Could have used getzonenamebyid() but this assumes that /etc/zones
140 	 * directory is available, which is not true in labeled zones
141 	 */
142 
143 	if (zone_getattr(min_zoneid, ZONE_ATTR_ROOT, zoneroot,
144 	    sizeof (zoneroot)) == -1) {
145 		dperror("setupfiles can't get zone root path for min sl");
146 		return (errno);
147 	}
148 
149 	(void) snprintf(min_home, MAXPATHLEN, "%s%s",
150 	    zoneroot, pwd->pw_dir);
151 
152 	/* process copy files */
153 
154 	if ((strlen(min_home) + strlen(COPY)) > (MAXPATHLEN - 1)) {
155 
156 		dprintf("setupfiles copy path %s", min_home);
157 		dprintf("%s ", COPY);
158 		dprintf("greater than %d\n", MAXPATHLEN);
159 		errno = ENAMETOOLONG;
160 		dperror("setupfiles copy path");
161 		return (errno);
162 	}
163 
164 	(void) strcpy(cl_file, min_home);
165 	(void) strcat(cl_file, COPY);
166 
167 	if ((clf = fopen(cl_file, "r")) != NULL) {
168 
169 		while (fgets(file, MAXPATHLEN, clf) != NULL) {
170 
171 			if (!feof(clf))		/* remove trailing \n */
172 				file[strlen(file) - 1] = '\0';
173 
174 			dprintf("copy file %s requested\n", file);
175 
176 			/* make any needed subdirectories */
177 
178 			if (mkdirs(home, file, flags) != 0) {
179 
180 				if ((flags & IGNE) == 0)
181 					return (errno);
182 				else
183 					continue;
184 			}
185 
186 			/* copy the file */
187 
188 			if (copyfile(min_home, home, file, flags) != 0) {
189 
190 				if ((flags & IGNE) == 0)
191 					return (errno);
192 				else
193 					continue;
194 
195 			}
196 
197 		}  /* while (fgets( ... ) != NULL) */
198 	} else {
199 		if (errno != ENOENT)
200 			dperror("setupfiles copy file open");
201 		dprintf("setupfiles no copyfile %s\n", cl_file);
202 	}  /* process copy files */
203 
204 
205 	/* process link files */
206 
207 	if ((strlen(min_home) + strlen(LINK)) > (MAXPATHLEN - 1)) {
208 
209 		dprintf("setupfiles link path %s", min_home);
210 		dprintf("%s ", LINK);
211 		dprintf("greater than %d\n", MAXPATHLEN);
212 		errno = ENAMETOOLONG;
213 		dperror("setupfiles link path");
214 		return (errno);
215 	}
216 
217 	(void) strcpy(cl_file, min_home);
218 	(void) strcat(cl_file, LINK);
219 
220 	if ((clf = fopen(cl_file, "r")) != NULL) {
221 
222 		while (fgets(file, MAXPATHLEN, clf) != NULL) {
223 
224 			if (!feof(clf))		/* remove trailing \n */
225 				file[strlen(file) - 1] = '\0';
226 
227 			dprintf("link file %s requested\n", file);
228 
229 			/* make any needed subdirectories */
230 
231 			if (mkdirs(home, file, flags) != 0) {
232 
233 				if ((flags & IGNE) == 0)
234 					return (errno);
235 				else
236 					continue;
237 			}
238 
239 			/* link the file */
240 
241 			if (linkfile(min_home, home, file, flags) != 0) {
242 
243 				if ((flags & IGNE) == 0)
244 					return (errno);
245 				else
246 					continue;
247 			}
248 
249 		}  /* while (fgets ... ) != NULL) */
250 	} else {
251 		if (errno != ENOENT)
252 			dperror("setupfiles link file open");
253 		dprintf("setupfiles no linkfile %s\n", cl_file);
254 	}  /* process link files */
255 
256 	return (0);
257 }  /* setupfiles() */
258 
259 
260 /*
261  *	mkdirs - Make any needed subdirectories in target's path.
262  *
263  *	Entry	home = base directory.
264  *		file = file to create with intermediate subdirectories.
265  *		flags = from __setupfiles -- for dprintf and dperror.
266  *
267  *	Exit	Needed subdirectories made.
268  *
269  *	Returns	0, if success.
270  *		errno, if failure.
271  *
272  *	Uses	MAXPATHLEN.
273  *
274  *	Calls	mkdir, strcat, strcpy, strlen, strtok.
275  */
276 
277 static int
278 mkdirs(const char *home, const char *file, int flags)
279 {
280 	char	path[MAXPATHLEN];
281 	char	dir[MAXPATHLEN];
282 	char	*tok;
283 
284 	if ((strlen(home) + strlen(file)) > (MAXPATHLEN - 2)) {
285 
286 		dprintf("setupfiles mkdirs path %s", home);
287 		dprintf("/%s ", file);
288 		dprintf("greater than %d\n", MAXPATHLEN);
289 		errno = ENAMETOOLONG;
290 		dperror("setupfiles mkdirs");
291 		return (errno);
292 	}
293 
294 	(void) strcpy(dir, file);
295 
296 	if ((tok = strrchr(dir, '/')) == NULL) {
297 
298 		dprintf("setupfiles no dirs to make in %s\n", dir);
299 		return (0);
300 	}
301 
302 	*tok = '\000';		/* drop last component, it's the target */
303 
304 	(void) strcpy(path, home);
305 
306 	for (tok = dir; tok = strtok(tok, "/"); tok = NULL) {
307 
308 		(void) strcat(path, "/");
309 		(void) strcat(path, tok);
310 
311 		if ((mkdir(path, 0777) != 0) && (errno != EEXIST)) {
312 
313 			dperror("setupfiles mkdir");
314 			dprintf("setupfiles mkdir path %s\n", path);
315 			return (errno);
316 		}
317 
318 		dprintf("setupfiles dir %s made or already exists\n", path);
319 	}
320 
321 	return (0);
322 }  /* mkdirs() */
323 
324 
325 /*
326  *	copyfile - Copy a file from the base home directory to the current.
327  *
328  *	Entry	min_home = from home directory.
329  *		home = current (to) home directory.
330  *		target = file to copy.
331  *		flags = from __setupfiles.
332  *			REPC, if replace existing file.
333  *
334  *	Exit	File copied.
335  *
336  *	Returns	0, if success.
337  *		errno, if failure.
338  *
339  *	Uses	CP, MAXPATHLEN.
340  *
341  *	Calls	access, execlp, exit, lstat, strcat, strcpy, strlen, unlink,
342  *		vfork, waitpid.
343  */
344 
345 static int
346 copyfile(const char *min_home, const char *home, const char *target, int flags)
347 {
348 	char	src[MAXPATHLEN];
349 	char	dest[MAXPATHLEN];
350 	struct stat	buf;
351 	pid_t	child;
352 
353 	/* prepare target */
354 
355 	if (snprintf(dest, sizeof (dest), "%s/%s", home, target) >
356 	    sizeof (dest) - 1) {
357 		dprintf("setupfiles copy dest %s", dest);
358 		dprintf("greater than %d\n", sizeof (dest));
359 		errno = ENAMETOOLONG;
360 		dperror("setupfiles copy to home");
361 		return (errno);
362 	}
363 
364 	if (lstat(dest, &buf) == 0) {
365 		/* target exists */
366 
367 		if (flags & REPC) {
368 			/* unlink and replace */
369 
370 			if (unlink(dest) != 0) {
371 
372 				dperror("setupfiles copy unlink");
373 				dprintf("setupfiles copy unable to unlink %s\n",
374 				    dest);
375 				return (errno);
376 			}
377 		} else {
378 			/* target exists and is not to be replaced */
379 
380 			return (0);
381 		}
382 	} else if (errno != ENOENT) {
383 		/* error on target */
384 
385 		dperror("setupfiles copy");
386 		dprintf("setupfiles copy lstat %s\n", dest);
387 		return (errno);
388 	}
389 
390 	/* prepare source */
391 
392 	if (snprintf(src, sizeof (src), "%s/%s", min_home, target) >
393 	    sizeof (src) - 1) {
394 		dprintf("setupfiles copy path %s", src);
395 		dprintf("greater than %d\n", sizeof (src));
396 		errno = ENAMETOOLONG;
397 		dperror("setupfiles copy from home");
398 		return (errno);
399 	}
400 
401 	if (access(src, R_OK) != 0) {
402 		/* can't access source */
403 
404 		dperror("setupfiles copy source access");
405 		dprintf("setupfiles copy unable to access %s\n", src);
406 		return (errno);
407 	}
408 
409 	/* attempt the copy */
410 
411 	dprintf("setupfiles attempting to copy %s\n", src);
412 	dprintf("\tto %s\n", dest);
413 
414 	if ((child = vfork()) != 0) {	/* parent, wait for child status */
415 		int	status;	/* child status */
416 
417 		(void) waitpid(child, &status, 0);  /* wait for child */
418 		dprintf("setupfiles copy child returned %x\n", status);
419 	} else {
420 		/* execute "cp -p min_home home" */
421 
422 		if (execlp(CP, CP, "-p", src, dest, 0) != 0) {
423 			/* can't execute cp */
424 
425 			dperror("setupfiles copy exec");
426 			dprintf("setupfiles copy couldn't exec \"%s  -p\"\n",
427 			    CP);
428 			exit(2);
429 		}
430 	}
431 
432 	return (0);
433 }  /* copyfile() */
434 
435 
436 /*
437  *	linkfile - Make a symlink from the the current directory to the base
438  *			home directory.
439  *
440  *	Entry	min_home = from home directory.
441  *		home = current (to) home directory.
442  *		target = file to copy.
443  *		flags = from __setupfiles.
444  *			REPL, if replace existing symlink.
445  *
446  *	Exit	File symlinked.
447  *
448  *	Returns	0, if success.
449  *		errno, if failure.
450  *
451  *	Uses	MAXPATHLEN.
452  *
453  *	Calls	lstat, symlink, strcat, strcpy, strlen, unlink.
454  */
455 
456 static int
457 linkfile(const char *min_home, const char *home, const char *target, int flags)
458 {
459 	char	src[MAXPATHLEN];
460 	char	dest[MAXPATHLEN];
461 	struct stat	buf;
462 
463 	/* prepare target */
464 
465 	if (snprintf(dest, sizeof (dest), "%s/%s", home, target) >
466 	    sizeof (dest) - 1) {
467 		dprintf("setupfiles link dest %s", dest);
468 		dprintf("greater than %d\n", sizeof (dest));
469 		errno = ENAMETOOLONG;
470 		dperror("setupfiles link to home");
471 		return (errno);
472 	}
473 
474 	if (lstat(dest, &buf) == 0) {
475 		/* target exists */
476 
477 		if (flags & REPL) {
478 			/* unlink and replace */
479 			if (unlink(dest) != 0) {
480 				dperror("setupfiles link unlink");
481 				dprintf("setupfiles link unable to unlink %s\n",
482 				    dest);
483 				return (errno);
484 			}
485 		} else {
486 			/* target exists and is not to be replaced */
487 			return (0);
488 		}
489 	} else if (errno != ENOENT) {
490 		/* error on target */
491 		dperror("setupfiles link");
492 		dprintf("setupfiles link lstat %s\n", dest);
493 		return (errno);
494 	}
495 
496 	if (snprintf(src, sizeof (src), "%s/%s", min_home, target) >
497 	    sizeof (src) - 1) {
498 		dprintf("setupfiles link path %s", src);
499 		dprintf("greater than %d\n", sizeof (src));
500 		errno = ENAMETOOLONG;
501 		dperror("setupfiles link from home");
502 		return (errno);
503 	}
504 
505 	/* attempt the copy */
506 
507 	dprintf("setupfiles attempting to link %s\n", dest);
508 	dprintf("\tto %s\n", src);
509 
510 	if (symlink(src, dest) != 0) {
511 		dperror("setupfiles link symlink");
512 		dprintf("setupfiles link unable to symlink%s\n", "");
513 		return (errno);
514 	}
515 
516 	return (0);
517 }  /* linkfile */
518