3 * Choose and check temporary directories
5 * (c) 1999 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of chkpath.
12 * chkpath is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * chkpath is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with chkpath; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 /*----- Header files ------------------------------------------------------*/
35 #include <sys/types.h>
41 #include <mLib/alloc.h>
42 #include <mLib/dstr.h>
43 #include <mLib/macros.h>
44 #include <mLib/mdwopt.h>
45 #include <mLib/quis.h>
46 #include <mLib/report.h>
48 #include "checkpath.h"
50 /*----- Static variables --------------------------------------------------*/
53 static struct checkpath cp
;
54 static struct passwd
*pw
;
56 /*----- Main code ---------------------------------------------------------*/
60 * Arguments: @const char *p@ = pathname to check
61 * @int *f@ = try-to-create flag
63 * Returns: Nonzero if the directory is OK
65 * Use: Ensures that a directory is OK. If @f@ is a real pointer,
66 * and @*f@ is set, then try to create the directory.
69 static int ok(const char *p
, int *f
)
73 /* --- Read the directory status --- */
77 /* --- Maybe create it if it doesn't exist --- */
79 if (errno
!= ENOENT
|| !f
|| !*f
)
86 /* --- Now read the new status --- *
88 * This fixes a race condition between the previous @lstat@ call and
96 /* --- Make sure the directory is good --- *
98 * It must be a genuine directory (not a link); it must be readable
99 * and writable only by its owner, and that owner must be me.
102 if (S_ISDIR(st
.st_mode
) && (st
.st_mode
& 0777) == 0700 && st
.st_uid
== me
)
107 /* --- @trytmp@ --- *
109 * Arguments: @const char *parent@ = parent directory name
110 * @const char *base@ = subdirectory base name
112 * Returns: Pointer to directory name, or null. (String needs freeing.)
114 * Use: Tries to find or create an appropriate temporary directory.
117 static char *trytmp(const char *parent
, const char *base
)
119 static char c
[] = { "0123456789"
120 "abcdefghijklmnopqrstuvwxyz"
121 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" };
127 /* --- Make sure the parent directory is sane --- *
129 * Must make sure that all the lead-up to the temporary directory is safe.
130 * Otherwise other people can play with symlinks or rename directories
131 * after I've done all this careful work to make the endpoint directory
135 if (checkpath(parent
, &cp
))
138 /* --- See whether the trivial version will work --- */
140 dstr_putf(&d
, "%s/%s", parent
, base
);
141 if (ok(d
.buf
, &createflag
))
144 /* --- Now try with various suffixes --- */
150 for (p
= c
; *p
; p
++) {
153 if (ok(d
.buf
, &createflag
))
155 for (q
= c
; *q
; q
++) {
158 if (ok(d
.buf
, &createflag
))
174 /* --- @fullcheck@ --- *
176 * Arguments: @const char *p@ = pointer to path to check
178 * Returns: Zero if it's a bad directory.
180 * Use: Runs a thorough check on a directory.
183 static int fullcheck(const char *p
)
185 return (checkpath(p
, &cp
) == 0 && ok(p
, 0));
188 /* --- @goodtmp@ --- *
192 * Returns: Pointer to a known-good secure temporary directory, or
195 * Use: Finds a good place to store temporary files.
198 static char *goodtmp(void)
202 /* --- First of all, try the user's current choice --- */
204 if ((p
= getenv("TMPDIR")) != 0 && fullcheck(p
))
207 /* --- Try making a directory in `/tmp' --- */
209 if (!(q
= getenv("USER")) && !(q
= getenv("LOGNAME")))
211 if ((q
= trytmp("/tmp", q
)) != 0)
214 /* --- That failed: try a directory in the user's home --- */
216 if (!(q
= getenv("HOME")))
218 if ((q
= trytmp(q
, "tmp")) != 0)
221 /* --- Still no joy: give up --- *
223 * To be fair, if the user can't find a safe place in his own home
224 * directory then he's pretty stuffed.
230 /* --- @report@ --- */
232 static void report(unsigned what
, int verbose
,
233 const char *p
, const char *msg
,
237 /* --- @usage@ --- */
239 static void usage(FILE *fp
)
240 { fprintf(fp
, "Usage: %s [-bc] [-v PATH]\n", QUIS
); }
242 /* --- @version@ --- */
244 static void version(FILE *fp
)
245 { fprintf(fp
, "%s version %s\n", QUIS
, VERSION
); }
249 static void help(FILE *fp
)
255 Sets a suitable and secure temporary directory, or checks that a given\n\
256 directory is suitable for use with temporary files. A directory is\n\
257 considered good for a particular user if it's readable and writable only\n\
258 by that user, and if all its parents are modifiable only by the user or\n\
261 Options supported:\n\
263 -h, --help Display this help text.\n\
264 -V, --version Display the program's version number.\n\
265 -u, --usage Display a terse usage summary.\n\
267 -b, --bourne Output a `TMPDIR' setting for Bourne shell users.\n\
268 -c, --cshell Output a `TMPDIR' setting for C shell users.\n\
269 -v, --verbose Report problems to standard error.\n\
270 -g, --group NAME Trust group NAME to be honest and true.\n\
271 -C, --check PATH Check whether PATH is good, setting exit status.\n\
273 The default action is to examine the caller's shell and output a suitable\n\
274 setting for that shell type.\n\
279 /* --- @allowgroup@ --- *
281 * Arguments: @const char *gname@ = trust group @gname@
285 * Use: Adds the gid corresponding to @gname@ (which may be a number)
286 * to the list of things we trust.
289 static void allowgroup(const char *gname
)
295 /* --- Check for numeric group spec --- */
297 for (p
= gname
; *p
; p
++) {
298 if (!isdigit((unsigned char)*p
))
304 /* --- Look up a group by name --- */
307 if ((gr
= getgrnam(gname
)) == 0)
308 die(1, "group %s not found", gname
);
311 /* --- Insert the group into the table --- */
314 if (cp
.cp_gids
>= N(cp
.cp_gid
))
315 die(1, "too many groups");
316 cp
.cp_gid
[cp
.cp_gids
++] = g
;
321 * Arguments: @int argc@ = number of command line arguments
322 * @char *argv[]@ = the actual argument strings
324 * Returns: Zero if all went well, else nonzero.
326 * Use: Performs helpful operations on temporary directories.
329 int main(int argc
, char *argv
[])
341 /* --- Initialize variables --- */
345 cp
.cp_what
= (CP_WRWORLD
| CP_WROTHGRP
| CP_WROTHUSR
|
346 CP_STICKYOK
| CP_REPORT
);
348 cp
.cp_report
= report
;
349 checkpath_setids(&cp
);
350 cp
.cp_gids
= 0; /* ignore group membership */
353 die(1, "you don't exist");
355 /* --- Parse arguments --- */
358 static struct option opts
[] = {
359 { "help", 0, 0, 'h' },
360 { "version", 0, 0, 'V' },
361 { "usage", 0, 0, 'u' },
362 { "bourne", 0, 0, 'b' },
363 { "cshell", 0, 0, 'c' },
364 { "check", OPTF_ARGREQ
, 0, 'C' },
365 { "verify", OPTF_ARGREQ
, 0, 'C' },
366 { "verbose", 0, 0, 'v' },
367 { "trust-groups", 0, 0, 't' },
368 { "group", OPTF_ARGREQ
, 0, 'g' },
371 int i
= mdwopt(argc
, argv
, "hVu bcvtg:c:", opts
, 0, 0, 0);
392 return (!fullcheck(optarg
));
406 if (duff
|| optind
!= argc
) {
411 /* --- Choose a shell --- */
414 if (!(p
= getenv("SHELL")))
416 if (strstr(p
, "csh"))
422 /* --- Start the checking --- */
424 if ((p
= goodtmp()) == 0)
425 die(1, "no good tmp directory");
428 printf("TMPDIR=\"%s\"; export TMPDIR\n", p
);
431 printf("setenv TMPDIR \"%s\"\n", p
);
438 /*----- That's all, folks -------------------------------------------------*/