]> granicus.if.org Git - sudo/blob - src/hooks.c
12169805f72adc27660a439446f4495d092e563a
[sudo] / src / hooks.c
1 /*
2  * Copyright (c) 2012-2016 Todd C. Miller <Todd.Miller@sudo.ws>
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 /*
18  * This is an open source non-commercial project. Dear PVS-Studio, please check it.
19  * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
20  */
21
22 #include <config.h>
23
24 #include <sys/types.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #ifdef HAVE_STRING_H
28 # include <string.h>
29 #endif /* HAVE_STRING_H */
30 #ifdef HAVE_STRINGS_H
31 # include <strings.h>
32 #endif /* HAVE_STRINGS_H */
33 #include <unistd.h>
34 #include <errno.h>
35
36 #include "sudo.h"
37 #include "sudo_plugin.h"
38 #include "sudo_plugin_int.h"
39
40 /* Singly linked hook list. */
41 struct sudo_hook_entry {
42     SLIST_ENTRY(sudo_hook_entry) entries;
43     union {
44         sudo_hook_fn_t generic_fn;
45         sudo_hook_fn_setenv_t setenv_fn;
46         sudo_hook_fn_unsetenv_t unsetenv_fn;
47         sudo_hook_fn_getenv_t getenv_fn;
48         sudo_hook_fn_putenv_t putenv_fn;
49     } u;
50     void *closure;
51 };
52 SLIST_HEAD(sudo_hook_list, sudo_hook_entry);
53
54 /* Each hook type gets own hook list. */
55 static struct sudo_hook_list sudo_hook_setenv_list =
56     SLIST_HEAD_INITIALIZER(sudo_hook_setenv_list);
57 static struct sudo_hook_list sudo_hook_unsetenv_list =
58     SLIST_HEAD_INITIALIZER(sudo_hook_unsetenv_list);
59 static struct sudo_hook_list sudo_hook_getenv_list =
60     SLIST_HEAD_INITIALIZER(sudo_hook_getenv_list);
61 static struct sudo_hook_list sudo_hook_putenv_list =
62     SLIST_HEAD_INITIALIZER(sudo_hook_putenv_list);
63
64 /* NOTE: must not anything that might call setenv() */
65 int
66 process_hooks_setenv(const char *name, const char *value, int overwrite)
67 {
68     struct sudo_hook_entry *hook;
69     int rc = SUDO_HOOK_RET_NEXT;
70
71     /* First process the hooks. */
72     SLIST_FOREACH(hook, &sudo_hook_setenv_list, entries) {
73         rc = hook->u.setenv_fn(name, value, overwrite, hook->closure);
74         if (rc == SUDO_HOOK_RET_STOP || rc == SUDO_HOOK_RET_ERROR)
75             break;
76     }
77     return rc;
78 }
79
80 /* NOTE: must not anything that might call putenv() */
81 int
82 process_hooks_putenv(char *string)
83 {
84     struct sudo_hook_entry *hook;
85     int rc = SUDO_HOOK_RET_NEXT;
86
87     /* First process the hooks. */
88     SLIST_FOREACH(hook, &sudo_hook_putenv_list, entries) {
89         rc = hook->u.putenv_fn(string, hook->closure);
90         if (rc == SUDO_HOOK_RET_STOP || rc == SUDO_HOOK_RET_ERROR)
91             break;
92     }
93     return rc;
94 }
95
96 /* NOTE: must not anything that might call getenv() */
97 int
98 process_hooks_getenv(const char *name, char **value)
99 {
100     struct sudo_hook_entry *hook;
101     char *val = NULL;
102     int rc = SUDO_HOOK_RET_NEXT;
103
104     /* First process the hooks. */
105     SLIST_FOREACH(hook, &sudo_hook_getenv_list, entries) {
106         rc = hook->u.getenv_fn(name, &val, hook->closure);
107         if (rc == SUDO_HOOK_RET_STOP || rc == SUDO_HOOK_RET_ERROR)
108             break;
109     }
110     if (val != NULL)
111         *value = val;
112     return rc;
113 }
114
115 /* NOTE: must not anything that might call unsetenv() */
116 int
117 process_hooks_unsetenv(const char *name)
118 {
119     struct sudo_hook_entry *hook;
120     int rc = SUDO_HOOK_RET_NEXT;
121
122     /* First process the hooks. */
123     SLIST_FOREACH(hook, &sudo_hook_unsetenv_list, entries) {
124         rc = hook->u.unsetenv_fn(name, hook->closure);
125         if (rc == SUDO_HOOK_RET_STOP || rc == SUDO_HOOK_RET_ERROR)
126             break;
127     }
128     return rc;
129 }
130
131 /* Hook registration internals. */
132 static int
133 register_hook_internal(struct sudo_hook_list *head,
134     int (*hook_fn)(), void *closure)
135 {
136     struct sudo_hook_entry *hook;
137     debug_decl(register_hook_internal, SUDO_DEBUG_HOOKS)
138
139     if ((hook = calloc(1, sizeof(*hook))) == NULL) {
140         sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
141             "unable to allocate memory");
142         debug_return_int(-1);
143     }
144     hook->u.generic_fn = hook_fn;
145     hook->closure = closure;
146     SLIST_INSERT_HEAD(head, hook, entries);
147
148     debug_return_int(0);
149 }
150
151 /* Register the specified hook. */
152 int
153 register_hook(struct sudo_hook *hook)
154 {
155     int ret;
156     debug_decl(register_hook, SUDO_DEBUG_HOOKS)
157
158     if (SUDO_API_VERSION_GET_MAJOR(hook->hook_version) != SUDO_HOOK_VERSION_MAJOR) {
159         /* Major versions must match. */
160         errno = EINVAL;
161         ret = -1;
162     } else {
163         switch (hook->hook_type) {
164             case SUDO_HOOK_GETENV:
165                 ret = register_hook_internal(&sudo_hook_getenv_list,
166                     hook->hook_fn, hook->closure);
167                 break;
168             case SUDO_HOOK_PUTENV:
169                 ret = register_hook_internal(&sudo_hook_putenv_list,
170                     hook->hook_fn, hook->closure);
171                 break;
172             case SUDO_HOOK_SETENV:
173                 ret = register_hook_internal(&sudo_hook_setenv_list,
174                     hook->hook_fn, hook->closure);
175                 break;
176             case SUDO_HOOK_UNSETENV:
177                 ret = register_hook_internal(&sudo_hook_unsetenv_list,
178                     hook->hook_fn, hook->closure);
179                 break;
180             default:
181                 /* XXX - use define for unknown value */
182                 errno = ENOTSUP;
183                 ret = 1;
184                 break;
185         }
186     }
187
188     debug_return_int(ret);
189 }
190
191 /* Hook deregistration internals. */
192 static void
193 deregister_hook_internal(struct sudo_hook_list *head,
194     int (*hook_fn)(), void *closure)
195 {
196     struct sudo_hook_entry *hook, *prev = NULL;
197     debug_decl(deregister_hook_internal, SUDO_DEBUG_HOOKS)
198
199     SLIST_FOREACH(hook, head, entries) {
200         if (hook->u.generic_fn == hook_fn && hook->closure == closure) {
201             /* Remove from list and free. */
202             if (prev == NULL)
203                 SLIST_REMOVE_HEAD(head, entries);
204             else
205                 SLIST_REMOVE_AFTER(prev, entries);
206             free(hook);
207             break;
208         }
209         prev = hook;
210     }
211
212     debug_return;
213 }
214
215 /* Deregister the specified hook. */
216 int
217 deregister_hook(struct sudo_hook *hook)
218 {
219     int ret = 0;
220     debug_decl(deregister_hook, SUDO_DEBUG_HOOKS)
221
222     if (SUDO_API_VERSION_GET_MAJOR(hook->hook_version) != SUDO_HOOK_VERSION_MAJOR) {
223         /* Major versions must match. */
224         ret = -1;
225     } else {
226         switch (hook->hook_type) {
227             case SUDO_HOOK_GETENV:
228                 deregister_hook_internal(&sudo_hook_getenv_list, hook->hook_fn,
229                     hook->closure);
230                 break;
231             case SUDO_HOOK_PUTENV:
232                 deregister_hook_internal(&sudo_hook_putenv_list, hook->hook_fn,
233                     hook->closure);
234                 break;
235             case SUDO_HOOK_SETENV:
236                 deregister_hook_internal(&sudo_hook_setenv_list, hook->hook_fn,
237                     hook->closure);
238                 break;
239             case SUDO_HOOK_UNSETENV:
240                 deregister_hook_internal(&sudo_hook_unsetenv_list, hook->hook_fn,
241                     hook->closure);
242                 break;
243             default:
244                 /* XXX - use define for unknown value */
245                 ret = 1;
246                 break;
247         }
248     }
249
250     debug_return_int(ret);
251 }