2 * Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
28 #endif /* STDC_HEADERS */
30 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
34 #endif /* HAVE_STRING_H */
37 #endif /* HAVE_STRINGS_H */
49 size_t (*copy_fn)(char *, size_t, char *);
52 static size_t fill_seq(char *, size_t, char *);
53 static size_t fill_user(char *, size_t, char *);
54 static size_t fill_group(char *, size_t, char *);
55 static size_t fill_runas_user(char *, size_t, char *);
56 static size_t fill_runas_group(char *, size_t, char *);
57 static size_t fill_hostname(char *, size_t, char *);
58 static size_t fill_command(char *, size_t, char *);
60 /* Note: "seq" must be first in the list. */
61 static struct path_escape io_path_escapes[] = {
63 { "user", fill_user },
64 { "group", fill_group },
65 { "runas_user", fill_runas_user },
66 { "runas_group", fill_runas_group },
67 { "hostname", fill_hostname },
68 { "command", fill_command },
73 fill_seq(char *str, size_t strsize, char *logdir)
75 static char sessid[7];
77 debug_decl(sudoers_io_version, SUDO_DEBUG_UTIL)
79 if (sessid[0] == '\0')
80 io_nextid(logdir, def_iolog_dir, sessid);
82 /* Path is of the form /var/log/sudo-io/00/00/01. */
83 len = snprintf(str, strsize, "%c%c/%c%c/%c%c", sessid[0],
84 sessid[1], sessid[2], sessid[3], sessid[4], sessid[5]);
86 debug_return_size_t(strsize); /* handle non-standard snprintf() */
87 debug_return_size_t(len);
91 fill_user(char *str, size_t strsize, char *unused)
93 debug_decl(fill_user, SUDO_DEBUG_UTIL)
94 debug_return_size_t(strlcpy(str, user_name, strsize));
98 fill_group(char *str, size_t strsize, char *unused)
102 debug_decl(fill_group, SUDO_DEBUG_UTIL)
104 if ((grp = sudo_getgrgid(user_gid)) != NULL) {
105 len = strlcpy(str, grp->gr_name, strsize);
109 len = snprintf(str + len, strsize - len, "#%u",
110 (unsigned int) user_gid);
112 debug_return_size_t(len);
116 fill_runas_user(char *str, size_t strsize, char *unused)
118 debug_decl(fill_runas_user, SUDO_DEBUG_UTIL)
119 debug_return_size_t(strlcpy(str, runas_pw->pw_name, strsize));
123 fill_runas_group(char *str, size_t strsize, char *unused)
127 debug_decl(fill_runas_group, SUDO_DEBUG_UTIL)
129 if (runas_gr != NULL) {
130 len = strlcpy(str, runas_gr->gr_name, strsize);
132 if ((grp = sudo_getgrgid(runas_pw->pw_gid)) != NULL) {
133 len = strlcpy(str, grp->gr_name, strsize);
137 len = snprintf(str + len, strsize - len, "#%u",
138 (unsigned int) runas_pw->pw_gid);
141 debug_return_size_t(len);
145 fill_hostname(char *str, size_t strsize, char *unused)
147 debug_decl(fill_hostname, SUDO_DEBUG_UTIL)
148 debug_return_size_t(strlcpy(str, user_shost, strsize));
152 fill_command(char *str, size_t strsize, char *unused)
154 debug_decl(fill_command, SUDO_DEBUG_UTIL)
155 debug_return_size_t(strlcpy(str, user_base, strsize));
159 * Concatenate dir + file, expanding any escape sequences.
160 * Returns the concatenated path and sets slashp point to
161 * the path separator between the expanded dir and file.
164 expand_iolog_path(const char *prefix, const char *dir, const char *file,
167 size_t len, prelen = 0;
168 char *dst, *dst0, *path, *pathend, tmpbuf[PATH_MAX];
170 const char *endbrace, *src = dir;
171 static struct path_escape *escapes;
174 debug_decl(expand_iolog_path, SUDO_DEBUG_UTIL)
176 /* Expanded path must be <= PATH_MAX */
178 prelen = strlen(prefix);
179 dst = path = emalloc(prelen + PATH_MAX);
181 pathend = path + prelen + PATH_MAX;
183 /* Copy prefix, if present. */
184 if (prefix != NULL) {
185 memcpy(path, prefix, prelen);
190 /* Trim leading slashes from file component. */
194 for (pass = 0; pass < 3; pass++) {
199 escapes = io_path_escapes + 1; /* skip "${seq}" */
202 /* Trim trailing slashes from dir component. */
203 while (dst - path - 1 > prelen && dst[-1] == '/')
205 /* The NUL will be replaced with a '/' at the end. */
206 if (dst + 1 >= pathend)
212 escapes = io_path_escapes;
216 for (; *src != '\0'; src++) {
219 endbrace = strchr(src + 2, '}');
220 if (endbrace != NULL) {
221 struct path_escape *esc;
222 len = (size_t)(endbrace - src - 2);
223 for (esc = escapes; esc->name != NULL; esc++) {
224 if (strncmp(src + 2, esc->name, len) == 0 &&
225 esc->name[len] == '\0')
228 if (esc->name != NULL) {
229 len = esc->copy_fn(dst, (size_t)(pathend - dst),
231 if (len >= (size_t)(pathend - dst))
238 } else if (src[1] == '%') {
239 /* Collapse %% -> % */
242 /* May need strftime() */
246 /* Need at least 2 chars, including the NUL terminator. */
247 if (dst + 1 >= pathend)
253 /* Expand strftime escapes as needed. */
259 timeptr = localtime(&now);
261 #ifdef HAVE_SETLOCALE
262 if (!setlocale(LC_ALL, def_sudoers_locale)) {
263 warningx(_("unable to set locale to \"%s\", using \"C\""),
265 setlocale(LC_ALL, "C");
268 /* We only calls strftime() on the current part of the buffer. */
269 tmpbuf[sizeof(tmpbuf) - 1] = '\0';
270 len = strftime(tmpbuf, sizeof(tmpbuf), dst0, timeptr);
272 #ifdef HAVE_SETLOCALE
273 setlocale(LC_ALL, "");
275 if (len == 0 || tmpbuf[sizeof(tmpbuf) - 1] != '\0')
276 goto bad; /* strftime() failed, buf too small? */
278 if (len >= (size_t)(pathend - dst0))
279 goto bad; /* expanded buffer too big to fit. */
280 memcpy(dst0, tmpbuf, len);
289 debug_return_str(path);
292 debug_return_str(NULL);