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