Coverage Report

Created: 2025-10-10 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/lib/util/mktemp.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2001, 2003, 2004, 2008-2011, 2013, 2015, 2017, 2018, 2022
5
 *  Todd C. Miller <Todd.Miller@sudo.ws>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
#include <config.h>
21
22
#if (!defined(HAVE_MKDTEMPAT) && !defined(HAVE_MKDTEMPAT_NP)) || \
23
    (!defined(HAVE_MKOSTEMPSAT) && !defined(HAVE_MKOSTEMPSAT_NP))
24
25
#include <sys/stat.h>
26
27
#include <errno.h>
28
#include <fcntl.h>
29
#include <limits.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#if defined(HAVE_STDINT_H)
33
# include <stdint.h>
34
#elif defined(HAVE_INTTYPES_H)
35
# include <inttypes.h>
36
#endif
37
#include <string.h>
38
#include <ctype.h>
39
#include <unistd.h>
40
41
#include <sudo_compat.h>
42
#include <sudo_rand.h>
43
#include <pathnames.h>
44
45
3.80k
#define MKTEMP_FILE 1
46
0
#define MKTEMP_DIR  2
47
48
13.3k
#define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
49
11.4k
#define NUM_CHARS (sizeof(TEMPCHARS) - 1)
50
7.61k
#define MIN_X   6
51
52
1.90k
#define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_SYNC)
53
54
static int
55
mktemp_internal(int dfd, char *path, int slen, int mode, int flags)
56
1.90k
{
57
1.90k
  char *start, *cp, *ep;
58
1.90k
  const char tempchars[] = TEMPCHARS;
59
1.90k
  unsigned int tries;
60
1.90k
  size_t len;
61
1.90k
  int fd;
62
63
1.90k
  len = strlen(path);
64
1.90k
  if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
65
0
    errno = EINVAL;
66
0
    return -1;
67
0
  }
68
1.90k
  ep = path + len - slen;
69
70
13.3k
  for (start = ep; start > path && start[-1] == 'X'; start--)
71
11.4k
    ;
72
1.90k
  if (ep - start < MIN_X) {
73
0
    errno = EINVAL;
74
0
    return -1;
75
0
  }
76
77
1.90k
  if (flags & ~MKOTEMP_FLAGS) {
78
0
    errno = EINVAL;
79
0
    return -1;
80
0
  }
81
1.90k
  flags |= O_CREAT | O_EXCL | O_RDWR;
82
83
1.90k
  tries = INT_MAX;
84
1.90k
  do {
85
1.90k
    cp = start;
86
1.90k
    do {
87
1.90k
      unsigned short rbuf[16];
88
1.90k
      size_t i;
89
90
      /*
91
       * Avoid lots of arc4random() calls by using
92
       * a buffer sized for up to 16 Xs at a time.
93
       */
94
1.90k
      arc4random_buf(rbuf, sizeof(rbuf));
95
13.3k
      for (i = 0; i < nitems(rbuf) && cp != ep; i++)
96
11.4k
        *cp++ = tempchars[rbuf[i] % NUM_CHARS];
97
1.90k
    } while (cp != ep);
98
99
1.90k
    switch (mode) {
100
1.90k
    case MKTEMP_FILE:
101
1.90k
      fd = openat(dfd, path, flags, S_IRUSR|S_IWUSR);
102
1.90k
      if (fd != -1 || errno != EEXIST)
103
1.90k
        return fd;
104
0
      break;
105
0
    case MKTEMP_DIR:
106
0
      if (mkdirat(dfd, path, S_IRWXU) == 0)
107
0
        return 0;
108
0
      if (errno != EEXIST)
109
0
        return -1;
110
0
      break;
111
1.90k
    }
112
1.90k
  } while (--tries);
113
114
1.90k
  errno = EEXIST;
115
0
  return -1;
116
1.90k
}
117
118
char *
119
sudo_mkdtemp(char *path)
120
0
{
121
0
  if (mktemp_internal(AT_FDCWD, path, 0, MKTEMP_DIR, 0) == -1)
122
0
    return NULL;
123
0
  return path;
124
0
}
125
126
char *
127
sudo_mkdtempat(int dfd, char *path)
128
0
{
129
0
  if (mktemp_internal(dfd, path, 0, MKTEMP_DIR, 0) == -1)
130
0
    return NULL;
131
0
  return path;
132
0
}
133
134
int
135
sudo_mkostempsat(int dfd, char *path, int slen, int flags)
136
0
{
137
0
  return mktemp_internal(dfd, path, slen, MKTEMP_FILE, flags);
138
0
}
139
140
#ifdef notyet
141
int
142
sudo_mkostemps(char *path, int slen, int flags)
143
{
144
  return mktemp_internal(AT_FDCWD, path, slen, MKTEMP_FILE, flags);
145
}
146
#endif
147
148
int
149
sudo_mkstemp(char *path)
150
1.90k
{
151
1.90k
  return mktemp_internal(AT_FDCWD, path, 0, MKTEMP_FILE, 0);
152
1.90k
}
153
154
#ifdef notyet
155
int
156
sudo_mkostemp(char *path, int flags)
157
{
158
  return mktemp_internal(AT_FDCWD, path, 0, MKTEMP_FILE, flags);
159
}
160
#endif
161
162
int
163
sudo_mkstemps(char *path, int slen)
164
0
{
165
0
  return mktemp_internal(AT_FDCWD, path, slen, MKTEMP_FILE, 0);
166
0
}
167
#endif /* !HAVE_MKDTEMPAT || !HAVE_MKOSTEMPSAT */