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
__setupfiles(const struct passwd * pwd,const m_label_t * min_sl,int flags)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
mkdirs(const char * home,const char * file,int flags)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
copyfile(const char * min_home,const char * home,const char * target,int flags)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
linkfile(const char * min_home,const char * home,const char * target,int flags)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