staprun/ctl.c - systemtap

Functions defined

Macros defined

Source code

/* -*- linux-c -*-
*
* ctl.c - staprun control channel
*
* This file is part of systemtap, and is free software.  You can
* redistribute it and/or modify it under the terms of the GNU General
* Public License (GPL); either version 2, or (at your option) any
* later version.
*
* Copyright (C) 2012 Red Hat Inc.
*/

#include "staprun.h"

#define CTL_CHANNEL_NAME ".cmd"

int init_ctl_channel(const char *name, int verb)
{
    char buf[PATH_MAX];
    struct statfs st;
    int old_transport = 0;

        (void) verb;
        if (0) goto out; /* just to defeat gcc warnings */

#ifdef HAVE_OPENAT
        if (relay_basedir_fd >= 0) {
                strncpy(buf, CTL_CHANNEL_NAME, PATH_MAX);
                control_channel = openat(relay_basedir_fd, CTL_CHANNEL_NAME, O_RDWR);
                dbug(2, "Opened %s (%d)\n", CTL_CHANNEL_NAME, control_channel);

                /* NB: Extra real-id access check as below */
                if (faccessat(relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) != 0){
                        close(control_channel);
                        return -5;
                }
                if (control_channel >= 0)
                        goto out; /* It's OK to bypass the [f]access[at] check below,
                                     since this would only occur the *second* time
                                     staprun tries this gig, or within unprivileged stapio. */
        }
        /* PR14245, NB: we fall through to /sys ... /proc searching,
           in case the relay_basedir_fd option wasn't given (i.e., for
           early in staprun), or if errors out for some reason. */
#endif

    if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) {
                /* PR14245: allow subsequent operations, and if
                   necessary, staprun->stapio forks, to reuse an fd for
                   directory lookups (even if some parent directories have
                   perms 0700. */
#ifdef HAVE_OPENAT
                if (! sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s", name)) {
                        relay_basedir_fd = open (buf, O_DIRECTORY | O_RDONLY);
                        /* If this fails, we don't much care; the
                           negative return value will just keep us
                           looking up by name again next time. */
                        /* NB: we don't plan to close this fd, so that we can pass
                           it across staprun->stapio fork/execs. */
                }
#endif
        if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s/%s",
                                name, CTL_CHANNEL_NAME))
            return -1;
    } else {
        old_transport = 1;
        if (sprintf_chk(buf, "/proc/systemtap/%s/%s", name, CTL_CHANNEL_NAME))
            return -2;
    }

    control_channel = open(buf, O_RDWR);
    dbug(2, "Opened %s (%d)\n", buf, control_channel);

    /* NB: Even if open() succeeded with effective-UID permissions, we
     * need the access() check to make sure real-UID permissions are also
     * sufficient.  When we run under the setuid staprun, effective and
     * real UID may not be the same.  Specifically, we want to prevent
         * a local stapusr from trying to attach to a different stapusr's module.
     *
     * The access() is done *after* open() to avoid any TOCTOU-style race
     * condition.  We believe it's probably safe either way, as the file
     * we're trying to access connot be modified by a typical user, but
     * better safe than sorry.
     */
#ifdef HAVE_OPENAT
        if (control_channel >= 0 && relay_basedir_fd >= 0) {
                if (faccessat (relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) == 0)
                        goto out;
                /* else fall through */
        }
#endif
    if (control_channel >= 0 && access(buf, R_OK|W_OK) != 0) {
        close(control_channel);
        return -5;
    }

out:
    if (control_channel < 0) {
                err(_("Cannot attach to module %s control channel; not running?\n"),
                    name);
        return -3;
    }
    if (set_clexec(control_channel) < 0)
        return -4;

    return old_transport;
}

void close_ctl_channel(void)
{
  if (control_channel >= 0) {
              dbug(2, "Closed ctl fd %d\n", control_channel);
        close(control_channel);
        control_channel = -1;
    }
}