74f5fdff175cd619a190262c06973d8a848e8b34
[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 char *
151 expand_iolog_path(const char *prefix, const char *dir, const char *file,
152     char **slashp)
153 {
154     size_t plen = 0, psize = 1024;
155     char *path, *dst;
156     const char *src = dir, *ep;
157     int pass, strfit = FALSE;
158
159     /* Concatenate dir + file -> path, expanding any escape sequences. */
160     dst = path = emalloc(psize);
161     *path = '\0';
162
163     /* Trim leading slashes from file component. */
164     while (*file == '/')
165         file++;
166
167     if (prefix != NULL) {
168         plen = strlcpy(path, prefix, psize);
169         dst += plen;
170     }
171     for (pass = 0; pass < 3; pass++) {
172         switch (pass) {
173         case 0:
174             src = dir;
175             break;
176         case 1:
177             /* Trim trailing slashes from dir component. */
178             while (dst > path && dst[-1] == '/')
179                 dst--;
180             if (slashp)
181                 *slashp = dst;
182             src = "/";
183             break;
184         case 2:
185             src = file;
186             break;
187         }
188         for (; *src != '\0'; src++) {
189             if (src[0] == '%') {
190                 if (src[1] == '{') {
191                     ep = strchr(src + 2, '}');
192                     if (ep != NULL) {
193                         struct path_escape *esc;
194                         size_t len = (size_t)(ep - src - 2);
195                         for (esc = escapes; esc->name != NULL; esc++) {
196                             if (strncmp(src + 2, esc->name, len) == 0 &&
197                                 esc->name[len] == '\0')
198                                 break;
199                         }
200                         if (esc->name != NULL) {
201                             for (;;) {
202                                 len = esc->copy_fn(dst, psize - (dst - path));
203                                 if (len < psize - (dst - path))
204                                     break;
205                                 path = erealloc3(path, 2, psize);
206                                 psize *= 2;
207                                 dst = path + plen;
208                             }
209                             dst += len;
210                             plen += len;
211                             src = ep;
212                             continue;
213                         }
214                     }
215                 } else if (src[1] == '%') {
216                     /* Collapse %% -> % */
217                     src++;
218                 } else {
219                     /* May need strftime() */
220                     strfit = 1;
221                 }
222             }
223             /* Need at least 2 chars, including the NUL terminator. */
224             if (plen + 2 >= psize) {
225                 path = erealloc3(path, 2, psize);
226                 psize *= 2;
227                 dst = path + plen;
228             }
229             *dst++ = *src;
230             plen++;
231         }
232     }
233     *dst = '\0';
234
235     if (strfit) {
236         time_t now;
237         struct tm *timeptr;
238         char *buf = NULL;
239
240         time(&now);
241         timeptr = localtime(&now);
242
243 #ifdef HAVE_SETLOCALE
244         if (!setlocale(LC_ALL, def_sudoers_locale)) {
245             warningx("unable to set locale to \"%s\", using \"C\"",
246                 def_sudoers_locale);
247             setlocale(LC_ALL, "C");
248         }
249 #endif
250         /* Double the size of the buffer until it is big enough to expand. */
251         do {
252             psize *= 2;
253             buf = erealloc(buf, psize);
254             buf[psize - 1] = '\0';
255         } while (!strftime(buf, psize, path, timeptr) || buf[psize - 1] != '\0');
256 #ifdef HAVE_SETLOCALE
257         setlocale(LC_ALL, "");
258 #endif
259         if (slashp)
260             *slashp = buf + (*slashp - path);
261         efree(path);
262         path = buf;
263     }
264
265     return path;
266 }