Make flags be unsigned.
[checkpath] / tmpdir.c
CommitLineData
efa7a97b 1/* -*-c-*-
2 *
72462155 3 * $Id: tmpdir.c,v 1.3 1999/05/21 22:07:20 mdw Exp $
efa7a97b 4 *
5 * Choose and check temporary directories
6 *
7 * (c) 1999 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of chkpath.
13 *
14 * chkpath is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * chkpath is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with chkpath; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: tmpdir.c,v $
72462155 32 * Revision 1.3 1999/05/21 22:07:20 mdw
33 * Take advantage of new dynamic string macros.
34 *
cef4f797 35 * Revision 1.2 1999/05/19 20:37:28 mdw
36 * Track gratuitous change in mdwopt interface.
37 *
38 * Revision 1.1.1.1 1999/04/06 20:12:07 mdw
39 * Import new project.
efa7a97b 40 *
41 */
42
43/*----- Header files ------------------------------------------------------*/
44
45#include <errno.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49
50#include <sys/types.h>
51#include <sys/stat.h>
52#include <unistd.h>
53#include <pwd.h>
54
55#include <mLib/alloc.h>
56#include <mLib/dstr.h>
57#include <mLib/mdwopt.h>
58#include <mLib/quis.h>
59#include <mLib/report.h>
60
61#include "path.h"
62
63/*----- Static variables --------------------------------------------------*/
64
65static uid_t me;
66static struct chkpath cp;
67static struct passwd *pw;
68
69/*----- Main code ---------------------------------------------------------*/
70
71/* --- @ok@ --- *
72 *
73 * Arguments: @const char *p@ = pathname to check
74 * @int *f@ = try-to-create flag
75 *
76 * Returns: Nonzero if the directory is OK
77 *
78 * Use: Ensures that a directory is OK. If @f@ is a real pointer,
79 * and @*f@ is set, then try to create the directory.
80 */
81
82static int ok(const char *p, int *f)
83{
84 struct stat st;
85
86 /* --- Read the directory status --- */
87
88 if (lstat(p, &st)) {
89
90 /* --- Maybe create it if it doesn't exist --- */
91
92 if (errno != ENOENT || !f || !*f)
93 return (0);
94 if (mkdir(p, 0700)) {
95 *f = 0;
96 return (0);
97 }
98
99 /* --- Now read the new status --- *
100 *
101 * This fixes a race condition between the previous @lstat@ call and
102 * the @mkdir@.
103 */
104
105 if (lstat(p, &st))
106 return (0);
107 }
108
109 /* --- Make sure the directory is good --- *
110 *
111 * It must be a genuine directory (not a link); it must be readable
112 * and writable only by its owner, and that owner must be me.
113 */
114
115 if (S_ISDIR(st.st_mode) && (st.st_mode & 0777) == 0700 && st.st_uid == me)
116 return (1);
117 return (0);
118}
119
120/* --- @trytmp@ --- *
121 *
122 * Arguments: @const char *parent@ = parent directory name
123 * @const char *base@ = subdirectory base name
124 *
125 * Returns: Pointer to directory name, or null. (String needs freeing.)
126 *
127 * Use: Tries to find or create an appropriate temporary directory.
128 */
129
130static char *trytmp(const char *parent, const char *base)
131{
132 static char c[] = { "0123456789"
133 "abcdefghijklmnopqrstuvwxyz"
134 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" };
135 char *p, *q;
136 char *qq;
72462155 137 dstr d = DSTR_INIT;
efa7a97b 138 int createflag = 1;
139
140 /* --- Make sure the parent directory is sane --- *
141 *
142 * Must make sure that all the lead-up to the temporary directory is safe.
143 * Otherwise other people can play with symlinks or rename directories
144 * after I've done all this careful work to make the endpoint directory
145 * safe.
146 */
147
148 if (path_check(parent, &cp))
149 return (0);
150
151 /* --- See whether the trivial version will work --- */
152
efa7a97b 153 dstr_putf(&d, "%s/%s", parent, base);
154 if (ok(d.buf, &createflag))
155 goto good;
156
157 /* --- Now try with various suffixes --- */
158
159 DENSURE(&d, 4);
160 qq = d.buf + d.len;
161 *qq++ = '-';
162
163 for (p = c; *p; p++) {
164 qq[0] = *p;
165 qq[1] = 0;
166 if (ok(d.buf, &createflag))
167 goto good;
168 for (q = c; *q; q++) {
169 qq[1] = *q;
170 qq[2] = 0;
171 if (ok(d.buf, &createflag))
172 goto good;
173 }
174 }
175
176 /* --- No joy --- */
177
178 dstr_destroy(&d);
179 return (0);
180
181good:
182 p = xstrdup(d.buf);
183 dstr_destroy(&d);
184 return (p);
185}
186
187/* --- @fullcheck@ --- *
188 *
189 * Arguments: @const char *p@ = pointer to path to check
190 *
191 * Returns: Zero if it's a bad directory.
192 *
193 * Use: Runs a thorough check on a directory.
194 */
195
196static int fullcheck(const char *p)
197{
198 return (path_check(p, &cp) == 0 && ok(p, 0));
199}
200
201/* --- @goodtmp@ --- *
202 *
203 * Arguments: ---
204 *
205 * Returns: Pointer to a known-good secure temporary directory, or
206 * null.
207 *
208 * Use: Finds a good place to store temporary files.
209 */
210
211static char *goodtmp(void)
212{
213 char *p, *q;
214
215 /* --- First of all, try the user's current choice --- */
216
217 if ((p = getenv("TMPDIR")) != 0 && fullcheck(p))
218 return (p);
219
220 /* --- Try making a directory in `/tmp' --- */
221
222 if (!(q = getenv("USER")) && !(q = getenv("LOGNAME")))
223 q = pw->pw_name;
224 if ((q = trytmp("/tmp", q)) != 0)
225 return (q);
226
227 /* --- That failed: try a directory in the user's home --- */
228
229 if (!(q = getenv("HOME")))
230 q = pw->pw_dir;
231 if ((q = trytmp(q, "tmp")) != 0)
232 return (q);
233
234 /* --- Still no joy: give up --- *
235 *
236 * To be fair, if the user can't find a safe place in his own home
237 * directory then he's pretty stuffed.
238 */
239
240 return (0);
241}
242
243/* --- @usage@ --- */
244
245static void usage(FILE *fp)
246{
247 fprintf(fp, "Usage: %s [-bc] [-v PATH]\n", QUIS);
248}
249
250/* --- @version@ --- */
251
252static void version(FILE *fp)
253{
254 fprintf(fp, "%s version %s\n", QUIS, VERSION);
255}
256
257/* --- @help@ --- */
258
259static void help(FILE *fp)
260{
261 version(fp);
262 putc('\n', fp);
263 usage(fp);
264 fputs("\n\
265Sets a suitable and secure temporary directory, or checks that a given\n\
266directory is suitable for use with temporary files. A directory is\n\
267considered good for a particular user if it's readable and writable only\n\
268by that user, and if all its parents are modifiable only by the user or\n\
269root.\n\
270\n\
271Options supported:\n\
272\n\
273-h, --help Display this help text.\n\
274-V, --version Display the program's version number.\n\
275-u, --usage Display a terse usage summary.\n\
276\n\
277-b, --bourne Output a `TMPDIR' setting for Bourne shell users.\n\
278-c, --cshell Output a `TMPDIR' setting for C shell users.\n\
279-v, --verify PATH Check whether PATH is good, setting exit status.\n\
280\n\
281The default action is to examine the caller's shell and output a suitable\n\
282setting for that shell type.\n\
283",
284 fp);
285}
286
287/* --- @main@ --- *
288 *
289 * Arguments: @int argc@ = number of command line arguments
290 * @char *argv[]@ = the actual argument strings
291 *
292 * Returns: Zero if all went well, else nonzero.
293 *
294 * Use: Performs helpful operations on temporary directories.
295 */
296
297int main(int argc, char *argv[])
298{
299 int shell = 0;
300 int duff = 0;
301
302 enum {
303 sh_unknown,
304 sh_bourne,
305 sh_csh
306 };
307
308 /* --- Initialize variables --- */
309
310 ego(argv[0]);
311 me = getuid();
312 cp.cp_what = CP_WRWORLD | CP_WRGRP | CP_WROTHUSR | CP_STICKYOK;
313 cp.cp_verbose = 0;
314 cp.cp_report = 0;
315 path_setids(&cp);
316 pw = getpwuid(me);
317 if (!pw)
318 die(1, "you don't exist");
319
320 /* --- Parse arguments --- */
321
322 for (;;) {
323 static struct option opts[] = {
324 { "help", 0, 0, 'h' },
325 { "version", 0, 0, 'V' },
326 { "usage", 0, 0, 'u' },
327 { "bourne", 0, 0, 'b' },
328 { "cshell", 0, 0, 'c' },
cef4f797 329 { "verify", OPTF_ARGREQ, 0, 'v' },
efa7a97b 330 { 0, 0, 0, 0 }
331 };
332 int i = mdwopt(argc, argv, "hVu bcv:", opts, 0, 0, 0);
333
334 if (i < 0)
335 break;
336 switch (i) {
337 case 'h':
338 help(stdout);
339 exit(0);
340 case 'V':
341 version(stdout);
342 exit(0);
343 case 'u':
344 usage(stdout);
345 exit(0);
346 case 'b':
347 shell = sh_bourne;
348 break;
349 case 'c':
350 shell = sh_csh;
351 break;
352 case 'v':
353 return (!fullcheck(optarg));
354 break;
355 default:
356 duff = 1;
357 break;
358 }
359 }
360
361 if (duff || optind != argc) {
362 usage(stderr);
363 exit(1);
364 }
365
366 /* --- Choose a shell --- */
367
368 if (!shell) {
369 char *p;
370 if (!(p = getenv("SHELL")))
371 p = pw->pw_shell;
372 if (strstr(p, "csh"))
373 shell = sh_csh;
374 else
375 shell = sh_bourne;
376 }
377
378 /* --- Start the checking --- */
379
380 {
381 char *p = goodtmp();
382 if (!p)
383 die(1, "no good tmp directory");
384 switch (shell) {
385 case sh_bourne:
386 printf("TMPDIR=\"%s\"; export TMPDIR\n", p);
387 break;
388 case sh_csh:
389 printf("setenv TMPDIR \"%s\"\n", p);
390 break;
391 }
392 }
393
394 return (0);
395}
396
397/*----- That's all, folks -------------------------------------------------*/