0f1044f20337f0c6019c00d422c1604d9cbb57e3
[debian/sudo] / plugins / sudoers / iolog_path.c
1 /*
2  * Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
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.
7  *
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.
15  */
16
17 #include <config.h>
18
19 #include <sys/types.h>
20 #include <stdio.h>
21 #ifdef STDC_HEADERS
22 # include <stdlib.h>
23 # include <stddef.h>
24 #else
25 # ifdef HAVE_STDLIB_H
26 #  include <stdlib.h>
27 # endif
28 #endif /* STDC_HEADERS */
29 #ifdef HAVE_STRING_H
30 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
31 #  include <memory.h>
32 # endif
33 # include <string.h>
34 #endif /* HAVE_STRING_H */
35 #ifdef HAVE_STRINGS_H
36 # include <strings.h>
37 #endif /* HAVE_STRINGS_H */
38 #ifdef HAVE_SETLOCALE
39 # include <locale.h>
40 #endif
41 #include <pwd.h>
42 #include <grp.h>
43 #include <time.h>
44
45 #include "sudoers.h"
46
47 struct path_escape {
48     const char *name;
49     size_t (*copy_fn)(char *, size_t);
50 };
51
52 static size_t fill_seq(char *, size_t);
53 static size_t fill_user(char *, size_t);
54 static size_t fill_group(char *, size_t);
55 static size_t fill_runas_user(char *, size_t);
56 static size_t fill_runas_group(char *, size_t);
57 static size_t fill_hostname(char *, size_t);
58 static size_t fill_command(char *, size_t);
59
60 static struct path_escape escapes[] = {
61     { "seq", fill_seq },
62     { "user", fill_user },
63     { "group", fill_group },
64     { "runas_user", fill_runas_user },
65     { "runas_group", fill_runas_group },
66     { "hostname", fill_hostname },
67     { "command", fill_command },
68     { NULL, NULL }
69 };
70
71 static size_t
72 fill_seq(char *str, size_t strsize)
73 {
74     static char sessid[7];
75     int len;
76
77     if (sessid[0] == '\0')
78         io_nextid(def_iolog_dir, sessid);
79
80     /* Path is of the form /var/log/sudo-io/00/00/01. */
81     len = snprintf(str, strsize, "%c%c/%c%c/%c%c", sessid[0],
82         sessid[1], sessid[2], sessid[3], sessid[4], sessid[5]);
83     if (len < 0)
84         return strsize; /* handle non-standard snprintf() */
85     return (size_t)len;
86 }
87
88 static size_t
89 fill_user(char *str, size_t strsize)
90 {
91     return strlcpy(str, user_name, strsize);
92 }
93
94 static size_t
95 fill_group(char *str, size_t strsize)
96 {
97     struct group *grp;
98     size_t len;
99
100     if ((grp = sudo_getgrgid(user_gid)) != NULL) {
101         len = strlcpy(str, grp->gr_name, strsize);
102         gr_delref(grp);
103     } else {
104         len = strlen(str);
105         len = snprintf(str + len, strsize - len, "#%u",
106             (unsigned int) user_gid);
107     }
108     return len;
109 }
110
111 static size_t
112 fill_runas_user(char *str, size_t strsize)
113 {
114     return strlcpy(str, runas_pw->pw_name, strsize);
115 }
116
117 static size_t
118 fill_runas_group(char *str, size_t strsize)
119 {
120     struct group *grp;
121     size_t len;
122
123     if (runas_gr != NULL) {
124         len = strlcpy(str, runas_gr->gr_name, strsize);
125     } else {
126         if ((grp = sudo_getgrgid(runas_pw->pw_gid)) != NULL) {
127             len = strlcpy(str, grp->gr_name, strsize);
128             gr_delref(grp);
129         } else {
130             len = strlen(str);
131             len = snprintf(str + len, strsize - len, "#%u",
132                 (unsigned int) runas_pw->pw_gid);
133         }
134     }
135     return len;
136 }
137
138 static size_t
139 fill_hostname(char *str, size_t strsize)
140 {
141     return strlcpy(str, user_shost, strsize);
142 }
143
144 static size_t
145 fill_command(char *str, size_t strsize)
146 {
147     return strlcpy(str, user_base, strsize);
148 }
149
150 /*
151  * Concatenate dir + file, expanding any escape sequences.
152  * Returns the concatenated path and sets slashp point to
153  * the path separator between the expanded dir and file.
154  */
155 char *
156 expand_iolog_path(const char *prefix, const char *dir, const char *file,
157     char **slashp)
158 {
159     size_t len, prelen = 0;
160     char *dst, *dst0, *path, *pathend, tmpbuf[PATH_MAX];
161     const char *endbrace, *src = dir;
162     int pass, strfit;
163
164     /* Expanded path must be <= PATH_MAX */
165     if (prefix != NULL)
166         prelen = strlen(prefix);
167     dst = path = emalloc(prelen + PATH_MAX);
168     *path = '\0';
169     pathend = path + prelen + PATH_MAX;
170
171     /* Copy prefix, if present. */
172     if (prefix != NULL) {
173         memcpy(path, prefix, prelen);
174         dst += prelen;
175         *dst = '\0';
176     }
177
178     /* Trim leading slashes from file component. */
179     while (*file == '/')
180         file++;
181
182     for (pass = 0; pass < 3; pass++) {
183         strfit = FALSE;
184         switch (pass) {
185         case 0:
186             src = dir;
187             break;
188         case 1:
189             /* Trim trailing slashes from dir component. */
190             while (dst - path - 1 > prelen && dst[-1] == '/')
191                 dst--;
192             if (slashp)
193                 *slashp = dst;
194             src = "/";
195             break;
196         case 2:
197             src = file;
198             break;
199         }
200         dst0 = dst;
201         for (; *src != '\0'; src++) {
202             if (src[0] == '%') {
203                 if (src[1] == '{') {
204                     endbrace = strchr(src + 2, '}');
205                     if (endbrace != NULL) {
206                         struct path_escape *esc;
207                         len = (size_t)(endbrace - src - 2);
208                         for (esc = escapes; esc->name != NULL; esc++) {
209                             if (strncmp(src + 2, esc->name, len) == 0 &&
210                                 esc->name[len] == '\0')
211                                 break;
212                         }
213                         if (esc->name != NULL) {
214                             len = esc->copy_fn(dst, (size_t)(pathend - dst));
215                             if (len >= (size_t)(pathend - dst))
216                                 goto bad;
217                             dst += len;
218                             src = endbrace;
219                             continue;
220                         }
221                     }
222                 } else if (src[1] == '%') {
223                     /* Collapse %% -> % */
224                     src++;
225                 } else {
226                     /* May need strftime() */
227                     strfit = 1;
228                 }
229             }
230             /* Need at least 2 chars, including the NUL terminator. */
231             if (dst + 1 >= pathend)
232                 goto bad;
233             *dst++ = *src;
234         }
235         *dst = '\0';
236
237         /* Expand strftime escapes as needed. */
238         if (strfit) {
239             time_t now;
240             struct tm *timeptr;
241
242             time(&now);
243             timeptr = localtime(&now);
244
245 #ifdef HAVE_SETLOCALE
246             if (!setlocale(LC_ALL, def_sudoers_locale)) {
247                 warningx(_("unable to set locale to \"%s\", using \"C\""),
248                     def_sudoers_locale);
249                 setlocale(LC_ALL, "C");
250             }
251 #endif
252             /* We only calls strftime() on the current part of the buffer. */
253             tmpbuf[sizeof(tmpbuf) - 1] = '\0';
254             len = strftime(tmpbuf, sizeof(tmpbuf), dst0, timeptr);
255
256 #ifdef HAVE_SETLOCALE
257             setlocale(LC_ALL, "");
258 #endif
259             if (len == 0 || tmpbuf[sizeof(tmpbuf) - 1] != '\0')
260                 goto bad;               /* strftime() failed, buf too small? */
261
262             if (len >= (size_t)(pathend - dst0))
263                 goto bad;               /* expanded buffer too big to fit. */
264             memcpy(dst0, tmpbuf, len);
265             dst = dst0 + len;
266             *dst = '\0';
267         }
268     }
269
270     return path;
271 bad:
272     efree(path);
273     return NULL;
274 }