sigh, looks like we need libtool too
[debian/sudo] / src / sudo.c
1 /*
2  * Copyright (c) 2009-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 #ifdef __TANDEM
18 # include <floss.h>
19 #endif
20
21 #include <config.h>
22
23 #include <sys/types.h>
24 #include <sys/param.h>
25 #include <sys/stat.h>
26 #include <sys/wait.h>
27 #include <sys/socket.h>
28 #include <sys/time.h>
29 #include <sys/resource.h>
30 #include <stdio.h>
31 #ifdef STDC_HEADERS
32 # include <stdlib.h>
33 # include <stddef.h>
34 #else
35 # ifdef HAVE_STDLIB_H
36 #  include <stdlib.h>
37 # endif
38 #endif /* STDC_HEADERS */
39 #ifdef HAVE_STRING_H
40 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
41 #  include <memory.h>
42 # endif
43 # include <string.h>
44 #endif /* HAVE_STRING_H */
45 #ifdef HAVE_STRINGS_H
46 # include <strings.h>
47 #endif /* HAVE_STRINGS_H */
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif /* HAVE_UNISTD_H */
51 #include <ctype.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <limits.h>
55 #include <signal.h>
56 #include <grp.h>
57 #include <pwd.h>
58 #if TIME_WITH_SYS_TIME
59 # include <time.h>
60 #endif
61 #ifdef HAVE_SETLOCALE
62 # include <locale.h>
63 #endif
64 #ifdef HAVE_LOGIN_CAP_H
65 # include <login_cap.h>
66 # ifndef LOGIN_SETENV
67 #  define LOGIN_SETENV  0
68 # endif
69 #endif
70 #ifdef HAVE_PROJECT_H
71 # include <project.h>
72 # include <sys/task.h>
73 #endif
74 #ifdef HAVE_SELINUX
75 # include <selinux/selinux.h>
76 #endif
77 #ifdef HAVE_SETAUTHDB
78 # include <usersec.h>
79 #endif /* HAVE_SETAUTHDB */
80 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
81 # ifdef __hpux
82 #  undef MAXINT
83 #  include <hpsecurity.h>
84 # else
85 #  include <sys/security.h>
86 # endif /* __hpux */
87 # include <prot.h>
88 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
89 #if defined(HAVE_STRUCT_KINFO_PROC_P_TDEV) || defined (HAVE_STRUCT_KINFO_PROC_KP_EPROC_E_TDEV)
90 # include <sys/sysctl.h>
91 #elif defined(HAVE_STRUCT_KINFO_PROC_KI_TDEV)
92 # include <sys/sysctl.h>
93 # include <sys/user.h>
94 #endif
95
96 #include "sudo.h"
97 #include "sudo_plugin.h"
98 #include "sudo_plugin_int.h"
99 #include <sudo_usage.h>
100
101 /*
102  * Local variables
103  */
104 struct plugin_container policy_plugin;
105 struct plugin_container_list io_plugins;
106 struct user_details user_details;
107 const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */
108 static int sudo_mode;
109
110 /*
111  * Local functions
112  */
113 static void fix_fds(void);
114 static void disable_coredumps(void);
115 static void sudo_check_suid(const char *path);
116 static char **get_user_info(struct user_details *);
117 static void command_info_to_details(char * const info[],
118     struct command_details *details);
119
120 /* Policy plugin convenience functions. */
121 static int policy_open(struct plugin_container *plugin, char * const settings[],
122     char * const user_info[], char * const user_env[]);
123 static void policy_close(struct plugin_container *plugin, int exit_status,
124     int error);
125 static int policy_show_version(struct plugin_container *plugin, int verbose);
126 static int policy_check(struct plugin_container *plugin, int argc,
127     char * const argv[], char *env_add[], char **command_info[],
128     char **argv_out[], char **user_env_out[]);
129 static int policy_list(struct plugin_container *plugin, int argc,
130     char * const argv[], int verbose, const char *list_user);
131 static int policy_validate(struct plugin_container *plugin);
132 static void policy_invalidate(struct plugin_container *plugin, int remove);
133
134 /* I/O log plugin convenience functions. */
135 static int iolog_open(struct plugin_container *plugin, char * const settings[],
136     char * const user_info[], char * const command_details[],
137     int argc, char * const argv[], char * const user_env[]);
138 static void iolog_close(struct plugin_container *plugin, int exit_status,
139     int error);
140 static int iolog_show_version(struct plugin_container *plugin, int verbose);
141 static void iolog_unlink(struct plugin_container *plugin);
142
143 #ifdef RLIMIT_CORE
144 static struct rlimit corelimit;
145 #endif /* RLIMIT_CORE */
146 #if defined(__linux__)
147 static struct rlimit nproclimit;
148 #endif
149
150 int
151 main(int argc, char *argv[], char *envp[])
152 {
153     int nargc, ok, exitcode = 0;
154     char **nargv, **settings, **env_add;
155     char **user_info, **command_info, **argv_out, **user_env_out;
156     struct plugin_container *plugin, *next;
157     struct command_details command_details;
158     sigset_t mask;
159     debug_decl(main, SUDO_DEBUG_MAIN)
160
161 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
162     {
163         extern char *malloc_options;
164         malloc_options = "AFGJPR";
165     }
166 #endif
167
168 #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
169     if (argc > 0)
170         setprogname(argv[0]);
171 #endif
172
173 #ifdef HAVE_SETLOCALE
174     setlocale(LC_ALL, "");
175 #endif
176     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
177     textdomain(PACKAGE_NAME);
178
179     /* Must be done before we do any password lookups */
180 #if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
181     (void) set_auth_parameters(argc, argv);
182 # ifdef HAVE_INITPRIVS
183     initprivs();
184 # endif
185 #endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
186
187     /* Make sure we are setuid root. */
188     sudo_check_suid(argv[0]);
189
190     /* Reset signal mask and make sure fds 0-2 are open. */
191     (void) sigemptyset(&mask);
192     (void) sigprocmask(SIG_SETMASK, &mask, NULL);
193     fix_fds();
194
195     /* Fill in user_info with user name, uid, cwd, etc. */
196     memset(&user_details, 0, sizeof(user_details));
197     user_info = get_user_info(&user_details);
198
199     /* Read sudo.conf. */
200     sudo_conf_read();
201
202     /* Disable core dumps if not enabled in sudo.conf. */
203     disable_coredumps();
204
205     /* Parse command line arguments. */
206     sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
207     sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode);
208
209     /* Print sudo version early, in case of plugin init failure. */
210     if (ISSET(sudo_mode, MODE_VERSION)) {
211         printf(_("Sudo version %s\n"), PACKAGE_VERSION);
212         if (user_details.uid == ROOT_UID)
213             (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
214     }
215
216     /* Load plugins. */
217     if (!sudo_load_plugins(&policy_plugin, &io_plugins))
218         errorx(1, _("fatal error, unable to load plugins"));
219
220     /* Open policy plugin. */
221     ok = policy_open(&policy_plugin, settings, user_info, envp);
222     if (ok != 1) {
223         if (ok == -2)
224             usage(1);
225         else
226             errorx(1, _("unable to initialize policy plugin"));
227     }
228
229     switch (sudo_mode & MODE_MASK) {
230         case MODE_VERSION:
231             policy_show_version(&policy_plugin, !user_details.uid);
232             tq_foreach_fwd(&io_plugins, plugin) {
233                 ok = iolog_open(plugin, settings, user_info, NULL,
234                     nargc, nargv, envp);
235                 if (ok == 1)
236                     iolog_show_version(plugin, !user_details.uid);
237             }
238             break;
239         case MODE_VALIDATE:
240         case MODE_VALIDATE|MODE_INVALIDATE:
241             ok = policy_validate(&policy_plugin);
242             exit(ok != 1);
243         case MODE_KILL:
244         case MODE_INVALIDATE:
245             policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL);
246             exit(0);
247             break;
248         case MODE_CHECK:
249         case MODE_CHECK|MODE_INVALIDATE:
250         case MODE_LIST:
251         case MODE_LIST|MODE_INVALIDATE:
252             ok = policy_list(&policy_plugin, nargc, nargv,
253                 ISSET(sudo_mode, MODE_LONG_LIST), list_user);
254             exit(ok != 1);
255         case MODE_EDIT:
256         case MODE_RUN:
257             ok = policy_check(&policy_plugin, nargc, nargv, env_add,
258                 &command_info, &argv_out, &user_env_out);
259             sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
260             if (ok != 1) {
261                 if (ok == -2)
262                     usage(1);
263                 exit(1); /* plugin printed error message */
264             }
265             /* Open I/O plugins once policy plugin succeeds. */
266             for (plugin = io_plugins.first; plugin != NULL; plugin = next) {
267                 next = plugin->next;
268                 ok = iolog_open(plugin, settings, user_info,
269                     command_info, nargc, nargv, envp);
270                 switch (ok) {
271                 case 1:
272                     break;
273                 case 0:
274                     /* I/O plugin asked to be disabled, remove and free. */
275                     iolog_unlink(plugin);
276                     break;
277                 case -2:
278                     usage(1);
279                     break;
280                 default:
281                     errorx(1, _("error initializing I/O plugin %s"),
282                         plugin->name);
283                 }
284             }
285             /* Setup command details and run command/edit. */
286             command_info_to_details(command_info, &command_details);
287             command_details.argv = argv_out;
288             command_details.envp = user_env_out;
289             if (ISSET(sudo_mode, MODE_BACKGROUND))
290                 SET(command_details.flags, CD_BACKGROUND);
291             /* Become full root (not just setuid) so user cannot kill us. */
292             (void) setuid(ROOT_UID);
293             /* Restore coredumpsize resource limit before running. */
294 #ifdef RLIMIT_CORE
295             if (sudo_conf_disable_coredump())
296                 (void) setrlimit(RLIMIT_CORE, &corelimit);
297 #endif /* RLIMIT_CORE */
298             if (ISSET(command_details.flags, CD_SUDOEDIT)) {
299                 exitcode = sudo_edit(&command_details);
300             } else {
301                 exitcode = run_command(&command_details);
302             }
303             /* The close method was called by sudo_edit/run_command. */
304             break;
305         default:
306             errorx(1, _("unexpected sudo mode 0x%x"), sudo_mode);
307     }
308     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);                
309     exit(exitcode);
310 }
311
312 /*
313  * Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
314  * Some operating systems do this automatically in the kernel or libc.
315  */
316 static void
317 fix_fds(void)
318 {
319     int miss[3], devnull = -1;
320     debug_decl(fix_fds, SUDO_DEBUG_UTIL)
321
322     /*
323      * stdin, stdout and stderr must be open; set them to /dev/null
324      * if they are closed.
325      */
326     miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
327     miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
328     miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
329     if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
330         if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) == -1)
331             error(1, _("unable to open %s"), _PATH_DEVNULL);
332         if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
333             error(1, "dup2");
334         if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
335             error(1, "dup2");
336         if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
337             error(1, "dup2");
338         if (devnull > STDERR_FILENO)
339             close(devnull);
340     }
341     debug_return;
342 }
343
344 /*
345  * Allocate space for groups and fill in using getgrouplist()
346  * for when we cannot use getgroups().
347  */
348 static int
349 fill_group_list(struct user_details *ud)
350 {
351     int maxgroups, tries, rval = -1;
352     debug_decl(fill_group_list, SUDO_DEBUG_UTIL)
353
354 #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
355     maxgroups = (int)sysconf(_SC_NGROUPS_MAX);
356     if (maxgroups < 0)
357 #endif
358         maxgroups = NGROUPS_MAX;
359
360     /*
361      * It is possible to belong to more groups in the group database
362      * than NGROUPS_MAX.  We start off with NGROUPS_MAX * 2 entries
363      * and double this as needed.
364      */
365     ud->groups = NULL;
366     ud->ngroups = maxgroups;
367     for (tries = 0; tries < 10 && rval == -1; tries++) {
368         ud->ngroups *= 2;
369         efree(ud->groups);
370         ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
371         rval = getgrouplist(ud->username, ud->gid, ud->groups, &ud->ngroups);
372     }
373     debug_return_int(rval);
374 }
375
376 static char *
377 get_user_groups(struct user_details *ud)
378 {
379     char *cp, *gid_list = NULL;
380     size_t glsize;
381     int i, len;
382     debug_decl(get_user_groups, SUDO_DEBUG_UTIL)
383
384     /*
385      * Systems with mbr_check_membership() support more than NGROUPS_MAX
386      * groups so we cannot use getgroups().
387      */
388     ud->groups = NULL;
389 #ifndef HAVE_MBR_CHECK_MEMBERSHIP
390     if ((ud->ngroups = getgroups(0, NULL)) > 0) {
391         ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
392         if (getgroups(ud->ngroups, ud->groups) < 0) {
393             efree(ud->groups);
394             ud->groups = NULL;
395         }
396     }
397 #endif /* HAVE_MBR_CHECK_MEMBERSHIP */
398     if (ud->groups == NULL) {
399         if (fill_group_list(ud) == -1)
400             error(1, _("unable to get group vector"));
401     }
402
403     /*
404      * Format group list as a comma-separated string of gids.
405      */
406     glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
407     gid_list = emalloc(glsize);
408     memcpy(gid_list, "groups=", sizeof("groups=") - 1);
409     cp = gid_list + sizeof("groups=") - 1;
410     for (i = 0; i < ud->ngroups; i++) {
411         /* XXX - check rval */
412         len = snprintf(cp, glsize - (cp - gid_list), "%s%u",
413             i ? "," : "", (unsigned int)ud->groups[i]);
414         cp += len;
415     }
416     debug_return_str(gid_list);
417 }
418
419 /*
420  * Return user information as an array of name=value pairs.
421  * and fill in struct user_details (which shares the same strings).
422  */
423 static char **
424 get_user_info(struct user_details *ud)
425 {
426     char *cp, **user_info, cwd[PATH_MAX], host[MAXHOSTNAMELEN];
427     struct passwd *pw;
428     int fd, i = 0;
429     debug_decl(get_user_info, SUDO_DEBUG_UTIL)
430
431     /* XXX - bound check number of entries */
432     user_info = emalloc2(32, sizeof(char *));
433
434     ud->pid = getpid();
435     ud->ppid = getppid();
436     ud->pgid = getpgid(0);
437     ud->tcpgid = (pid_t)-1;
438     fd = open(_PATH_TTY, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
439     if (fd != -1) {
440         ud->tcpgid = tcgetpgrp(fd);
441         close(fd);
442     }
443     ud->sid = getsid(0);
444
445     ud->uid = getuid();
446     ud->euid = geteuid();
447     ud->gid = getgid();
448     ud->egid = getegid();
449
450     pw = getpwuid(ud->uid);
451     if (pw == NULL)
452         errorx(1, _("unknown uid %u: who are you?"), (unsigned int)ud->uid);
453
454     user_info[i] = fmt_string("user", pw->pw_name);
455     if (user_info[i] == NULL)
456         errorx(1, _("unable to allocate memory"));
457     ud->username = user_info[i] + sizeof("user=") - 1;
458
459     /* Stash user's shell for use with the -s flag; don't pass to plugin. */
460     if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
461         ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
462     }
463     ud->shell = estrdup(ud->shell);
464
465     easprintf(&user_info[++i], "pid=%d", (int)ud->pid);
466     easprintf(&user_info[++i], "ppid=%d", (int)ud->ppid);
467     easprintf(&user_info[++i], "pgid=%d", (int)ud->pgid);
468     easprintf(&user_info[++i], "tcpgid=%d", (int)ud->tcpgid);
469     easprintf(&user_info[++i], "sid=%d", (int)ud->sid);
470
471     easprintf(&user_info[++i], "uid=%u", (unsigned int)ud->uid);
472     easprintf(&user_info[++i], "euid=%u", (unsigned int)ud->euid);
473     easprintf(&user_info[++i], "gid=%u", (unsigned int)ud->gid);
474     easprintf(&user_info[++i], "egid=%u", (unsigned int)ud->egid);
475
476     if ((cp = get_user_groups(ud)) != NULL)
477         user_info[++i] = cp;
478
479     if (getcwd(cwd, sizeof(cwd)) != NULL) {
480         user_info[++i] = fmt_string("cwd", cwd);
481         if (user_info[i] == NULL)
482             errorx(1, _("unable to allocate memory"));
483         ud->cwd = user_info[i] + sizeof("cwd=") - 1;
484     }
485
486     if ((cp = get_process_ttyname()) != NULL) {
487         user_info[++i] = fmt_string("tty", cp);
488         if (user_info[i] == NULL)
489             errorx(1, _("unable to allocate memory"));
490         ud->tty = user_info[i] + sizeof("tty=") - 1;
491         efree(cp);
492     }
493
494     if (gethostname(host, sizeof(host)) == 0)
495         host[sizeof(host) - 1] = '\0';
496     else
497         strlcpy(host, "localhost", sizeof(host));
498     user_info[++i] = fmt_string("host", host);
499     if (user_info[i] == NULL)
500         errorx(1, _("unable to allocate memory"));
501     ud->host = user_info[i] + sizeof("host=") - 1;
502
503     get_ttysize(&ud->ts_lines, &ud->ts_cols);
504     easprintf(&user_info[++i], "lines=%d", ud->ts_lines);
505     easprintf(&user_info[++i], "cols=%d", ud->ts_cols);
506
507     user_info[++i] = NULL;
508
509     debug_return_ptr(user_info);
510 }
511
512 /*
513  * Convert a command_info array into a command_details structure.
514  */
515 static void
516 command_info_to_details(char * const info[], struct command_details *details)
517 {
518     int i;
519     long lval;
520     unsigned long ulval;
521     char *cp, *ep;
522     debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM)
523
524     memset(details, 0, sizeof(*details));
525     details->closefrom = -1;
526
527 #define SET_STRING(s, n) \
528     if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
529         details->n = info[i] + sizeof(s) - 1; \
530         break; \
531     }
532
533     sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
534     for (i = 0; info[i] != NULL; i++) {
535         sudo_debug_printf(SUDO_DEBUG_INFO, "    %d: %s", i, info[i]);
536         switch (info[i][0]) {
537             case 'c':
538                 SET_STRING("chroot=", chroot)
539                 SET_STRING("command=", command)
540                 SET_STRING("cwd=", cwd)
541                 if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
542                     cp = info[i] + sizeof("closefrom=") - 1;
543                     if (*cp == '\0')
544                         break;
545                     errno = 0;
546                     lval = strtol(cp, &ep, 0);
547                     if (*cp != '\0' && *ep == '\0' &&
548                         !(errno == ERANGE &&
549                         (lval == LONG_MAX || lval == LONG_MIN)) &&
550                         lval < INT_MAX && lval > INT_MIN) {
551                         details->closefrom = (int)lval;
552                     }
553                     break;
554                 }
555                 break;
556             case 'l':
557                 SET_STRING("login_class=", login_class)
558                 break;
559             case 'n':
560                 /* XXX - bounds check  -NZERO to NZERO (inclusive). */
561                 if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
562                     cp = info[i] + sizeof("nice=") - 1;
563                     if (*cp == '\0')
564                         break;
565                     errno = 0;
566                     lval = strtol(cp, &ep, 0);
567                     if (*cp != '\0' && *ep == '\0' &&
568                         !(errno == ERANGE &&
569                         (lval == LONG_MAX || lval == LONG_MIN)) &&
570                         lval < INT_MAX && lval > INT_MIN) {
571                         details->priority = (int)lval;
572                         SET(details->flags, CD_SET_PRIORITY);
573                     }
574                     break;
575                 }
576                 if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
577                     if (atobool(info[i] + sizeof("noexec=") - 1) == true)
578                         SET(details->flags, CD_NOEXEC);
579                     break;
580                 }
581                 break;
582             case 'p':
583                 if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
584                     if (atobool(info[i] + sizeof("preserve_groups=") - 1) == true)
585                         SET(details->flags, CD_PRESERVE_GROUPS);
586                     break;
587                 }
588                 break;
589             case 'r':
590                 if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
591                     cp = info[i] + sizeof("runas_egid=") - 1;
592                     if (*cp == '\0')
593                         break;
594                     errno = 0;
595                     ulval = strtoul(cp, &ep, 0);
596                     if (*cp != '\0' && *ep == '\0' &&
597                         (errno != ERANGE || ulval != ULONG_MAX)) {
598                         details->egid = (gid_t)ulval;
599                         SET(details->flags, CD_SET_EGID);
600                     }
601                     break;
602                 }
603                 if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
604                     cp = info[i] + sizeof("runas_euid=") - 1;
605                     if (*cp == '\0')
606                         break;
607                     errno = 0;
608                     ulval = strtoul(cp, &ep, 0);
609                     if (*cp != '\0' && *ep == '\0' &&
610                         (errno != ERANGE || ulval != ULONG_MAX)) {
611                         details->euid = (uid_t)ulval;
612                         SET(details->flags, CD_SET_EUID);
613                     }
614                     break;
615                 }
616                 if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
617                     cp = info[i] + sizeof("runas_gid=") - 1;
618                     if (*cp == '\0')
619                         break;
620                     errno = 0;
621                     ulval = strtoul(cp, &ep, 0);
622                     if (*cp != '\0' && *ep == '\0' &&
623                         (errno != ERANGE || ulval != ULONG_MAX)) {
624                         details->gid = (gid_t)ulval;
625                         SET(details->flags, CD_SET_GID);
626                     }
627                     break;
628                 }
629                 if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
630                     int j;
631
632                     /* count groups, alloc and fill in */
633                     cp = info[i] + sizeof("runas_groups=") - 1;
634                     if (*cp == '\0')
635                         break;
636                     for (;;) {
637                         details->ngroups++;
638                         if ((cp = strchr(cp, ',')) == NULL)
639                             break;
640                         cp++;
641                     }
642                     if (details->ngroups != 0) {
643                         details->groups =
644                             emalloc2(details->ngroups, sizeof(GETGROUPS_T));
645                         cp = info[i] + sizeof("runas_groups=") - 1;
646                         for (j = 0; j < details->ngroups;) {
647                             errno = 0;
648                             ulval = strtoul(cp, &ep, 0);
649                             if (*cp == '\0' || (*ep != ',' && *ep != '\0') ||
650                                 (ulval == ULONG_MAX && errno == ERANGE)) {
651                                 break;
652                             }
653                             details->groups[j++] = (gid_t)ulval;
654                             cp = ep + 1;
655                         }
656                         details->ngroups = j;
657                     }
658                     break;
659                 }
660                 if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
661                     cp = info[i] + sizeof("runas_uid=") - 1;
662                     if (*cp == '\0')
663                         break;
664                     errno = 0;
665                     ulval = strtoul(cp, &ep, 0);
666                     if (*cp != '\0' && *ep == '\0' &&
667                         (errno != ERANGE || ulval != ULONG_MAX)) {
668                         details->uid = (uid_t)ulval;
669                         SET(details->flags, CD_SET_UID);
670                     }
671                     break;
672                 }
673                 break;
674             case 's':
675                 SET_STRING("selinux_role=", selinux_role)
676                 SET_STRING("selinux_type=", selinux_type)
677                 if (strncmp("set_utmp=", info[i], sizeof("set_utmp=") - 1) == 0) {
678                     if (atobool(info[i] + sizeof("set_utmp=") - 1) == true)
679                         SET(details->flags, CD_SET_UTMP);
680                     break;
681                 }
682                 if (strncmp("sudoedit=", info[i], sizeof("sudoedit=") - 1) == 0) {
683                     if (atobool(info[i] + sizeof("sudoedit=") - 1) == true)
684                         SET(details->flags, CD_SUDOEDIT);
685                     break;
686                 }
687                 break;
688             case 't':
689                 if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
690                     cp = info[i] + sizeof("timeout=") - 1;
691                     if (*cp == '\0')
692                         break;
693                     errno = 0;
694                     lval = strtol(cp, &ep, 0);
695                     if (*cp != '\0' && *ep == '\0' &&
696                         !(errno == ERANGE &&
697                         (lval == LONG_MAX || lval == LONG_MIN)) &&
698                         lval <= INT_MAX && lval >= 0) {
699                         details->timeout = (int)lval;
700                         SET(details->flags, CD_SET_TIMEOUT);
701                     }
702                     break;
703                 }
704                 break;
705             case 'u':
706                 if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
707                     cp = info[i] + sizeof("umask=") - 1;
708                     if (*cp == '\0')
709                         break;
710                     errno = 0;
711                     ulval = strtoul(cp, &ep, 8);
712                     if (*cp != '\0' && *ep == '\0' &&
713                         (errno != ERANGE || ulval != ULONG_MAX)) {
714                         details->umask = (uid_t)ulval;
715                         SET(details->flags, CD_SET_UMASK);
716                     }
717                     break;
718                 }
719                 if (strncmp("use_pty=", info[i], sizeof("use_pty=") - 1) == 0) {
720                     if (atobool(info[i] + sizeof("use_pty=") - 1) == true)
721                         SET(details->flags, CD_USE_PTY);
722                     break;
723                 }
724                 SET_STRING("utmp_user=", utmp_user)
725                 break;
726         }
727     }
728
729     if (!ISSET(details->flags, CD_SET_EUID))
730         details->euid = details->uid;
731
732 #ifdef HAVE_SETAUTHDB
733     aix_setauthdb(IDtouser(details->euid));
734 #endif
735     details->pw = getpwuid(details->euid);
736     if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
737         errorx(1, _("unable to allocate memory"));
738 #ifdef HAVE_SETAUTHDB
739     aix_restoreauthdb();
740 #endif
741
742 #ifdef HAVE_SELINUX
743     if (details->selinux_role != NULL && is_selinux_enabled() > 0)
744         SET(details->flags, CD_RBAC_ENABLED);
745 #endif
746     debug_return;
747 }
748
749 static void
750 sudo_check_suid(const char *path)
751 {
752     struct stat sb;
753     debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM)
754
755     if (geteuid() != 0) {
756         if (strchr(path, '/') != NULL && stat(path, &sb) == 0) {
757             /* Try to determine why sudo was not running as root. */
758             if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
759                 errorx(1,
760                     _("%s must be owned by uid %d and have the setuid bit set"),
761                     path, ROOT_UID);
762             } else {
763                 errorx(1, _("effective uid is not %d, is %s on a file system "
764                     "with the 'nosuid' option set or an NFS file system without"
765                     " root privileges?"), ROOT_UID, path);
766             }
767         } else {
768             errorx(1,
769                 _("effective uid is not %d, is sudo installed setuid root?"),
770                 ROOT_UID);
771         }
772     }
773     debug_return;
774 }
775
776 /*
777  * Disable core dumps to avoid dropping a core with user password in it.
778  * We will reset this limit before executing the command.
779  * Not all operating systems disable core dumps for setuid processes.
780  */
781 static void
782 disable_coredumps(void)
783 {
784 #if defined(__linux__) || defined(RLIMIT_CORE)
785     struct rlimit rl;
786 #endif
787     debug_decl(disable_coredumps, SUDO_DEBUG_UTIL)
788
789 #if defined(__linux__)
790     /*
791      * Unlimit the number of processes since Linux's setuid() will
792      * apply resource limits when changing uid and return EAGAIN if
793      * nproc would be violated by the uid switch.
794      */
795     (void) getrlimit(RLIMIT_NPROC, &nproclimit);
796     rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
797     if (setrlimit(RLIMIT_NPROC, &rl)) {
798         memcpy(&rl, &nproclimit, sizeof(struct rlimit));
799         rl.rlim_cur = rl.rlim_max;
800         (void)setrlimit(RLIMIT_NPROC, &rl);
801     }
802 #endif /* __linux__ */
803 #ifdef RLIMIT_CORE
804     /*
805      * Turn off core dumps?
806      */
807     if (sudo_conf_disable_coredump()) {
808         (void) getrlimit(RLIMIT_CORE, &corelimit);
809         memcpy(&rl, &corelimit, sizeof(struct rlimit));
810         rl.rlim_cur = 0;
811         (void) setrlimit(RLIMIT_CORE, &rl);
812     }
813 #endif /* RLIMIT_CORE */
814     debug_return;
815 }
816
817 #ifdef HAVE_PROJECT_H
818 static void
819 set_project(struct passwd *pw)
820 {
821     struct project proj;
822     char buf[PROJECT_BUFSZ];
823     int errval;
824     debug_decl(set_project, SUDO_DEBUG_UTIL)
825
826     /*
827      * Collect the default project for the user and settaskid
828      */
829     setprojent();
830     if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) {
831         errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL);
832         switch(errval) {
833         case 0:
834             break;
835         case SETPROJ_ERR_TASK:
836             switch (errno) {
837             case EAGAIN:
838                 warningx(_("resource control limit has been reached"));
839                 break;
840             case ESRCH:
841                 warningx(_("user \"%s\" is not a member of project \"%s\""),
842                     pw->pw_name, proj.pj_name);
843                 break;
844             case EACCES:
845                 warningx(_("the invoking task is final"));
846                 break;
847             default:
848                 warningx(_("could not join project \"%s\""), proj.pj_name);
849             }
850         case SETPROJ_ERR_POOL:
851             switch (errno) {
852             case EACCES:
853                 warningx(_("no resource pool accepting default bindings "
854                     "exists for project \"%s\""), proj.pj_name);
855                 break;
856             case ESRCH:
857                 warningx(_("specified resource pool does not exist for "
858                     "project \"%s\""), proj.pj_name);
859                 break;
860             default:
861                 warningx(_("could not bind to default resource pool for "
862                     "project \"%s\""), proj.pj_name);
863             }
864             break;
865         default:
866             if (errval <= 0) {
867                 warningx(_("setproject failed for project \"%s\""), proj.pj_name);
868             } else {
869                 warningx(_("warning, resource control assignment failed for "
870                     "project \"%s\""), proj.pj_name);
871             }
872         }
873     } else {
874         warning("getdefaultproj");
875     }
876     endprojent();
877     debug_return;
878 }
879 #endif /* HAVE_PROJECT_H */
880
881 /*
882  * Setup the execution environment immediately prior to the call to execve()
883  * Returns true on success and false on failure.
884  */
885 bool
886 exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
887 {
888     bool rval = false;
889     debug_decl(exec_setup, SUDO_DEBUG_EXEC)
890
891 #ifdef HAVE_SELINUX
892     if (ISSET(details->flags, CD_RBAC_ENABLED)) {
893         if (selinux_setup(details->selinux_role, details->selinux_type,
894             ptyname ? ptyname : user_details.tty, ptyfd) == -1)
895             goto done;
896     }
897 #endif
898
899     if (details->pw != NULL) {
900 #ifdef HAVE_PROJECT_H
901         set_project(details->pw);
902 #endif
903 #ifdef HAVE_GETUSERATTR
904         aix_prep_user(details->pw->pw_name, ptyname ? ptyname : user_details.tty);
905 #endif
906 #ifdef HAVE_LOGIN_CAP_H
907         if (details->login_class) {
908             int flags;
909             login_cap_t *lc;
910
911             /*
912              * We only use setusercontext() to set the nice value and rlimits
913              * unless this is a login shell (sudo -i).
914              */
915             lc = login_getclass((char *)details->login_class);
916             if (!lc) {
917                 warningx(_("unknown login class %s"), details->login_class);
918                 errno = ENOENT;
919                 goto done;
920             }
921             if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
922                 /* Set everything except user, group and login name. */
923                 flags = LOGIN_SETALL;
924                 CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER|LOGIN_SETENV|LOGIN_SETPATH);
925                 CLR(details->flags, CD_SET_UMASK); /* LOGIN_UMASK instead */
926             } else {
927                 flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
928             }
929             if (setusercontext(lc, details->pw, details->pw->pw_uid, flags)) {
930                 if (details->pw->pw_uid != ROOT_UID) {
931                     warning(_("unable to set user context"));
932                     goto done;
933                 } else
934                     warning(_("unable to set user context"));
935             }
936         }
937 #endif /* HAVE_LOGIN_CAP_H */
938     }
939
940     /*
941      * Set groups, including supplementary group vector.
942      */
943     if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
944         if (details->ngroups >= 0) {
945             if (sudo_setgroups(details->ngroups, details->groups) < 0) {
946                 warning(_("unable to set supplementary group IDs"));
947                 goto done;
948             }
949         }
950     }
951 #ifdef HAVE_SETEUID
952     if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
953         warning(_("unable to set effective gid to runas gid %u"),
954             (unsigned int)details->egid);
955         goto done;
956     }
957 #endif
958     if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
959         warning(_("unable to set gid to runas gid %u"),
960             (unsigned int)details->gid);
961         goto done;
962     }
963
964     if (ISSET(details->flags, CD_SET_PRIORITY)) {
965         if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
966             warning(_("unable to set process priority"));
967             goto done;
968         }
969     }
970     if (ISSET(details->flags, CD_SET_UMASK))
971         (void) umask(details->umask);
972     if (details->chroot) {
973         if (chroot(details->chroot) != 0 || chdir("/") != 0) {
974             warning(_("unable to change root to %s"), details->chroot);
975             goto done;
976         }
977     }
978
979 #ifdef HAVE_SETRESUID
980     if (setresuid(details->uid, details->euid, details->euid) != 0) {
981         warning(_("unable to change to runas uid (%u, %u)"), details->uid,
982             details->euid);
983         goto done;
984     }
985 #elif HAVE_SETREUID
986     if (setreuid(details->uid, details->euid) != 0) {
987         warning(_("unable to change to runas uid (%u, %u)"),
988             (unsigned int)details->uid, (unsigned int)details->euid);
989         goto done;
990     }
991 #else
992     if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
993         warning(_("unable to change to runas uid (%u, %u)"), details->uid,
994             details->euid);
995         goto done;
996     }
997 #endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
998
999     /*
1000      * Only change cwd if we have chroot()ed or the policy modules
1001      * specifies a different cwd.  Must be done after uid change.
1002      */
1003     if (details->cwd) {
1004         if (details->chroot || strcmp(details->cwd, user_details.cwd) != 0) {
1005             /* Note: cwd is relative to the new root, if any. */
1006             if (chdir(details->cwd) != 0) {
1007                 warning(_("unable to change directory to %s"), details->cwd);
1008                 goto done;
1009             }
1010         }
1011     }
1012
1013     /*
1014      * Restore nproc resource limit if pam_limits didn't do it for us.
1015      * We must do this *after* the uid change to avoid potential EAGAIN
1016      * from setuid().
1017      */
1018 #if defined(__linux__)
1019     {
1020         struct rlimit rl;
1021         if (getrlimit(RLIMIT_NPROC, &rl) == 0) {
1022             if (rl.rlim_cur == RLIM_INFINITY && rl.rlim_max == RLIM_INFINITY)
1023                 (void) setrlimit(RLIMIT_NPROC, &nproclimit);
1024         }
1025     }
1026 #endif
1027
1028     rval = true;
1029
1030 done:
1031     debug_return_bool(rval);
1032 }
1033
1034 /*
1035  * Run the command and wait for it to complete.
1036  */
1037 int
1038 run_command(struct command_details *details)
1039 {
1040     struct plugin_container *plugin;
1041     struct command_status cstat;
1042     int exitcode = 1;
1043     debug_decl(run_command, SUDO_DEBUG_EXEC)
1044
1045     cstat.type = CMD_INVALID;
1046     cstat.val = 0;
1047
1048     sudo_execute(details, &cstat);
1049
1050     switch (cstat.type) {
1051     case CMD_ERRNO:
1052         /* exec_setup() or execve() returned an error. */
1053         sudo_debug_printf(SUDO_DEBUG_DEBUG,
1054             "calling policy close with errno %d", cstat.val);
1055         policy_close(&policy_plugin, 0, cstat.val);
1056         tq_foreach_fwd(&io_plugins, plugin) {
1057             sudo_debug_printf(SUDO_DEBUG_DEBUG,
1058                 "calling I/O close with errno %d", cstat.val);
1059             iolog_close(plugin, 0, cstat.val);
1060         }
1061         exitcode = 1;
1062         break;
1063     case CMD_WSTATUS:
1064         /* Command ran, exited or was killed. */
1065         sudo_debug_printf(SUDO_DEBUG_DEBUG,
1066             "calling policy close with wait status %d", cstat.val);
1067         policy_close(&policy_plugin, cstat.val, 0);
1068         tq_foreach_fwd(&io_plugins, plugin) {
1069             sudo_debug_printf(SUDO_DEBUG_DEBUG,
1070                 "calling I/O close with wait status %d", cstat.val);
1071             iolog_close(plugin, cstat.val, 0);
1072         }
1073         if (WIFEXITED(cstat.val))
1074             exitcode = WEXITSTATUS(cstat.val);
1075         else if (WIFSIGNALED(cstat.val))
1076             exitcode = WTERMSIG(cstat.val) | 128;
1077         break;
1078     default:
1079         warningx(_("unexpected child termination condition: %d"), cstat.type);
1080         break;
1081     }
1082     debug_return_int(exitcode);
1083 }
1084
1085 static int
1086 policy_open(struct plugin_container *plugin, char * const settings[],
1087     char * const user_info[], char * const user_env[])
1088 {
1089     int rval;
1090     debug_decl(policy_open, SUDO_DEBUG_PCOMM)
1091
1092     /*
1093      * Backwards compatibility for older API versions
1094      */
1095     switch (plugin->u.generic->version) {
1096     case SUDO_API_MKVERSION(1, 0):
1097     case SUDO_API_MKVERSION(1, 1):
1098         rval = plugin->u.policy_1_0->open(plugin->u.io_1_0->version,
1099             sudo_conversation, _sudo_printf, settings, user_info, user_env);
1100         break;
1101     default:
1102         rval = plugin->u.policy->open(SUDO_API_VERSION, sudo_conversation,
1103             _sudo_printf, settings, user_info, user_env, plugin->options);
1104     }
1105
1106     debug_return_bool(rval);
1107 }
1108
1109 static void
1110 policy_close(struct plugin_container *plugin, int exit_status, int error)
1111 {
1112     debug_decl(policy_close, SUDO_DEBUG_PCOMM)
1113     plugin->u.policy->close(exit_status, error);
1114     debug_return;
1115 }
1116
1117 static int
1118 policy_show_version(struct plugin_container *plugin, int verbose)
1119 {
1120     debug_decl(policy_show_version, SUDO_DEBUG_PCOMM)
1121     debug_return_bool(plugin->u.policy->show_version(verbose));
1122 }
1123
1124 static int
1125 policy_check(struct plugin_container *plugin, int argc, char * const argv[],
1126     char *env_add[], char **command_info[], char **argv_out[],
1127     char **user_env_out[])
1128 {
1129     debug_decl(policy_check, SUDO_DEBUG_PCOMM)
1130     debug_return_bool(plugin->u.policy->check_policy(argc, argv, env_add,
1131         command_info, argv_out, user_env_out));
1132 }
1133
1134 static int
1135 policy_list(struct plugin_container *plugin, int argc, char * const argv[],
1136     int verbose, const char *list_user)
1137 {
1138     debug_decl(policy_list, SUDO_DEBUG_PCOMM)
1139     if (plugin->u.policy->list == NULL) {
1140         warningx(_("policy plugin %s does not support listing privileges"),
1141             plugin->name);
1142         debug_return_bool(false);
1143     }
1144     debug_return_bool(plugin->u.policy->list(argc, argv, verbose, list_user));
1145 }
1146
1147 static int
1148 policy_validate(struct plugin_container *plugin)
1149 {
1150     debug_decl(policy_validate, SUDO_DEBUG_PCOMM)
1151     if (plugin->u.policy->validate == NULL) {
1152         warningx(_("policy plugin %s does not support the -v option"),
1153             plugin->name);
1154         debug_return_bool(false);
1155     }
1156     debug_return_bool(plugin->u.policy->validate());
1157 }
1158
1159 static void
1160 policy_invalidate(struct plugin_container *plugin, int remove)
1161 {
1162     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
1163     if (plugin->u.policy->invalidate == NULL) {
1164         errorx(1, _("policy plugin %s does not support the -k/-K options"),
1165             plugin->name);
1166     }
1167     plugin->u.policy->invalidate(remove);
1168     debug_return;
1169 }
1170
1171 int
1172 policy_init_session(struct command_details *details)
1173 {
1174     int rval = true;
1175     debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
1176
1177     if (policy_plugin.u.policy->init_session) {
1178         /*
1179          * Backwards compatibility for older API versions
1180          */
1181         switch (policy_plugin.u.generic->version) {
1182         case SUDO_API_MKVERSION(1, 0):
1183         case SUDO_API_MKVERSION(1, 1):
1184             rval = policy_plugin.u.policy_1_0->init_session(details->pw);
1185             break;
1186         default:
1187             rval = policy_plugin.u.policy->init_session(details->pw,
1188                 &details->envp);
1189         }
1190     }
1191     debug_return_bool(rval);
1192 }
1193
1194 static int
1195 iolog_open(struct plugin_container *plugin, char * const settings[],
1196     char * const user_info[], char * const command_info[],
1197     int argc, char * const argv[], char * const user_env[])
1198 {
1199     int rval;
1200     debug_decl(iolog_open, SUDO_DEBUG_PCOMM)
1201
1202     /*
1203      * Backwards compatibility for older API versions
1204      */
1205     switch (plugin->u.generic->version) {
1206     case SUDO_API_MKVERSION(1, 0):
1207         rval = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
1208             sudo_conversation, _sudo_printf, settings, user_info, argc, argv,
1209             user_env);
1210         break;
1211     case SUDO_API_MKVERSION(1, 1):
1212         rval = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
1213             sudo_conversation, _sudo_printf, settings, user_info,
1214             command_info, argc, argv, user_env);
1215         break;
1216     default:
1217         rval = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1218             _sudo_printf, settings, user_info, command_info,
1219             argc, argv, user_env, plugin->options);
1220     }
1221     debug_return_bool(rval);
1222 }
1223
1224 static void
1225 iolog_close(struct plugin_container *plugin, int exit_status, int error)
1226 {
1227     debug_decl(iolog_close, SUDO_DEBUG_PCOMM)
1228     plugin->u.io->close(exit_status, error);
1229     debug_return;
1230 }
1231
1232 static int
1233 iolog_show_version(struct plugin_container *plugin, int verbose)
1234 {
1235     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
1236     debug_return_bool(plugin->u.io->show_version(verbose));
1237 }
1238
1239 /*
1240  * Remove the specified I/O logging plugin from the io_plugins list.
1241  * Deregisters any hooks before unlinking, then frees the container.
1242  */
1243 static void
1244 iolog_unlink(struct plugin_container *plugin)
1245 {
1246     debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
1247
1248     /* Deregister hooks, if any. */
1249     if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
1250         if (plugin->u.io->deregister_hooks != NULL)
1251             plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION,
1252                 deregister_hook);
1253     }
1254     /* Remove from io_plugins list and free. */
1255     tq_remove(&io_plugins, plugin);
1256     efree(plugin);
1257
1258     debug_return;
1259 }