28 Oct 2007

Is there a safer way to use system()?

Many security guidelines tell people not to use system() and similar functions, because the command is passed wholesale to the shell, and if the shell string isn't escaped properly, then you have all sorts of security problems: attackers can insert redirections to files (and file descriptors) they wouldn't otherwise have access to, and insert arbitrary commands by using ;, &, &&, ||, backticks, etc.

Yet, at the same time, system() is convenient: you don't have to build up your argument array by hand, you don't have to perform token parsing, variable substitution, or tilde expansion, and you don't have to do all the forking work. So, really, you just want all the conveniences of system() without the security risks.

Well, here's something that just might do the job, in certain instances where allowing users to specify command-line arguments is useful. Rather than passing the string to the shell, it uses wordexp() to do the parsing/substitution/expansion, which rejects all the unescaped shell metacharacters and backtick usages. I need to do more testing to be sure, but I am of the opinion that it's somewhat safer to use than system().

My code is based on the implementation of system() provided in the Single Unix Specification, and I release my modifications into the public domain.


#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <wordexp.h>

int
safer_system(const char *command)
{
    int result;
    struct sigaction sa_new, sa_oldintr, sa_oldquit;
    sigset_t ss_newblock, ss_oldblock;
    pid_t pid;
    wordexp_t we = {};

    if (!command)
        return 1;

    sa_new.sa_handler = SIG_IGN;
    sigemptyset(&sa_new.sa_mask);
    sa_new.sa_flags = 0;
    sigaction(SIGINT, &sa_new, &sa_oldintr);
    sigaction(SIGQUIT, &sa_new, &sa_oldquit);

    sigemptyset(&ss_newblock);
    sigaddset(&ss_newblock, SIGCHLD);
    sigprocmask(SIG_BLOCK, &ss_newblock, &ss_oldblock);

    switch (pid = fork()) {
    case -1:
        result = -1;
        break;

    case 0:
        sigaction(SIGINT, &sa_oldintr, NULL);
        sigaction(SIGQUIT, &sa_oldquit, NULL);
        sigprocmask(SIG_SETMASK, &ss_oldblock, NULL);
        if (wordexp(command, &we, WRDE_NOCMD)) {
            errno = EINVAL;
            return -1;
        }
        execvp(*we.we_wordv, we.we_wordv);
        _exit(127);
        /* NOTREACHED */

    default:
        while (waitpid(pid, &result, 0) == -1) {
            if (errno != EINTR) {
                result = -1;
                break;
            }
        }
    }

    sigaction(SIGINT, &sa_oldintr, NULL);
    sigaction(SIGQUIT, &sa_oldquit, NULL);
    sigprocmask(SIG_SETMASK, &ss_oldblock, NULL);
    return result;
}

My original version did the wordexp() call in the parent process, but this then expands $$ to the wrong process ID. There may be other bugs in this approach that I have yet to discover; as mentioned, I haven't done very much testing with it yet.

On quality relationships, part 1

Many thanks to my friend Nathan whose Final nail article inspired this one.

I totally identify with Nathan's article, because it really upsets me when people try to do too much on a first date. I have had similar experiences (which I won't go into specifics about, both out of respect for those involved, and to protect my privacy and theirs), and I, too, chose to back off.

I chose to back off because I feel that when someone tries to be physical with me before knowing me personally and emotionally, before we establish a meaningful relationship, I worry about the possibility that emotional compatibility is less important to them than physical compatibility. This is doubly so because I'm an introvert (so I actively hide my real self), and thus it takes a lot of effort to know who I am, something that many people I know have difficulty fully understanding. I am a very different person in my own space, much harder to get along with than the social personae (yes, plural) that people usually know me by.

Now, I'm not saying that physical compatibility is not important: sex and physical affection are vital components of a good relationship, but I feel that it cannot be built primarily upon physical aspects. Honest, open communication, which I think too many couples have trouble dealing with, is much more critical (pun intended), for without it, a long-lasting relationship is almost impossible. We each are all too different to begin with; without a solid way to bridge these gaps, what hope have we got?

That's not to say that I'm perfect at communicating. Far, far from it; I still have difficulty dealing with blunt truth, and I dare say I still verbally tiptoe around my spouse too much for my liking. But I do commit to putting in the hard work needed to improve on it, and it is the top priority in my marriage.

Imagine, if you were to share your body, your intimate space, and your heart, with somebody whom, yes, you do share a strong attraction with, but whom you also know you cannot have a long-lasting and meaningful relationship with, because something much more important is in the way, like being unable to talk openly with each other. I can't speak for you, but that'd break my heart more than I want to think about.