1/* $OpenBSD: mktemp.c,v 1.35 2014/10/31 15:54:14 millert 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 44static int 45mktemp_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 113char *_mktemp(char *); 114 115char * 116_mktemp(char *path) 117{ 118 if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1) 119 return(NULL); 120 return(path); 121} 122 123__warn_references(mktemp, 124 "warning: mktemp() possibly used unsafely; consider using mkstemp()"); 125 126char * 127mktemp(char *path) 128{ 129 return(_mktemp(path)); 130} 131 132int 133mkostemps(char *path, int slen, int flags) 134{ 135 return(mktemp_internal(path, slen, MKTEMP_FILE, flags)); 136} 137 138int 139mkstemp(char *path) 140{ 141 return(mktemp_internal(path, 0, MKTEMP_FILE, 0)); 142} 143 144int 145mkostemp(char *path, int flags) 146{ 147 return(mktemp_internal(path, 0, MKTEMP_FILE, flags)); 148} 149 150int 151mkstemps(char *path, int slen) 152{ 153 return(mktemp_internal(path, slen, MKTEMP_FILE, 0)); 154} 155 156char * 157mkdtemp(char *path) 158{ 159 int error; 160 161 error = mktemp_internal(path, 0, MKTEMP_DIR, 0); 162 return(error ? NULL : path); 163} 164