* Bug #15213, #18635: The use of the insecure m_scratch() and
[mmh] / sbr / m_mktemp.c
1 /*
2  * m_mktemp.c -- Construct a temporary file.
3  *
4  * $Id$
5  *
6  * This code is Copyright (c) 2010, by the authors of nmh.  See the
7  * COPYRIGHT file in the root directory of the nmh distribution for
8  * complete copyright information.
9  */
10
11 #include <errno.h>
12 #include <h/mh.h>
13
14 static char *get_temp_dir();
15
16 /*  Create a temporary file.  If pfx_in is null, the temporary file
17  *  will be created in the temporary directory (more on that later).
18  *  If pfx_in is not null, then the temporary file location will be
19  *  defined by the value pfx_in.
20  *
21  *  The file created will be at the pathname specified appended with
22  *  6 random (we hope :) characters.
23  *
24  *  The return value will be the pathname to the file created.
25  *
26  *  CAUTION: The return pointer references static data.  If
27  *  you need to modify, or save, the return string, make a copy of it
28  *  first.
29  *
30  *  When pfx_in is null, the temporary directory is determined as
31  *  follows, in order:
32  *  
33  *    MHTMPDIR envvar
34  *    TMPDIR envvar
35  *    TMP envvar
36  *    User's mail directory.
37  *
38  *  NOTE: One will probably use m_mktemp2() instead of this function.
39  *  For example, if you want to create a temp file in the defined
40  *  temporary directory, but with a custom basename prefix, do
41  *  something like the following:
42  *
43  *    char *tmp_pathname = m_mktemp2(NULL, "mypre", ...);
44  */
45 char *
46 m_mktemp (
47     const char *pfx_in,   /* Pathname prefix for temporary file. */
48     int *fd_ret,          /* (return,optional) File descriptor to temp file. */
49     FILE **fp_ret         /* (return,optional) FILE pointer to temp file. */
50 )
51 {
52     static char tmpfil[BUFSIZ];
53     int fd = -1;
54     int keep_open = 0;
55     mode_t oldmode = umask(077);
56
57     if (pfx_in == NULL) {
58         snprintf(tmpfil, sizeof(tmpfil), "%s/nmhXXXXXX", get_temp_dir());
59     } else {
60         snprintf(tmpfil, sizeof(tmpfil), "%sXXXXXX", pfx_in);
61     }
62
63     fd = mkstemp(tmpfil);
64     if (fd < 0) {
65         umask(oldmode);
66         return NULL;
67     }
68     if (fd_ret != NULL) {
69         *fd_ret = fd;
70         keep_open = 1;
71     }
72     if (fp_ret != NULL) {
73         FILE *fp = fdopen(fd, "w+");
74         if (fp == NULL) {
75             int olderr = errno;
76             unlink(tmpfil);
77             close(fd);
78             errno = olderr;
79             umask(oldmode);
80             return NULL;
81         }
82         *fp_ret = fp;
83         keep_open = 1;
84     }
85     if (!keep_open) {
86         close(fd);
87     }
88     umask(oldmode);
89     return tmpfil;
90 }
91
92 /* This version allows one to specify the directory the temp file should
93  * by created based on a given pathname.  Although m_mktemp() technically
94  * supports this, this version is when the directory is defined by
95  * a separate variable from the prefix, eliminating the caller from having
96  * to do string manipulation to generate the desired. pathname prefix.
97  *
98  * The pfx_in parameter specifies a basename prefix for the file.  If dir_in
99  * is NULL, then the defined temporary directory (see comments to m_mktemp()
100  * above) is used to create the temp file.
101  */
102 char *
103 m_mktemp2 (
104     const char *dir_in,   /* Directory to place temp file. */
105     const char *pfx_in,   /* Basename prefix for temp file. */
106     int *fd_ret,          /* (return,optional) File descriptor to temp file. */
107     FILE **fp_ret         /* (return,optional) FILE pointer to temp file. */
108 )
109 {
110     static char buffer[BUFSIZ];
111     char *cp;
112
113     if (dir_in == NULL) {
114         if (pfx_in == NULL) {
115             return m_mktemp(NULL, fd_ret, fp_ret);
116         }
117         snprintf(buffer, sizeof(buffer), "%s/%s", get_temp_dir(), pfx_in);
118         return m_mktemp(buffer, fd_ret, fp_ret);
119     }
120
121     if ((cp = r1bindex ((char *)dir_in, '/')) == dir_in) {
122         /* No directory component */
123         return m_mktemp(pfx_in, fd_ret, fp_ret);
124     }
125     int n = (int)(cp-dir_in-1); /* Length of dir component */
126     snprintf(buffer, sizeof(buffer), "%.*s%s", n, dir_in, pfx_in);
127     return m_mktemp(buffer, fd_ret, fp_ret);
128 }
129
130
131 static char *
132 get_temp_dir()
133 {
134     // Ignore envvars if we are setuid
135     if ((getuid()==geteuid()) && (getgid()==getegid())) {
136         char *tmpdir = NULL;
137         tmpdir = getenv("MHTMPDIR");
138         if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
139
140         tmpdir = getenv("TMPDIR");
141         if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
142
143         tmpdir = getenv("TMP");
144         if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
145     }
146     return m_maildir("");
147 }