Imported Upstream version 1.8.7
[debian/sudo] / src / hooks.c
1 /*
2  * Copyright (c) 2012 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 # include <string.h>
31 #endif /* HAVE_STRING_H */
32 #ifdef HAVE_STRINGS_H
33 # include <strings.h>
34 #endif /* HAVE_STRINGS_H */
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif /* HAVE_UNISTD_H */
38
39 #include "sudo.h"
40 #include "sudo_plugin.h"
41 #include "sudo_plugin_int.h"
42 #include "sudo_debug.h"
43
44 /* Singly linked hook list. */
45 struct sudo_hook_list {
46     struct sudo_hook_list *next;
47     union {
48         sudo_hook_fn_t generic_fn;
49         sudo_hook_fn_setenv_t setenv_fn;
50         sudo_hook_fn_unsetenv_t unsetenv_fn;
51         sudo_hook_fn_getenv_t getenv_fn;
52         sudo_hook_fn_putenv_t putenv_fn;
53     } u;
54     void *closure;
55 };
56
57 /* Each hook type gets own hook list. */
58 static struct sudo_hook_list *sudo_hook_setenv_list;
59 static struct sudo_hook_list *sudo_hook_unsetenv_list;
60 static struct sudo_hook_list *sudo_hook_getenv_list;
61 static struct sudo_hook_list *sudo_hook_putenv_list;
62
63 /* NOTE: must not anything that might call setenv() */
64 int
65 process_hooks_setenv(const char *name, const char *value, int overwrite)
66 {
67     struct sudo_hook_list *hook;
68     int rc = SUDO_HOOK_RET_NEXT;
69
70     /* First process the hooks. */
71     for (hook = sudo_hook_setenv_list; hook != NULL; hook = hook->next) {
72         rc = hook->u.setenv_fn(name, value, overwrite, hook->closure);
73         switch (rc) {
74             case SUDO_HOOK_RET_NEXT:
75                 break;
76             case SUDO_HOOK_RET_ERROR:
77             case SUDO_HOOK_RET_STOP:
78                 goto done;
79             default:
80                 warningx_nodebug("invalid setenv hook return value: %d", rc);
81                 break;
82         }
83     }
84 done:
85     return rc;
86 }
87
88 /* NOTE: must not anything that might call putenv() */
89 int
90 process_hooks_putenv(char *string)
91 {
92     struct sudo_hook_list *hook;
93     int rc = SUDO_HOOK_RET_NEXT;
94
95     /* First process the hooks. */
96     for (hook = sudo_hook_putenv_list; hook != NULL; hook = hook->next) {
97         rc = hook->u.putenv_fn(string, hook->closure);
98         switch (rc) {
99             case SUDO_HOOK_RET_NEXT:
100                 break;
101             case SUDO_HOOK_RET_ERROR:
102             case SUDO_HOOK_RET_STOP:
103                 goto done;
104             default:
105                 warningx_nodebug("invalid putenv hook return value: %d", rc);
106                 break;
107         }
108     }
109 done:
110     return rc;
111 }
112
113 /* NOTE: must not anything that might call getenv() */
114 int
115 process_hooks_getenv(const char *name, char **value)
116 {
117     struct sudo_hook_list *hook;
118     char *val = NULL;
119     int rc = SUDO_HOOK_RET_NEXT;
120
121     /* First process the hooks. */
122     for (hook = sudo_hook_getenv_list; hook != NULL; hook = hook->next) {
123         rc = hook->u.getenv_fn(name, &val, hook->closure);
124         switch (rc) {
125             case SUDO_HOOK_RET_NEXT:
126                 break;
127             case SUDO_HOOK_RET_ERROR:
128             case SUDO_HOOK_RET_STOP:
129                 goto done;
130             default:
131                 warningx_nodebug("invalid getenv hook return value: %d", rc);
132                 break;
133         }
134     }
135 done:
136     if (val != NULL)
137         *value = val;
138     return rc;
139 }
140
141 /* NOTE: must not anything that might call unsetenv() */
142 int
143 process_hooks_unsetenv(const char *name)
144 {
145     struct sudo_hook_list *hook;
146     int rc = SUDO_HOOK_RET_NEXT;
147
148     /* First process the hooks. */
149     for (hook = sudo_hook_unsetenv_list; hook != NULL; hook = hook->next) {
150         rc = hook->u.unsetenv_fn(name, hook->closure);
151         switch (rc) {
152             case SUDO_HOOK_RET_NEXT:
153                 break;
154             case SUDO_HOOK_RET_ERROR:
155             case SUDO_HOOK_RET_STOP:
156                 goto done;
157             default:
158                 warningx_nodebug("invalid unsetenv hook return value: %d", rc);
159                 break;
160         }
161     }
162 done:
163     return rc;
164 }
165
166 /* Hook registration internals. */
167 static void
168 register_hook_internal(struct sudo_hook_list **head,
169     int (*hook_fn)(), void *closure)
170 {
171     struct sudo_hook_list *hook;
172     debug_decl(register_hook_internal, SUDO_DEBUG_HOOKS)
173
174     hook = ecalloc(1, sizeof(*hook));
175     hook->u.generic_fn = hook_fn;
176     hook->closure = closure;
177     hook->next = *head;
178     *head = hook;
179
180     debug_return;
181 }
182
183 /* Register the specified hook. */
184 int
185 register_hook(struct sudo_hook *hook)
186 {
187     int rval = 0;
188     debug_decl(register_hook, SUDO_DEBUG_HOOKS)
189
190     if (SUDO_HOOK_VERSION_GET_MAJOR(hook->hook_version) != SUDO_HOOK_VERSION_MAJOR) {
191         /* Major versions must match. */
192         rval = -1;
193     } else {
194         switch (hook->hook_type) {
195             case SUDO_HOOK_GETENV:
196                 register_hook_internal(&sudo_hook_getenv_list, hook->hook_fn,
197                     hook->closure);
198                 break;
199             case SUDO_HOOK_PUTENV:
200                 register_hook_internal(&sudo_hook_putenv_list, hook->hook_fn,
201                     hook->closure);
202                 break;
203             case SUDO_HOOK_SETENV:
204                 register_hook_internal(&sudo_hook_setenv_list, hook->hook_fn,
205                     hook->closure);
206                 break;
207             case SUDO_HOOK_UNSETENV:
208                 register_hook_internal(&sudo_hook_unsetenv_list, hook->hook_fn,
209                     hook->closure);
210                 break;
211             default:
212                 /* XXX - use define for unknown value */
213                 rval = 1;
214                 break;
215         }
216     }
217
218     debug_return_int(rval);
219 }
220
221 /* Hook deregistration internals. */
222 static void
223 deregister_hook_internal(struct sudo_hook_list **head,
224     int (*hook_fn)(), void *closure)
225 {
226     struct sudo_hook_list *hook, *prev = NULL;
227     debug_decl(deregister_hook_internal, SUDO_DEBUG_HOOKS)
228
229     for (hook = *head, prev = NULL; hook != NULL; prev = hook, hook = hook->next) {
230         if (hook->u.generic_fn == hook_fn && hook->closure == closure) {
231             /* Remove from list and free. */
232             if (prev == NULL)
233                 *head = hook->next;
234             else
235                 prev->next = hook->next;
236             efree(hook);
237             break;
238         }
239     }
240
241     debug_return;
242 }
243
244 /* Deregister the specified hook. */
245 int
246 deregister_hook(struct sudo_hook *hook)
247 {
248     int rval = 0;
249     debug_decl(deregister_hook, SUDO_DEBUG_HOOKS)
250
251     if (SUDO_HOOK_VERSION_GET_MAJOR(hook->hook_version) != SUDO_HOOK_VERSION_MAJOR) {
252         /* Major versions must match. */
253         rval = -1;
254     } else {
255         switch (hook->hook_type) {
256             case SUDO_HOOK_GETENV:
257                 deregister_hook_internal(&sudo_hook_getenv_list, hook->hook_fn,
258                     hook->closure);
259                 break;
260             case SUDO_HOOK_PUTENV:
261                 deregister_hook_internal(&sudo_hook_putenv_list, hook->hook_fn,
262                     hook->closure);
263                 break;
264             case SUDO_HOOK_SETENV:
265                 deregister_hook_internal(&sudo_hook_setenv_list, hook->hook_fn,
266                     hook->closure);
267                 break;
268             case SUDO_HOOK_UNSETENV:
269                 deregister_hook_internal(&sudo_hook_unsetenv_list, hook->hook_fn,
270                     hook->closure);
271                 break;
272             default:
273                 /* XXX - use define for unknown value */
274                 rval = 1;
275                 break;
276         }
277     }
278
279     debug_return_int(rval);
280 }