summaryrefslogtreecommitdiffstats
path: root/src/lib/safe-mkstemp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/safe-mkstemp.c')
-rw-r--r--src/lib/safe-mkstemp.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/lib/safe-mkstemp.c b/src/lib/safe-mkstemp.c
new file mode 100644
index 0000000..27b401e
--- /dev/null
+++ b/src/lib/safe-mkstemp.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "randgen.h"
+#include "hostpid.h"
+#include "eacces-error.h"
+#include "safe-mkstemp.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static int ATTR_NULL(5)
+safe_mkstemp_full(string_t *prefix, mode_t mode, uid_t uid, gid_t gid,
+ const char *gid_origin)
+{
+ size_t prefix_len;
+ struct stat st;
+ unsigned char randbuf[8];
+ mode_t old_umask;
+ int fd;
+
+ prefix_len = str_len(prefix);
+ for (;;) {
+ do {
+ random_fill(randbuf, sizeof(randbuf));
+ str_truncate(prefix, prefix_len);
+ str_append(prefix,
+ binary_to_hex(randbuf, sizeof(randbuf)));
+ } while (lstat(str_c(prefix), &st) == 0);
+
+ if (errno != ENOENT) {
+ i_error("stat(%s) failed: %m", str_c(prefix));
+ str_truncate(prefix, prefix_len);
+ return -1;
+ }
+
+ old_umask = umask(0666 ^ mode);
+ fd = open(str_c(prefix), O_RDWR | O_EXCL | O_CREAT, 0666);
+ umask(old_umask);
+ if (fd != -1)
+ break;
+
+ if (errno != EEXIST) {
+ if (errno != ENOENT && errno != EACCES)
+ i_error("open(%s) failed: %m", str_c(prefix));
+ str_truncate(prefix, prefix_len);
+ return -1;
+ }
+ }
+ if (uid == (uid_t)-1 && gid == (gid_t)-1)
+ return fd;
+
+ if (fchown(fd, uid, gid) < 0) {
+ if (errno == EPERM) {
+ i_error("%s", eperm_error_get_chgrp("fchown",
+ str_c(prefix), gid, gid_origin));
+ } else {
+ i_error("fchown(%s, %ld, %ld) failed: %m",
+ str_c(prefix),
+ uid == (uid_t)-1 ? -1L : (long)uid,
+ gid == (gid_t)-1 ? -1L : (long)gid);
+ }
+ i_close_fd(&fd);
+ i_unlink(str_c(prefix));
+ str_truncate(prefix, prefix_len);
+ return -1;
+ }
+ return fd;
+}
+
+int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
+{
+ return safe_mkstemp_full(prefix, mode, uid, gid, NULL);
+}
+
+int safe_mkstemp_group(string_t *prefix, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ return safe_mkstemp_full(prefix, mode, (uid_t)-1, gid, gid_origin);
+}
+
+int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
+{
+ size_t orig_prefix_len = str_len(prefix);
+ int fd;
+
+ str_printfa(prefix, "%s.%s.", my_hostname, my_pid);
+ if ((fd = safe_mkstemp(prefix, mode, uid, gid)) == -1)
+ str_truncate(prefix, orig_prefix_len);
+ return fd;
+}
+
+int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ size_t orig_prefix_len = str_len(prefix);
+ int fd;
+
+ str_printfa(prefix, "%s.%s.", my_hostname, my_pid);
+ if ((fd = safe_mkstemp_group(prefix, mode, gid, gid_origin)) == -1)
+ str_truncate(prefix, orig_prefix_len);
+ return fd;
+}