1 /* $OpenBSD: mktemp.c,v 1.38 2015/09/13 08:31:47 guenther Exp $ */ 2 /* 3 * Copyright (c) 1996-1998, 2008 Theo de Raadt 4 * Copyright (c) 1997, 2008-2009 Todd C. Miller 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <ctype.h> 28 #include <unistd.h> 29 30 #define MKTEMP_NAME 0 31 #define MKTEMP_FILE 1 32 #define MKTEMP_DIR 2 33 34 #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 35 #define NUM_CHARS (sizeof(TEMPCHARS) - 1) 36 #define MIN_X 6 37 38 #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC) 39 40 #ifndef nitems 41 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 42 #endif 43 44 static int 45 mktemp_internal(char *path, int slen, int mode, int flags) 46 { 47 char *start, *cp, *ep; 48 const char tempchars[] = TEMPCHARS; 49 unsigned int tries; 50 struct stat sb; 51 size_t len; 52 int fd; 53 54 len = strlen(path); 55 if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) { 56 errno = EINVAL; 57 return(-1); 58 } 59 ep = path + len - slen; 60 61 for (start = ep; start > path && start[-1] == 'X'; start--) 62 ; 63 if (ep - start < MIN_X) { 64 errno = EINVAL; 65 return(-1); 66 } 67 68 if (flags & ~MKOTEMP_FLAGS) { 69 errno = EINVAL; 70 return(-1); 71 } 72 flags |= O_CREAT | O_EXCL | O_RDWR; 73 74 tries = INT_MAX; 75 do { 76 cp = start; 77 do { 78 unsigned short rbuf[16]; 79 unsigned int i; 80 81 /* 82 * Avoid lots of arc4random() calls by using 83 * a buffer sized for up to 16 Xs at a time. 84 */ 85 arc4random_buf(rbuf, sizeof(rbuf)); 86 for (i = 0; i < nitems(rbuf) && cp != ep; i++) 87 *cp++ = tempchars[rbuf[i] % NUM_CHARS]; 88 } while (cp != ep); 89 90 switch (mode) { 91 case MKTEMP_NAME: 92 if (lstat(path, &sb) != 0) 93 return(errno == ENOENT ? 0 : -1); 94 break; 95 case MKTEMP_FILE: 96 fd = open(path, flags, S_IRUSR|S_IWUSR); 97 if (fd != -1 || errno != EEXIST) 98 return(fd); 99 break; 100 case MKTEMP_DIR: 101 if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0) 102 return(0); 103 if (errno != EEXIST) 104 return(-1); 105 break; 106 } 107 } while (--tries); 108 109 errno = EEXIST; 110 return(-1); 111 } 112 113 char * 114 _mktemp(char *path) 115 { 116 if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1) 117 return(NULL); 118 return(path); 119 } 120 121 __warn_references(mktemp, 122 "warning: mktemp() possibly used unsafely; consider using mkstemp()"); 123 124 char * 125 mktemp(char *path) 126 { 127 return(_mktemp(path)); 128 } 129 130 int 131 mkostemps(char *path, int slen, int flags) 132 { 133 return(mktemp_internal(path, slen, MKTEMP_FILE, flags)); 134 } 135 136 int 137 mkstemp(char *path) 138 { 139 return(mktemp_internal(path, 0, MKTEMP_FILE, 0)); 140 } 141 DEF_WEAK(mkstemp); 142 143 int 144 mkostemp(char *path, int flags) 145 { 146 return(mktemp_internal(path, 0, MKTEMP_FILE, flags)); 147 } 148 DEF_WEAK(mkostemp); 149 150 int 151 mkstemps(char *path, int slen) 152 { 153 return(mktemp_internal(path, slen, MKTEMP_FILE, 0)); 154 } 155 156 char * 157 mkdtemp(char *path) 158 { 159 int error; 160 161 error = mktemp_internal(path, 0, MKTEMP_DIR, 0); 162 return(error ? NULL : path); 163 } 164