Annotation of embedaddon/sudo/plugins/sample/sample_plugin.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (c) 2010-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 <sys/param.h>
                     21: #include <sys/stat.h>
                     22: #include <sys/wait.h>
                     23: 
                     24: #include <stdio.h>
                     25: #ifdef STDC_HEADERS
                     26: # include <stdlib.h>
                     27: # include <stddef.h>
                     28: #else
                     29: # ifdef HAVE_STDLIB_H
                     30: #  include <stdlib.h>
                     31: # endif
                     32: #endif /* STDC_HEADERS */
                     33: #ifdef HAVE_STRING_H
                     34: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
                     35: #  include <memory.h>
                     36: # endif
                     37: # include <string.h>
                     38: #endif /* HAVE_STRING_H */
                     39: #ifdef HAVE_STRINGS_H
                     40: # include <strings.h>
                     41: #endif /* HAVE_STRINGS_H */
                     42: #ifdef HAVE_UNISTD_H
                     43: # include <unistd.h>
                     44: #endif /* HAVE_UNISTD_H */
                     45: #include <ctype.h>
                     46: #include <fcntl.h>
                     47: #include <limits.h>
                     48: #include <grp.h>
                     49: #include <pwd.h>
                     50: #include <stdarg.h>
                     51: 
                     52: #include <pathnames.h>
                     53: #include "sudo_plugin.h"
                     54: #include "missing.h"
                     55: 
                     56: /*
                     57:  * Sample plugin module that allows any user who knows the password
                     58:  * ("test") to run any command as root.  Since there is no credential
                     59:  * caching the validate and invalidate functions are NULL.
                     60:  */
                     61: 
                     62: #ifdef __TANDEM
                     63: # define ROOT_UID       65535
                     64: #else
                     65: # define ROOT_UID       0
                     66: #endif
                     67: 
                     68: #undef TRUE
                     69: #define TRUE 1
                     70: #undef FALSE
                     71: #define FALSE 0
                     72: #undef ERROR
                     73: #define ERROR -1
                     74: 
                     75: static struct plugin_state {
                     76:     char **envp;
                     77:     char * const *settings;
                     78:     char * const *user_info;
                     79: } plugin_state;
                     80: static sudo_conv_t sudo_conv;
                     81: static sudo_printf_t sudo_log;
                     82: static FILE *input, *output;
                     83: static uid_t runas_uid = ROOT_UID;
                     84: static gid_t runas_gid = -1;
                     85: static int use_sudoedit = FALSE;
                     86: 
                     87: /*
                     88:  * Allocate storage for a name=value string and return it.
                     89:  */
                     90: static char *
                     91: fmt_string(const char *var, const char *val)
                     92: {
                     93:     size_t var_len = strlen(var);
                     94:     size_t val_len = strlen(val);
                     95:     char *cp, *str;
                     96: 
                     97:     cp = str = malloc(var_len + 1 + val_len + 1);
                     98:     if (str != NULL) {
                     99:        memcpy(cp, var, var_len);
                    100:        cp += var_len;
                    101:        *cp++ = '=';
                    102:        memcpy(cp, val, val_len);
                    103:        cp += val_len;
                    104:        *cp = '\0';
                    105:     }
                    106: 
                    107:     return str;
                    108: }
                    109: 
                    110: /*
                    111:  * Plugin policy open function.
                    112:  */
                    113: static int
                    114: policy_open(unsigned int version, sudo_conv_t conversation,
                    115:     sudo_printf_t sudo_printf, char * const settings[],
                    116:     char * const user_info[], char * const user_env[])
                    117: {
                    118:     char * const *ui;
                    119:     struct passwd *pw;
                    120:     const char *runas_user = NULL;
                    121:     struct group *gr;
                    122:     const char *runas_group = NULL;
                    123: 
                    124:     if (!sudo_conv)
                    125:        sudo_conv = conversation;
                    126:     if (!sudo_log)
                    127:        sudo_log = sudo_printf;
                    128: 
                    129:     if (SUDO_API_VERSION_GET_MAJOR(version) != SUDO_API_VERSION_MAJOR) {
                    130:        sudo_log(SUDO_CONV_ERROR_MSG,
                    131:            "the sample plugin requires API version %d.x\n",
                    132:            SUDO_API_VERSION_MAJOR);
                    133:        return ERROR;
                    134:     }
                    135: 
                    136:     /* Only allow commands to be run as root. */
                    137:     for (ui = settings; *ui != NULL; ui++) {
                    138:        if (strncmp(*ui, "runas_user=", sizeof("runas_user=") - 1) == 0) {
                    139:            runas_user = *ui + sizeof("runas_user=") - 1;
                    140:        }
                    141:        if (strncmp(*ui, "runas_group=", sizeof("runas_group=") - 1) == 0) {
                    142:            runas_group = *ui + sizeof("runas_group=") - 1;
                    143:        }
                    144: #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
                    145:        if (strncmp(*ui, "progname=", sizeof("progname=") - 1) == 0) {
                    146:            setprogname(*ui + sizeof("progname=") - 1);
                    147:        }
                    148: #endif
                    149:        /* Check to see if sudo was called as sudoedit or with -e flag. */
                    150:        if (strncmp(*ui, "sudoedit=", sizeof("sudoedit=") - 1) == 0) {
                    151:            if (strcasecmp(*ui + sizeof("sudoedit=") - 1, "true") == 0)
                    152:                use_sudoedit = TRUE;
                    153:        }
                    154:        /* This plugin doesn't support running sudo with no arguments. */
                    155:        if (strncmp(*ui, "implied_shell=", sizeof("implied_shell=") - 1) == 0) {
                    156:            if (strcasecmp(*ui + sizeof("implied_shell=") - 1, "true") == 0)
                    157:                return -2; /* usage error */
                    158:        }
                    159:     }
                    160:     if (runas_user != NULL) {
                    161:        if ((pw = getpwnam(runas_user)) == NULL) {
                    162:            sudo_log(SUDO_CONV_ERROR_MSG, "unknown user %s\n", runas_user);
                    163:            return 0;
                    164:        }
                    165:        runas_uid = pw->pw_uid;
                    166:     }
                    167:     if (runas_group != NULL) {
                    168:        if ((gr = getgrnam(runas_group)) == NULL) {
                    169:            sudo_log(SUDO_CONV_ERROR_MSG, "unknown group %s\n", runas_group);
                    170:            return 0;
                    171:        }
                    172:        runas_gid = gr->gr_gid;
                    173:     }
                    174: 
                    175:     /* Plugin state. */
                    176:     plugin_state.envp = (char **)user_env;
                    177:     plugin_state.settings = settings;
                    178:     plugin_state.user_info = user_info;
                    179: 
                    180:     return 1;
                    181: }
                    182: 
                    183: static char *
                    184: find_in_path(char *command, char **envp)
                    185: {
                    186:     struct stat sb;
                    187:     char *path, *path0, **ep, *cp;
                    188:     char pathbuf[PATH_MAX], *qualified = NULL;
                    189: 
                    190:     if (strchr(command, '/') != NULL)
                    191:        return command;
                    192: 
                    193:     path = _PATH_DEFPATH;
                    194:     for (ep = plugin_state.envp; *ep != NULL; ep++) {
                    195:        if (strncmp(*ep, "PATH=", 5) == 0) {
                    196:            path = *ep + 5;
                    197:            break;
                    198:        }
                    199:     }
                    200:     path = path0 = strdup(path);
                    201:     do {
                    202:        if ((cp = strchr(path, ':')))
                    203:            *cp = '\0';
                    204:        snprintf(pathbuf, sizeof(pathbuf), "%s/%s", *path ? path : ".",
                    205:            command);
                    206:        if (stat(pathbuf, &sb) == 0) {
                    207:            if (S_ISREG(sb.st_mode) && (sb.st_mode & 0000111)) {
                    208:                qualified = pathbuf;
                    209:                break;
                    210:            }
                    211:        }
                    212:        path = cp + 1;
                    213:     } while (cp != NULL);
                    214:     free(path0);
                    215:     return qualified ? strdup(qualified) : NULL;
                    216: }
                    217: 
                    218: static int
                    219: check_passwd(void)
                    220: {
                    221:     struct sudo_conv_message msg;
                    222:     struct sudo_conv_reply repl;
                    223: 
                    224:     /* Prompt user for password via conversation function. */
                    225:     memset(&msg, 0, sizeof(msg));
                    226:     msg.msg_type = SUDO_CONV_PROMPT_ECHO_OFF;
                    227:     msg.msg = "Password: ";
                    228:     memset(&repl, 0, sizeof(repl));
                    229:     sudo_conv(1, &msg, &repl);
                    230:     if (repl.reply == NULL) {
                    231:        sudo_log(SUDO_CONV_ERROR_MSG, "missing password\n");
                    232:        return FALSE;
                    233:     }
                    234:     if (strcmp(repl.reply, "test") != 0) {
                    235:        sudo_log(SUDO_CONV_ERROR_MSG, "incorrect password\n");
                    236:        return FALSE;
                    237:     }
                    238:     return TRUE;
                    239: }
                    240: 
                    241: static char **
                    242: build_command_info(char *command)
                    243: {
                    244:     static char **command_info;
                    245:     int i = 0;
                    246: 
                    247:     /* Setup command info. */
                    248:     command_info = calloc(32, sizeof(char *));
                    249:     if (command_info == NULL)
                    250:        return NULL;
                    251:     if ((command_info[i++] = fmt_string("command", command)) == NULL ||
                    252:        asprintf(&command_info[i++], "runas_euid=%ld", (long)runas_uid) == -1 ||
                    253:        asprintf(&command_info[i++], "runas_uid=%ld", (long)runas_uid) == -1) {
                    254:        return NULL;
                    255:     }
                    256:     if (runas_gid != -1) {
                    257:        if (asprintf(&command_info[i++], "runas_gid=%ld", (long)runas_gid) == -1 ||
                    258:            asprintf(&command_info[i++], "runas_egid=%ld", (long)runas_gid) == -1) {
                    259:            return NULL;
                    260:        }
                    261:     }
                    262:     if (use_sudoedit) {
                    263:        command_info[i] = strdup("sudoedit=true");
                    264:        if (command_info[i++] == NULL)
                    265:                return NULL;
                    266:     }
                    267: #ifdef USE_TIMEOUT
                    268:     command_info[i++] = "timeout=30";
                    269: #endif
                    270:     return command_info;
                    271: }
                    272: 
                    273: static char *
                    274: find_editor(int nfiles, char * const files[], char **argv_out[])
                    275: {
                    276:     char *cp, **ep, **nargv, *editor, *editor_path;
                    277:     int ac, i, nargc, wasblank;
                    278: 
                    279:     /* Lookup EDITOR in user's environment. */
                    280:     editor = _PATH_VI;
                    281:     for (ep = plugin_state.envp; *ep != NULL; ep++) {
                    282:        if (strncmp(*ep, "EDITOR=", 7) == 0) {
                    283:            editor = *ep + 7;
                    284:            break;
                    285:        }
                    286:     }
                    287:     editor = strdup(editor);
                    288:     if (editor == NULL) {
                    289:        sudo_log(SUDO_CONV_ERROR_MSG, "unable to allocate memory\n");
                    290:        return NULL;
                    291:     }
                    292: 
                    293:     /*
                    294:      * Split editor into an argument vector; editor is reused (do not free).
                    295:      * The EDITOR environment variables may contain command
                    296:      * line args so look for those and alloc space for them too.
                    297:      */
                    298:     nargc = 1;
                    299:     for (wasblank = 0, cp = editor; *cp != '\0'; cp++) {
                    300:        if (isblank((unsigned char) *cp))
                    301:            wasblank = 1;
                    302:        else if (wasblank) {
                    303:            wasblank = 0;
                    304:            nargc++;
                    305:        }
                    306:     }
                    307:     /* If we can't find the editor in the user's PATH, give up. */
                    308:     cp = strtok(editor, " \t");
                    309:     if (cp == NULL ||
                    310:        (editor_path = find_in_path(editor, plugin_state.envp)) == NULL) {
                    311:        return NULL;
                    312:     }
                    313:     nargv = (char **) malloc((nargc + 1 + nfiles + 1) * sizeof(char *));
                    314:     if (nargv == NULL) {
                    315:        sudo_log(SUDO_CONV_ERROR_MSG, "unable to allocate memory\n");
                    316:        return NULL;
                    317:     }
                    318:     for (ac = 0; cp != NULL && ac < nargc; ac++) {
                    319:        nargv[ac] = cp;
                    320:        cp = strtok(NULL, " \t");
                    321:     }
                    322:     nargv[ac++] = "--";
                    323:     for (i = 0; i < nfiles; )
                    324:        nargv[ac++] = files[i++];
                    325:     nargv[ac] = NULL;
                    326: 
                    327:     *argv_out = nargv;
                    328:     return editor_path;
                    329: }
                    330: 
                    331: /*
                    332:  * Plugin policy check function.
                    333:  * Simple example that prompts for a password, hard-coded to "test".
                    334:  */
                    335: static int 
                    336: policy_check(int argc, char * const argv[],
                    337:     char *env_add[], char **command_info_out[],
                    338:     char **argv_out[], char **user_env_out[])
                    339: {
                    340:     char *command;
                    341: 
                    342:     if (!argc || argv[0] == NULL) {
                    343:        sudo_log(SUDO_CONV_ERROR_MSG, "no command specified\n");
                    344:        return FALSE;
                    345:     }
                    346: 
                    347:     if (!check_passwd())
                    348:        return FALSE;
                    349: 
                    350:     command = find_in_path(argv[0], plugin_state.envp);
                    351:     if (command == NULL) {
                    352:        sudo_log(SUDO_CONV_ERROR_MSG, "%s: command not found\n", argv[0]);
                    353:        return FALSE;
                    354:     }
                    355: 
                    356:     /* If "sudo vi" is run, auto-convert to sudoedit.  */
                    357:     if (strcmp(command, _PATH_VI) == 0)
                    358:        use_sudoedit = TRUE;
                    359: 
                    360:     if (use_sudoedit) {
                    361:        /* Rebuild argv using editor */
                    362:        command = find_editor(argc - 1, argv + 1, argv_out);
                    363:        if (command == NULL) {
                    364:            sudo_log(SUDO_CONV_ERROR_MSG, "unable to find valid editor\n");
                    365:            return ERROR;
                    366:        }
                    367:        use_sudoedit = TRUE;
                    368:     } else {
                    369:        /* No changes needd to argv */
                    370:        *argv_out = (char **)argv;
                    371:     }
                    372: 
                    373:     /* No changes to envp */
                    374:     *user_env_out = plugin_state.envp;
                    375: 
                    376:     /* Setup command info. */
                    377:     *command_info_out = build_command_info(command);
                    378:     if (*command_info_out == NULL) {
                    379:        sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n");
                    380:        return ERROR;
                    381:     }
                    382: 
                    383:     return TRUE;
                    384: }
                    385: 
                    386: static int
                    387: policy_list(int argc, char * const argv[], int verbose, const char *list_user)
                    388: {
                    389:     /*
                    390:      * List user's capabilities.
                    391:      */
                    392:     sudo_log(SUDO_CONV_INFO_MSG, "Validated users may run any command\n");
                    393:     return TRUE;
                    394: }
                    395: 
                    396: static int
                    397: policy_version(int verbose)
                    398: {
                    399:     sudo_log(SUDO_CONV_INFO_MSG, "Sample policy plugin version %s\n", PACKAGE_VERSION);
                    400:     return TRUE;
                    401: }
                    402: 
                    403: static void
                    404: policy_close(int exit_status, int error)
                    405: {
                    406:     /*
                    407:      * The policy might log the command exit status here.
                    408:      * In this example, we just print a message.
                    409:      */
                    410:     if (error) {
                    411:        sudo_log(SUDO_CONV_ERROR_MSG, "Command error: %s\n", strerror(error));
                    412:     } else {
                    413:         if (WIFEXITED(exit_status)) {
                    414:            sudo_log(SUDO_CONV_INFO_MSG, "Command exited with status %d\n",
                    415:                WEXITSTATUS(exit_status));
                    416:         } else if (WIFSIGNALED(exit_status)) {
                    417:            sudo_log(SUDO_CONV_INFO_MSG, "Command killed by signal %d\n",
                    418:                WTERMSIG(exit_status));
                    419:        }
                    420:     }
                    421: }
                    422: 
                    423: static int
                    424: io_open(unsigned int version, sudo_conv_t conversation,
                    425:     sudo_printf_t sudo_printf, char * const settings[],
                    426:     char * const user_info[], char * const command_info[],
                    427:     int argc, char * const argv[], char * const user_env[])
                    428: {
                    429:     int fd;
                    430:     char path[PATH_MAX];
                    431: 
                    432:     if (!sudo_conv)
                    433:        sudo_conv = conversation;
                    434:     if (!sudo_log)
                    435:        sudo_log = sudo_printf;
                    436: 
                    437:     /* Open input and output files. */
                    438:     snprintf(path, sizeof(path), "/var/tmp/sample-%u.output",
                    439:        (unsigned int)getpid());
                    440:     fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0644);
                    441:     if (fd == -1)
                    442:        return FALSE;
                    443:     output = fdopen(fd, "w");
                    444: 
                    445:     snprintf(path, sizeof(path), "/var/tmp/sample-%u.input",
                    446:        (unsigned int)getpid());
                    447:     fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0644);
                    448:     if (fd == -1)
                    449:        return FALSE;
                    450:     input = fdopen(fd, "w");
                    451: 
                    452:     return TRUE;
                    453: }
                    454: 
                    455: static void
                    456: io_close(int exit_status, int error)
                    457: {
                    458:     fclose(input);
                    459:     fclose(output);
                    460: }
                    461: 
                    462: static int
                    463: io_version(int verbose)
                    464: {
                    465:     sudo_log(SUDO_CONV_INFO_MSG, "Sample I/O plugin version %s\n",
                    466:        PACKAGE_VERSION);
                    467:     return TRUE;
                    468: }
                    469: 
                    470: static int
                    471: io_log_input(const char *buf, unsigned int len)
                    472: {
                    473:     fwrite(buf, len, 1, input);
                    474:     return TRUE;
                    475: }
                    476: 
                    477: static int
                    478: io_log_output(const char *buf, unsigned int len)
                    479: {
                    480:     fwrite(buf, len, 1, output);
                    481:     return TRUE;
                    482: }
                    483: 
                    484: struct policy_plugin sample_policy = {
                    485:     SUDO_POLICY_PLUGIN,
                    486:     SUDO_API_VERSION,
                    487:     policy_open,
                    488:     policy_close,
                    489:     policy_version,
                    490:     policy_check,
                    491:     policy_list,
                    492:     NULL, /* validate */
                    493:     NULL /* invalidate */
                    494: };
                    495: 
                    496: /*
                    497:  * Note: This plugin does not differentiate between tty and pipe I/O.
                    498:  *       It all gets logged to the same file.
                    499:  */
                    500: struct io_plugin sample_io = {
                    501:     SUDO_IO_PLUGIN,
                    502:     SUDO_API_VERSION,
                    503:     io_open,
                    504:     io_close,
                    505:     io_version,
                    506:     io_log_input,      /* tty input */
                    507:     io_log_output,     /* tty output */
                    508:     io_log_input,      /* command stdin if not tty */
                    509:     io_log_output,     /* command stdout if not tty */
                    510:     io_log_output      /* command stderr if not tty */
                    511: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>