Esempio n. 1
0
/*
 * This function attempts to revert the relabeling done to the tty.
 * fd		   - referencing the opened ttyn
 * ttyn		   - name of tty to restore
 *
 * Returns zero on success, non-zero otherwise
 */
int
selinux_restore_tty(void)
{
    int retval = 0;
    security_context_t chk_tty_context = NULL;
    debug_decl(selinux_restore_tty, SUDO_DEBUG_SELINUX)

    if (se_state.ttyfd == -1 || se_state.new_tty_context == NULL)
	goto skip_relabel;

    /* Verify that the tty still has the context set by sudo. */
    if ((retval = fgetfilecon(se_state.ttyfd, &chk_tty_context)) < 0) {
	sudo_warn(U_("unable to fgetfilecon %s"), se_state.ttyn);
	goto skip_relabel;
    }

    if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) {
	sudo_warnx(U_("%s changed labels"), se_state.ttyn);
	goto skip_relabel;
    }

    if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0)
	sudo_warn(U_("unable to restore context for %s"), se_state.ttyn);

skip_relabel:
    if (se_state.ttyfd != -1) {
	close(se_state.ttyfd);
	se_state.ttyfd = -1;
    }
    if (chk_tty_context != NULL) {
	freecon(chk_tty_context);
	chk_tty_context = NULL;
    }
    debug_return_int(retval);
}
Esempio n. 2
0
/* 
 * Set the exec and tty contexts in preparation for fork/exec.
 * Must run as root, before the uid change.
 * If ptyfd is not -1, it indicates we are running
 * in a pty and do not need to reset std{in,out,err}.
 * Returns 0 on success and -1 on failure.
 */
int
selinux_setup(const char *role, const char *type, const char *ttyn,
    int ptyfd)
{
    int ret = -1;
    debug_decl(selinux_setup, SUDO_DEBUG_SELINUX)

    /* Store the caller's SID in old_context. */
    if (getprevcon(&se_state.old_context)) {
	sudo_warn(U_("failed to get old_context"));
	goto done;
    }

    se_state.enforcing = security_getenforce();
    if (se_state.enforcing < 0) {
	sudo_warn(U_("unable to determine enforcing mode."));
	goto done;
    }

#ifdef DEBUG
    sudo_warnx("your old context was %s", se_state.old_context);
#endif
    se_state.new_context = get_exec_context(se_state.old_context, role, type);
    if (!se_state.new_context) {
#ifdef HAVE_LINUX_AUDIT
	audit_role_change(se_state.old_context, "?",
	  se_state.ttyn, 0);
#endif
	goto done;
    }
    
    if (relabel_tty(ttyn, ptyfd) < 0) {
	sudo_warn(U_("unable to set tty context to %s"), se_state.new_context);
	goto done;
    }

#ifdef DEBUG
    if (se_state.ttyfd != -1) {
	sudo_warnx("your old tty context is %s", se_state.tty_context);
	sudo_warnx("your new tty context is %s", se_state.new_tty_context);
    }
#endif

#ifdef HAVE_LINUX_AUDIT
    audit_role_change(se_state.old_context, se_state.new_context,
	se_state.ttyn, 1);
#endif

    ret = 0;

done:
    debug_return_int(ret);
}
Esempio n. 3
0
void
selinux_execve(const char *path, char *const argv[], char *const envp[],
    int noexec)
{
    char **nargv;
    const char *sesh;
    int argc, serrno;
    debug_decl(selinux_execve, SUDO_DEBUG_SELINUX)

    sesh = sudo_conf_sesh_path();
    if (sesh == NULL) {
	sudo_warnx("internal error: sesh path not set");
	errno = EINVAL;
	debug_return;
    }

    if (setexeccon(se_state.new_context)) {
	sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
	if (se_state.enforcing)
	    debug_return;
    }

#ifdef HAVE_SETKEYCREATECON
    if (setkeycreatecon(se_state.new_context)) {
	sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
	if (se_state.enforcing)
	    debug_return;
    }
#endif /* HAVE_SETKEYCREATECON */

    /*
     * Build new argv with sesh as argv[0].
     * If argv[0] ends in -noexec, sesh will disable execute
     * for the command it runs.
     */
    for (argc = 0; argv[argc] != NULL; argc++)
	continue;
    nargv = sudo_emallocarray(argc + 2, sizeof(char *));
    if (noexec)
	nargv[0] = *argv[0] == '-' ? "-sesh-noexec" : "sesh-noexec";
    else
	nargv[0] = *argv[0] == '-' ? "-sesh" : "sesh";
    nargv[1] = (char *)path;
    memcpy(&nargv[2], &argv[1], argc * sizeof(char *)); /* copies NULL */

    /* sesh will handle noexec for us. */
    sudo_execve(sesh, nargv, envp, false);
    serrno = errno;
    free(nargv);
    errno = serrno;
    debug_return;
}
Esempio n. 4
0
static int
audit_role_change(const security_context_t old_context,
    const security_context_t new_context, const char *ttyn, int result)
{
    int au_fd, rc = -1;
    char *message;
    debug_decl(audit_role_change, SUDO_DEBUG_SELINUX)

    au_fd = audit_open();
    if (au_fd == -1) {
        /* Kernel may not have audit support. */
        if (errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT
)
            sudo_fatal(U_("unable to open audit system"));
    } else {
	/* audit role change using the same format as newrole(1) */
	rc = asprintf(&message, "newrole: old-context=%s new-context=%s",
	    old_context, new_context);
	if (rc == -1)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	rc = audit_log_user_message(au_fd, AUDIT_USER_ROLE_CHANGE,
	    message, NULL, NULL, ttyn, result);
	if (rc <= 0)
	    sudo_warn(U_("unable to send audit message"));
	free(message);
	close(au_fd);
    }

    debug_return_int(rc);
}
Esempio n. 5
0
/*
 * Unlimit the number of processes since Linux's setuid() will
 * apply resource limits when changing uid and return EAGAIN if
 * nproc would be exceeded by the uid switch.
 */
static void
unlimit_nproc(void)
{
#ifdef __linux__
    struct rlimit rl;
    debug_decl(unlimit_nproc, SUDOERS_DEBUG_UTIL)

    if (getrlimit(RLIMIT_NPROC, &nproclimit) != 0)
	    sudo_warn("getrlimit");
    rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
    if (setrlimit(RLIMIT_NPROC, &rl) != 0) {
	rl.rlim_cur = rl.rlim_max = nproclimit.rlim_max;
	if (setrlimit(RLIMIT_NPROC, &rl) != 0)
	    sudo_warn("setrlimit");
    }
    debug_return;
#endif /* __linux__ */
}
Esempio n. 6
0
int
linux_audit_command(char *argv[], int result)
{
    int au_fd, rc = -1;
    char *command, *cp, **av;
    size_t size, n;
    debug_decl(linux_audit_command, SUDOERS_DEBUG_AUDIT)

    /* Don't return an error if auditing is not configured. */
    if ((au_fd = linux_audit_open()) < 0)
	debug_return_int(au_fd == AUDIT_NOT_CONFIGURED ? 0 : -1);

    /* Convert argv to a flat string. */
    for (size = 0, av = argv; *av != NULL; av++)
	size += strlen(*av) + 1;
    command = cp = sudo_emalloc(size);
    for (av = argv; *av != NULL; av++) {
	n = strlcpy(cp, *av, size - (cp - command));
	if (n >= size - (cp - command)) {
	    sudo_warnx(U_("internal error, %s overflow"), __func__);
	    goto done;
	}
	cp += n;
	*cp++ = ' ';
    }
    *--cp = '\0';

    /* Log command, ignoring ECONNREFUSED on error. */
    if (audit_log_user_command(au_fd, AUDIT_USER_CMD, command, NULL, result) <= 0) {
	if (errno != ECONNREFUSED) {
	    sudo_warn(U_("unable to send audit message"));
	    goto done;
	}
    }

    rc = 0;

done:
    sudo_efree(command);

    debug_return_int(rc);
}
Esempio n. 7
0
/*
 * Open audit connection if possible.
 * Returns audit fd on success and -1 on failure.
 */
static int
linux_audit_open(void)
{
    static int au_fd = -1;
    debug_decl(linux_audit_open, SUDOERS_DEBUG_AUDIT)

    if (au_fd != -1)
	debug_return_int(au_fd);
    au_fd = audit_open();
    if (au_fd == -1) {
	/* Kernel may not have audit support. */
	if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT)
	    au_fd = AUDIT_NOT_CONFIGURED;
	else
	    sudo_warn(U_("unable to open audit system"));
    } else {
	(void)fcntl(au_fd, F_SETFD, FD_CLOEXEC);
    }
    debug_return_int(au_fd);
}
Esempio n. 8
0
File: sesh.c Progetto: aixoss/sudo
/*
 * Exit codes defined in sudo_exec.h:
 *  SESH_SUCCESS (0)         ... successful operation
 *  SESH_ERR_FAILURE (1)     ... unspecified error
 *  SESH_ERR_INVALID (30)    ... invalid -e arg value
 *  SESH_ERR_BAD_PATHS (31)  ... odd number of paths
 *  SESH_ERR_NO_FILES (32)   ... copy error, no files copied
 *  SESH_ERR_SOME_FILES (33) ... copy error, no files copied
 */
int
main(int argc, char *argv[], char *envp[])
{
    int ret;
    debug_decl(main, SUDO_DEBUG_MAIN)

    initprogname(argc > 0 ? argv[0] : "sesh");

    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE_NAME, LOCALEDIR);
    textdomain(PACKAGE_NAME);

    if (argc < 2)
	sudo_fatalx(U_("requires at least one argument"));

    /* Read sudo.conf and initialize the debug subsystem. */
    if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
	exit(EXIT_FAILURE);
    sudo_debug_register(getprogname(), NULL, NULL,
	sudo_conf_debug_files(getprogname()));

    if (strcmp(argv[1], "-e") == 0) {
	ret = sesh_sudoedit(argc, argv);
    } else {
	bool login_shell, noexec = false;
	char *cp, *cmnd;
	int fd = -1;

	/* If the first char of argv[0] is '-', we are running a login shell. */
	login_shell = argv[0][0] == '-';

	/* If argv[0] ends in -noexec, pass the flag to sudo_execve() */
	if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0])
	    noexec = strcmp(cp, "-noexec") == 0;

	/* If argv[1] is --execfd=%d, extract the fd to exec with. */
	if (strncmp(argv[1], "--execfd=", 9) == 0) {
	    const char *errstr;

	    cp = argv[1] + 9;
	    fd = strtonum(cp, 0, INT_MAX, &errstr);
	    if (errstr != NULL)
		sudo_fatalx(U_("invalid file descriptor number: %s"), cp);
	    argv++;
	    argc--;
	}

	/* Shift argv and make a copy of the command to execute. */
	argv++;
	argc--;
	if ((cmnd = strdup(argv[0])) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));

	/* If invoked as a login shell, modify argv[0] accordingly. */
	if (login_shell) {
	    if ((cp = strrchr(argv[0], '/')) == NULL)
		sudo_fatal(U_("unable to run %s as a login shell"), argv[0]);
	    *cp = '-';
	    argv[0] = cp;
	}
	sudo_execve(fd, cmnd, argv, envp, noexec);
	sudo_warn(U_("unable to execute %s"), cmnd);
	ret = SESH_ERR_FAILURE;
    }
    sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, ret);
    _exit(ret);
}
Esempio n. 9
0
File: sesh.c Progetto: aixoss/sudo
static int
sesh_sudoedit(int argc, char *argv[])
{
    int i, oflags_dst, post, ret = SESH_ERR_FAILURE;
    int fd_src = -1, fd_dst = -1, follow = 0;
    ssize_t nread, nwritten;
    struct stat sb;
    struct timespec times[2];
    char buf[BUFSIZ];
    debug_decl(sesh_sudoedit, SUDO_DEBUG_EDIT)

    /* Check for -h flag (don't follow links). */
    if (strcmp(argv[2], "-h") == 0) {
	argv++;
	argc--;
	follow = O_NOFOLLOW;
    }

    if (argc < 3)
	debug_return_int(SESH_ERR_FAILURE);

    /*
     * We need to know whether we are performing the copy operation
     * before or after the editing. Without this we would not know
     * which files are temporary and which are the originals.
     *  post = 0 ... before
     *  post = 1 ... after
     */
    if (strcmp(argv[2], "0") == 0)
	post = 0;
    else if (strcmp(argv[2], "1") == 0)
	post = 1;
    else /* invalid value */
	debug_return_int(SESH_ERR_INVALID);

    /* Align argv & argc to the beggining of the file list. */
    argv += 3;
    argc -= 3;

    /* no files specified, nothing to do */
    if (argc == 0)
	debug_return_int(SESH_SUCCESS);
    /* odd number of paths specified */
    if (argc & 1)
	debug_return_int(SESH_ERR_BAD_PATHS);

    /*
     * Use O_EXCL if we are not in the post editing stage
     * so that it's ensured that the temporary files are
     * created by us and that we are not opening any symlinks.
     */
    oflags_dst = O_WRONLY|O_TRUNC|O_CREAT|(post ? follow : O_EXCL);
    for (i = 0; i < argc - 1; i += 2) {
	const char *path_src = argv[i];
	const char *path_dst = argv[i + 1];
	/*
	 * Try to open the source file for reading. If it
	 * doesn't exist, that's OK, we'll create an empty
	 * destination file.
	 */
	if ((fd_src = open(path_src, O_RDONLY|follow, 0600)) < 0) {
	    if (errno != ENOENT) {
		sudo_warn("%s", path_src);
		if (post) {
		    ret = SESH_ERR_SOME_FILES;
		    goto nocleanup;
		} else
		    goto cleanup_0;
	    }
	}

	if ((fd_dst = open(path_dst, oflags_dst, post ? 0644 : 0600)) < 0) {
	    /* error - cleanup */
	    sudo_warn("%s", path_dst);
	    if (post) {
		ret = SESH_ERR_SOME_FILES;
		goto nocleanup;
	    } else
		goto cleanup_0;
	}

	if (fd_src != -1) {
	    while ((nread = read(fd_src, buf, sizeof(buf))) > 0) {
		if ((nwritten = write(fd_dst, buf, nread)) != nread) {
		    sudo_warn("%s", path_src);
		    if (post) {
			ret = SESH_ERR_SOME_FILES;
			goto nocleanup;
		    } else
			goto cleanup_0;
		}
	    }
	}

	if (fd_dst != -1) {
	    if (!post) {
		if (fd_src == -1 || fstat(fd_src, &sb) != 0)
		    memset(&sb, 0, sizeof(sb));
		/* Make mtime on temp file match src. */
		mtim_get(&sb, times[0]);
		times[1].tv_sec = times[0].tv_sec;
		times[1].tv_nsec = times[0].tv_nsec;
		if (futimens(fd_dst, times) == -1) {
		    if (utimensat(AT_FDCWD, path_dst, times, 0) == -1)
			sudo_warn("%s", path_dst);
		}
	    }
	    close(fd_dst);
	}
	if (fd_src != -1)
	    close(fd_src);
	fd_dst = fd_src = -1;
    }

    ret = SESH_SUCCESS;
    if (post) {
	/* Remove temporary files (post=1) */
	for (i = 0; i < argc - 1; i += 2)
	    unlink(argv[i]);
    }
nocleanup:
    if (fd_dst != -1)
	close(fd_dst);
    if (fd_src != -1)
	close(fd_src);
    return(ret);
cleanup_0:
    /* Remove temporary files (post=0) */
    for (i = 0; i < argc - 1; i += 2)
	unlink(argv[i + 1]);
    if (fd_dst != -1)
	close(fd_dst);
    if (fd_src != -1)
	close(fd_src);
    return(SESH_ERR_NO_FILES);
}
Esempio n. 10
0
void
set_project(struct passwd *pw)
{
    struct project proj;
    char buf[PROJECT_BUFSZ];
    int errval;
    debug_decl(set_project, SUDO_DEBUG_UTIL)

    /*
     * Collect the default project for the user and settaskid
     */
    setprojent();
    if (getdefaultproj(pw->pw_name, &proj, buf, sizeof(buf)) != NULL) {
	errval = setproject(proj.pj_name, pw->pw_name, TASK_NORMAL);
	switch(errval) {
	case 0:
	    break;
	case SETPROJ_ERR_TASK:
	    switch (errno) {
	    case EAGAIN:
		sudo_warnx(U_("resource control limit has been reached"));
		break;
	    case ESRCH:
		sudo_warnx(U_("user \"%s\" is not a member of project \"%s\""),
		    pw->pw_name, proj.pj_name);
		break;
	    case EACCES:
		sudo_warnx(U_("the invoking task is final"));
		break;
	    default:
		sudo_warnx(U_("could not join project \"%s\""), proj.pj_name);
	    }
	case SETPROJ_ERR_POOL:
	    switch (errno) {
	    case EACCES:
		sudo_warnx(U_("no resource pool accepting default bindings "
		    "exists for project \"%s\""), proj.pj_name);
		break;
	    case ESRCH:
		sudo_warnx(U_("specified resource pool does not exist for "
		    "project \"%s\""), proj.pj_name);
		break;
	    default:
		sudo_warnx(U_("could not bind to default resource pool for "
		    "project \"%s\""), proj.pj_name);
	    }
	    break;
	default:
	    if (errval <= 0) {
		sudo_warnx(U_("setproject failed for project \"%s\""), proj.pj_name);
	    } else {
		sudo_warnx(U_("warning, resource control assignment failed for "
		    "project \"%s\""), proj.pj_name);
	    }
	}
    } else {
	sudo_warn("getdefaultproj");
    }
    endprojent();
    debug_return;
}
Esempio n. 11
0
/*
 * Returns a new security context based on the old context and the
 * specified role and type.
 */
security_context_t
get_exec_context(security_context_t old_context, const char *role, const char *type)
{
    security_context_t new_context = NULL;
    context_t context = NULL;
    char *typebuf = NULL;
    debug_decl(get_exec_context, SUDO_DEBUG_SELINUX)
    
    /* We must have a role, the type is optional (we can use the default). */
    if (!role) {
	sudo_warnx(U_("you must specify a role for type %s"), type);
	errno = EINVAL;
	goto bad;
    }
    if (!type) {
	if (get_default_type(role, &typebuf)) {
	    sudo_warnx(U_("unable to get default type for role %s"), role);
	    errno = EINVAL;
	    goto bad;
	}
	type = typebuf;
    }
    
    /* 
     * Expand old_context into a context_t so that we extract and modify 
     * its components easily. 
     */
    context = context_new(old_context);
    
    /*
     * Replace the role and type in "context" with the role and
     * type we will be running the command as.
     */
    if (context_role_set(context, role)) {
	sudo_warn(U_("failed to set new role %s"), role);
	goto bad;
    }
    if (context_type_set(context, type)) {
	sudo_warn(U_("failed to set new type %s"), type);
	goto bad;
    }
      
    /*
     * Convert "context" back into a string and verify it.
     */
    if ((new_context = strdup(context_str(context))) == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	goto bad;
    }
    if (security_check_context(new_context) < 0) {
	sudo_warnx(U_("%s is not a valid context"), new_context);
	errno = EINVAL;
	goto bad;
    }

#ifdef DEBUG
    sudo_warnx("Your new context is %s", new_context);
#endif

    context_free(context);
    debug_return_ptr(new_context);

bad:
    free(typebuf);
    context_free(context);
    freecon(new_context);
    debug_return_ptr(NULL);
}
Esempio n. 12
0
/*
 * This function attempts to relabel the tty. If this function fails, then
 * the contexts are free'd and -1 is returned. On success, 0 is returned
 * and tty_context and new_tty_context are set.
 *
 * This function will not fail if it can not relabel the tty when selinux is
 * in permissive mode.
 */
static int
relabel_tty(const char *ttyn, int ptyfd)
{
    security_context_t tty_con = NULL;
    security_context_t new_tty_con = NULL;
    struct stat sb;
    int fd;
    debug_decl(relabel_tty, SUDO_DEBUG_SELINUX)

    se_state.ttyfd = ptyfd;

    /* It is perfectly legal to have no tty. */
    if (ptyfd == -1 && ttyn == NULL)
	debug_return_int(0);

    /* If sudo is not allocating a pty for the command, open current tty. */
    if (ptyfd == -1) {
	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY|O_NONBLOCK);
	if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) {
	    sudo_warn(U_("unable to open %s, not relabeling tty"), ttyn);
	    goto bad;
	}
	if (!S_ISCHR(sb.st_mode)) {
	    sudo_warn(U_("%s is not a character device, not relabeling tty"),
		ttyn);
	    goto bad;
	}
	(void)fcntl(se_state.ttyfd, F_SETFL,
	    fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
    }

    if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) {
	sudo_warn(U_("unable to get current tty context, not relabeling tty"));
	goto bad;
    }

    if (tty_con) {
	security_class_t tclass = string_to_security_class("chr_file");
	if (tclass == 0) {
	    sudo_warn(U_("unknown security class \"chr_file\", not relabeling tty"));
	    goto bad;
	}
	if (security_compute_relabel(se_state.new_context, tty_con,
	    tclass, &new_tty_con) < 0) {
	    sudo_warn(U_("unable to get new tty context, not relabeling tty"));
	    goto bad;
	}
    }

    if (new_tty_con != NULL) {
	if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) {
	    sudo_warn(U_("unable to set new tty context"));
	    goto bad;
	}
    }

    if (ptyfd != -1) {
	/* Reopen pty that was relabeled, std{in,out,err} are reset later. */
	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0);
	if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) {
	    sudo_warn(U_("unable to open %s"), ttyn);
	    goto bad;
	}
	if (!S_ISCHR(sb.st_mode)) {
	    sudo_warn(U_("%s is not a character device, not relabeling tty"),
		ttyn);
	    goto bad;
	}
	if (dup2(se_state.ttyfd, ptyfd) == -1) {
	    sudo_warn("dup2");
	    goto bad;
	}
    } else {
	/* Re-open tty to get new label and reset std{in,out,err} */
	close(se_state.ttyfd);
	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY|O_NONBLOCK);
	if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) {
	    sudo_warn(U_("unable to open %s"), ttyn);
	    goto bad;
	}
	if (!S_ISCHR(sb.st_mode)) {
	    sudo_warn(U_("%s is not a character device, not relabeling tty"),
		ttyn);
	    goto bad;
	}
	(void)fcntl(se_state.ttyfd, F_SETFL,
	    fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
	for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
	    if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) {
		sudo_warn("dup2");
		goto bad;
	    }
	}
    }
    /* Retain se_state.ttyfd so we can restore label when command finishes. */
    (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC);

    se_state.ttyn = ttyn;
    se_state.tty_context = tty_con;
    se_state.new_tty_context = new_tty_con;
    debug_return_int(0);

bad:
    if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) {
	close(se_state.ttyfd);
	se_state.ttyfd = -1;
    }
    freecon(tty_con);
    debug_return_int(se_state.enforcing ? -1 : 0);
}
Esempio n. 13
0
struct log_info *
parse_logfile(const char *logfile)
{
    FILE *fp;
    char *buf = NULL, *cp, *ep;
    const char *errstr;
    size_t bufsize = 0, cwdsize = 0, cmdsize = 0;
    struct log_info *li = NULL;
    debug_decl(parse_logfile, SUDO_DEBUG_UTIL)

    fp = fopen(logfile, "r");
    if (fp == NULL) {
	sudo_warn(U_("unable to open %s"), logfile);
	goto bad;
    }

    /*
     * ID file has three lines:
     *  1) a log info line
     *  2) cwd
     *  3) command with args
     */
    if ((li = calloc(1, sizeof(*li))) == NULL)
	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    if (getdelim(&buf, &bufsize, '\n', fp) == -1 ||
	getdelim(&li->cwd, &cwdsize, '\n', fp) == -1 ||
	getdelim(&li->cmd, &cmdsize, '\n', fp) == -1) {
	sudo_warn(U_("%s: invalid log file"), logfile);
	goto bad;
    }

    /* Strip the newline from the cwd and command. */
    li->cwd[strcspn(li->cwd, "\n")] = '\0';
    li->cmd[strcspn(li->cmd, "\n")] = '\0';

    /*
     * Crack the log line (rows and cols not present in old versions).
     *	timestamp:user:runas_user:runas_group:tty:rows:cols
     * XXX - probably better to use strtok and switch on the state.
     */
    buf[strcspn(buf, "\n")] = '\0';
    cp = buf;

    /* timestamp */
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: time stamp field is missing"), logfile);
	goto bad;
    }
    *ep = '\0';
    li->tstamp = strtonum(cp, 0, TIME_T_MAX, &errstr);
    if (errstr != NULL) {
	sudo_warn(U_("%s: time stamp %s: %s"), logfile, cp, errstr);
	goto bad;
    }

    /* user */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: user field is missing"), logfile);
	goto bad;
    }
    if ((li->user = strndup(cp, (size_t)(ep - cp))) == NULL)
	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));

    /* runas user */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: runas user field is missing"), logfile);
	goto bad;
    }
    if ((li->runas_user = strndup(cp, (size_t)(ep - cp))) == NULL)
	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));

    /* runas group */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	sudo_warn(U_("%s: runas group field is missing"), logfile);
	goto bad;
    }
    if (cp != ep) {
	if ((li->runas_group = strndup(cp, (size_t)(ep - cp))) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    }

    /* tty, followed by optional rows + columns */
    cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL) {
	/* just the tty */
	if ((li->tty = strdup(cp)) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
    } else {
	/* tty followed by rows + columns */
	if ((li->tty = strndup(cp, (size_t)(ep - cp))) == NULL)
	    sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	cp = ep + 1;
	/* need to NULL out separator to use strtonum() */
	if ((ep = strchr(cp, ':')) != NULL) {
	    *ep = '\0';
	}
	li->rows = strtonum(cp, 1, INT_MAX, &errstr);
	if (errstr != NULL) {
	    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
		"%s: tty rows %s: %s", logfile, cp, errstr);
	}
	if (ep != NULL) {
	    cp = ep + 1;
	    li->cols = strtonum(cp, 1, INT_MAX, &errstr);
	    if (errstr != NULL) {
		sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
		    "%s: tty cols %s: %s", logfile, cp, errstr);
	    }
	}
    }
    fclose(fp);
    free(buf);
    debug_return_ptr(li);

bad:
    if (fp != NULL)
	fclose(fp);
    free(buf);
    free_log_info(li);
    debug_return_ptr(NULL);
}