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