summaryrefslogtreecommitdiffstats
path: root/runtime/staprun
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/staprun')
-rw-r--r--runtime/staprun/common.c123
-rw-r--r--runtime/staprun/mainloop.c70
-rw-r--r--runtime/staprun/modverify.c391
-rw-r--r--runtime/staprun/modverify.h9
-rw-r--r--runtime/staprun/relay.c149
-rw-r--r--runtime/staprun/relay_old.c138
-rw-r--r--runtime/staprun/staprun.h29
-rw-r--r--runtime/staprun/staprun_funcs.c190
8 files changed, 999 insertions, 100 deletions
diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c
index fd16b4b8..c67ce340 100644
--- a/runtime/staprun/common.c
+++ b/runtime/staprun/common.c
@@ -27,6 +27,9 @@ int attach_mod;
int delete_mod;
int load_only;
int need_uprobes;
+int daemon_mode;
+off_t fsize_max;
+int fnum_max;
/* module variables */
char *modname = NULL;
@@ -35,9 +38,38 @@ char *modoptions[MAXMODOPTIONS];
int control_channel = -1; /* NB: fd==0 possible */
+static char path_buf[PATH_MAX];
+static char *get_abspath(char *path)
+{
+ int len;
+ if (path[0] == '/')
+ return path;
+
+ len = strlen(getcwd(path_buf, PATH_MAX));
+ if (len + 2 + strlen(path) >= PATH_MAX)
+ return NULL;
+ path_buf[len] = '/';
+ strcpy(&path_buf[len + 1], path);
+ return path_buf;
+}
+
+int stap_strfloctime(char *buf, size_t max, const char *fmt, time_t t)
+{
+ struct tm tm;
+ size_t ret;
+ if (buf == NULL || fmt == NULL || max <= 1)
+ return -EINVAL;
+ localtime_r(&t, &tm);
+ ret = strftime(buf, max, fmt, &tm);
+ if (ret == 0)
+ return -EINVAL;
+ return (int)ret;
+}
+
void parse_args(int argc, char **argv)
{
int c;
+ char *s;
/* Initialize option variables. */
verbose = 0;
@@ -49,8 +81,11 @@ void parse_args(int argc, char **argv)
delete_mod = 0;
load_only = 0;
need_uprobes = 0;
+ daemon_mode = 0;
+ fsize_max = 0;
+ fnum_max = 0;
- while ((c = getopt(argc, argv, "ALuvb:t:dc:o:x:")) != EOF) {
+ while ((c = getopt(argc, argv, "ALuvb:t:dc:o:x:S:D")) != EOF) {
switch (c) {
case 'u':
need_uprobes = 1;
@@ -85,11 +120,38 @@ void parse_args(int argc, char **argv)
case 'L':
load_only = 1;
break;
+ case 'D':
+ daemon_mode = 1;
+ break;
+ case 'S':
+ fsize_max = strtoul(optarg, &s, 10);
+ fsize_max <<= 20;
+ if (s[0] == ',')
+ fnum_max = (int)strtoul(&s[1], &s, 10);
+ if (s[0] != '\0') {
+ err("Invalid file size option '%s'.\n", optarg);
+ usage(argv[0]);
+ }
+ break;
default:
usage(argv[0]);
}
}
-
+ if (outfile_name) {
+ char tmp[PATH_MAX];
+ int ret;
+ outfile_name = get_abspath(outfile_name);
+ if (outfile_name == NULL) {
+ err("File name is too long.\n");
+ usage(argv[0]);
+ }
+ ret = stap_strfloctime(tmp, PATH_MAX - 18, /* = _cpuNNN.SSSSSSSSSS */
+ outfile_name, time(NULL));
+ if (ret < 0) {
+ err("Filename format is invalid or too long.\n");
+ usage(argv[0]);
+ }
+ }
if (attach_mod && load_only) {
err("You can't specify the '-A' and '-L' options together.\n");
usage(argv[0]);
@@ -118,18 +180,40 @@ void parse_args(int argc, char **argv)
err("You can't specify the '-c' and '-x' options together.\n");
usage(argv[0]);
}
+
+ if (daemon_mode && load_only) {
+ err("You can't specify the '-D' and '-L' options together.\n");
+ usage(argv[0]);
+ }
+ if (daemon_mode && delete_mod) {
+ err("You can't specify the '-D' and '-d' options together.\n");
+ usage(argv[0]);
+ }
+ if (daemon_mode && target_cmd) {
+ err("You can't specify the '-D' and '-c' options together.\n");
+ usage(argv[0]);
+ }
+ if (daemon_mode && outfile_name == NULL) {
+ err("You have to specify output FILE with '-D' option.\n");
+ usage(argv[0]);
+ }
+ if (outfile_name == NULL && fsize_max != 0) {
+ err("You have to specify output FILE with '-S' option.\n");
+ usage(argv[0]);
+ }
}
void usage(char *prog)
{
- err("\n%s [-v] [-c cmd ] [-x pid] [-u user]\n"
- "\t[-A|-L] [-b bufsize] [-o FILE] MODULE [module-options]\n", prog);
+ err("\n%s [-v] [-c cmd ] [-x pid] [-u user] [-A|-L|-d]\n"
+ "\t[-b bufsize] [-o FILE [-D] [-S size[,N]]] MODULE [module-options]\n", prog);
err("-v Increase verbosity.\n");
err("-c cmd Command \'cmd\' will be run and staprun will\n");
err(" exit when it does. The '_stp_target' variable\n");
err(" will contain the pid for the command.\n");
err("-x pid Sets the '_stp_target' variable to pid.\n");
- err("-o FILE Send output to FILE.\n");
+ err("-o FILE Send output to FILE. This supports strftime(3)\n");
+ err(" formats for FILE.\n");
err("-b buffer size The systemtap module specifies a buffer size.\n");
err(" Setting one here will override that value. The\n");
err(" value should be an integer between 1 and 4095 \n");
@@ -140,6 +224,14 @@ void usage(char *prog)
err("-d Delete a module. Only detached or unused modules\n");
err(" the user has permission to access will be deleted. Use \"*\"\n");
err(" (quoted) to delete all unused modules.\n");
+ err("-D Run in background. This requires '-o' option.\n");
+ err("-S size[,N] Switches output file to next file when the size\n");
+ err(" of file reaches the specified size. The value\n");
+ err(" should be an integer greater than 1 which is\n");
+ err(" assumed to be the maximum file size in MB.\n");
+ err(" When the number of output files reaches N, it\n");
+ err(" switches to the first output file. You can omit\n");
+ err(" the second argument.\n");
err("MODULE can be either a module name or a module path. If a\n");
err("module name is used, it is looked for in the following\n");
err("directory: /lib/modules/`uname -r`/systemtap\n");
@@ -344,3 +436,24 @@ int send_request(int type, void *data, int len)
if (rc < 0) return rc;
return (rc != len+4);
}
+
+#include <stdarg.h>
+
+static int use_syslog = 0;
+
+void eprintf(const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ if (use_syslog)
+ vsyslog(LOG_ERR, fmt, va);
+ else
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+}
+
+void switch_syslog(const char *name)
+{
+ openlog(name, LOG_PID, LOG_DAEMON);
+ use_syslog = 1;
+}
diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c
index 0745f611..7125a7bb 100644
--- a/runtime/staprun/mainloop.c
+++ b/runtime/staprun/mainloop.c
@@ -7,7 +7,7 @@
* Public License (GPL); either version 2, or (at your option) any
* later version.
*
- * Copyright (C) 2005-2008 Red Hat Inc.
+ * Copyright (C) 2005-2009 Red Hat Inc.
*/
#include "staprun.h"
@@ -318,6 +318,41 @@ int init_stapio(void)
if (target_cmd)
start_cmd();
+ /* Run in background */
+ if (daemon_mode) {
+ pid_t pid;
+ int ret;
+ dbug(2, "daemonizing stapio\n");
+
+ /* daemonize */
+ ret = daemon(0, 1); /* don't close stdout at this time. */
+ if (ret) {
+ err("Failed to daemonize stapio\n");
+ return -1;
+ }
+
+ /* change error messages to syslog. */
+ switch_syslog("stapio");
+
+ /* show new pid */
+ pid = getpid();
+ fprintf(stdout, "%d\n", pid);
+ fflush(stdout);
+
+ /* redirect all outputs to /dev/null */
+ ret = open("/dev/null", O_RDWR);
+ if (ret < 0) {
+ err("Failed to open /dev/null\n");
+ return -1;
+ }
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ dup2(ret, STDOUT_FILENO);
+ dup2(ret, STDERR_FILENO);
+ close(ret);
+ }
+
return 0;
}
@@ -360,10 +395,10 @@ void cleanup_and_exit(int detach)
#define BUG9788_WORKAROUND
#ifndef BUG9788_WORKAROUND
dbug(2, "removing %s\n", modname);
- if (execlp(staprun, basename (staprun), "-d", modname, NULL) < 0) {
+ if (execlp(staprun, basename (staprun), "-d", modpath, NULL) < 0) {
if (errno == ENOEXEC) {
char *cmd;
- if (asprintf(&cmd, "%s -d '%s'", staprun, modname) > 0)
+ if (asprintf(&cmd, "%s -d '%s'", staprun, modpath) > 0)
execl("/bin/sh", "sh", "-c", cmd, NULL);
free(cmd);
}
@@ -392,10 +427,10 @@ void cleanup_and_exit(int detach)
if (pid == 0) { /* child process */
/* Run the command. */
- if (execlp(staprun, basename (staprun), "-d", modname, NULL) < 0) {
+ if (execlp(staprun, basename (staprun), "-d", modpath, NULL) < 0) {
if (errno == ENOEXEC) {
char *cmd;
- if (asprintf(&cmd, "%s -d '%s'", staprun, modname) > 0)
+ if (asprintf(&cmd, "%s -d '%s'", staprun, modpath) > 0)
execl("/bin/sh", "sh", "-c", cmd, NULL);
free(cmd);
}
@@ -454,21 +489,14 @@ int stp_main_loop(void)
switch (type) {
#if STP_TRANSPORT_VERSION == 1
case STP_REALTIME_DATA:
- {
- ssize_t bw = write(out_fd[0], data, nb);
- if (bw >= 0 && bw != nb) {
- nb = nb - bw;
- bw = write(out_fd[0], data, nb);
- }
- if (bw != nb) {
- _perr("write error (nb=%ld)", (long)nb);
- cleanup_and_exit(0);
- }
- break;
+ if (write_realtime_data(data, nb)) {
+ _perr("write error (nb=%ld)", (long)nb);
+ cleanup_and_exit(0);
}
+ break;
#endif
case STP_OOB_DATA:
- fputs((char *)data, stderr);
+ eprintf("%s", (char *)data);
break;
case STP_EXIT:
{
@@ -477,6 +505,14 @@ int stp_main_loop(void)
cleanup_and_exit(0);
break;
}
+ case STP_REQUEST_EXIT:
+ {
+ /* module asks us to start exiting, so send STP_EXIT */
+ dbug(2, "got STP_REQUEST_EXIT\n");
+ int32_t rc, btype = STP_EXIT;
+ rc = write(control_channel, &btype, sizeof(btype));
+ break;
+ }
case STP_START:
{
struct _stp_msg_start *t = (struct _stp_msg_start *)data;
diff --git a/runtime/staprun/modverify.c b/runtime/staprun/modverify.c
new file mode 100644
index 00000000..b50a69f4
--- /dev/null
+++ b/runtime/staprun/modverify.c
@@ -0,0 +1,391 @@
+/*
+ This program verifies the given file using the given signature, the named
+ certificate and public key in the given certificate database.
+
+ Copyright (C) 2009 Red Hat Inc.
+
+ 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 as published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+
+#include <nspr.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <cryptohi.h>
+#include <cert.h>
+#include <certt.h>
+
+#include "nsscommon.h"
+#include "modverify.h"
+
+#include <sys/stat.h>
+
+/* Function: int check_cert_db_permissions (const char *cert_db_path);
+ *
+ * Check that the given certificate directory and its contents have
+ * the correct permissions.
+ *
+ * Returns 0 if there is an error, 1 otherwise.
+ */
+static int
+check_db_file_permissions (const char *cert_db_file) {
+ struct stat info;
+ int rc;
+
+ rc = stat (cert_db_file, & info);
+ if (rc)
+ {
+ fprintf (stderr, "Could not obtain information on certificate database file %s.\n",
+ cert_db_file);
+ perror ("");
+ return 0;
+ }
+
+ rc = 1; /* ok */
+
+ /* The owner of the file must be root. */
+ if (info.st_uid != 0)
+ {
+ fprintf (stderr, "Certificate database file %s must be owned by root.\n",
+ cert_db_file);
+ rc = 0;
+ }
+
+ /* Check the access permissions of the file. */
+ if ((info.st_mode & S_IRUSR) == 0)
+ fprintf (stderr, "Certificate database file %s should be readable by the owner.\n", cert_db_file);
+ if ((info.st_mode & S_IWUSR) == 0)
+ fprintf (stderr, "Certificate database file %s should be writeable by the owner.\n", cert_db_file);
+ if ((info.st_mode & S_IXUSR) != 0)
+ {
+ fprintf (stderr, "Certificate database file %s must not be executable by the owner.\n", cert_db_file);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IRGRP) == 0)
+ {
+ fprintf (stderr, "Certificate database file %s should be readable by the group.\n", cert_db_file);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IWGRP) != 0)
+ {
+ fprintf (stderr, "Certificate database file %s must not be writable by the group.\n", cert_db_file);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IXGRP) != 0)
+ {
+ fprintf (stderr, "Certificate database file %s must not be executable by the group.\n", cert_db_file);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IROTH) == 0)
+ {
+ fprintf (stderr, "Certificate database file %s should be readable by others.\n", cert_db_file);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IWOTH) != 0)
+ {
+ fprintf (stderr, "Certificate database file %s must not be writable by others.\n", cert_db_file);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IXOTH) != 0)
+ {
+ fprintf (stderr, "Certificate database file %s must not be executable by others.\n", cert_db_file);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* Function: int check_cert_db_permissions (const char *cert_db_path);
+ *
+ * Check that the given certificate directory and its contents have
+ * the correct permissions.
+ *
+ * Returns 0 if there is an error, 1 otherwise.
+ */
+static int
+check_cert_db_permissions (const char *cert_db_path) {
+ struct stat info;
+ char *fileName;
+ int rc;
+
+ rc = stat (cert_db_path, & info);
+ if (rc)
+ {
+ fprintf (stderr, "Could not obtain information on certificate database directory %s.\n",
+ cert_db_path);
+ perror ("");
+ return 0;
+ }
+
+ rc = 1; /* ok */
+
+ /* The owner of the database must be root. */
+ if (info.st_uid != 0)
+ {
+ fprintf (stderr, "Certificate database directory %s must be owned by root.\n", cert_db_path);
+ rc = 0;
+ }
+
+ /* Check the database directory access permissions */
+ if ((info.st_mode & S_IRUSR) == 0)
+ fprintf (stderr, "Certificate database %s should be readable by the owner.\n", cert_db_path);
+ if ((info.st_mode & S_IWUSR) == 0)
+ fprintf (stderr, "Certificate database %s should be writeable by the owner.\n", cert_db_path);
+ if ((info.st_mode & S_IXUSR) == 0)
+ fprintf (stderr, "Certificate database %s should be searchable by the owner.\n", cert_db_path);
+ if ((info.st_mode & S_IRGRP) == 0)
+ fprintf (stderr, "Certificate database %s should be readable by the group.\n", cert_db_path);
+ if ((info.st_mode & S_IWGRP) != 0)
+ {
+ fprintf (stderr, "Certificate database %s must not be writable by the group.\n", cert_db_path);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IXGRP) == 0)
+ fprintf (stderr, "Certificate database %s should be searchable by the group.\n", cert_db_path);
+ if ((info.st_mode & S_IROTH) == 0)
+ fprintf (stderr, "Certificate database %s should be readable by others.\n", cert_db_path);
+ if ((info.st_mode & S_IWOTH) != 0)
+ {
+ fprintf (stderr, "Certificate database %s must not be writable by others.\n", cert_db_path);
+ rc = 0;
+ }
+ if ((info.st_mode & S_IXOTH) == 0)
+ fprintf (stderr, "Certificate database %s should be searchable by others.\n", cert_db_path);
+
+ /* Now check the permissions of the critical files. */
+ fileName = PORT_Alloc (strlen (cert_db_path) + 11);
+ if (! fileName)
+ {
+ fprintf (stderr, "Unable to allocate memory for certificate database file names\n");
+ return 0;
+ }
+
+ sprintf (fileName, "%s/cert8.db", cert_db_path);
+ rc &= check_db_file_permissions (fileName);
+ sprintf (fileName, "%s/key3.db", cert_db_path);
+ rc &= check_db_file_permissions (fileName);
+ sprintf (fileName, "%s/secmod.db", cert_db_path);
+ rc &= check_db_file_permissions (fileName);
+
+ PORT_Free (fileName);
+
+ if (rc == 0)
+ fprintf (stderr, "Unable to use certificate database %s due to errors.\n", cert_db_path);
+
+ return rc;
+}
+
+static int
+verify_it (const char *inputName, const char *signatureName, SECKEYPublicKey *pubKey)
+{
+ unsigned char buffer[4096];
+ PRFileInfo info;
+ PRStatus prStatus;
+ PRInt32 numBytes;
+ PRFileDesc *local_file_fd;
+ VFYContext *vfy;
+ SECItem signature;
+ SECStatus secStatus;
+
+ /* Get the size of the signature file. */
+ prStatus = PR_GetFileInfo (signatureName, &info);
+ if (prStatus != PR_SUCCESS || info.type != PR_FILE_FILE || info.size < 0)
+ {
+ fprintf (stderr, "Unable to obtain information on the signature file %s.\n", signatureName);
+ nssError ();
+ return MODULE_UNTRUSTED; /* Not signed */
+ }
+
+ /* Open the signature file. */
+ local_file_fd = PR_Open (signatureName, PR_RDONLY, 0);
+ if (local_file_fd == NULL)
+ {
+ fprintf (stderr, "Could not open the signature file %s\n.", signatureName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ /* Allocate space to read the signature file. */
+ signature.data = PORT_Alloc (info.size);
+ if (! signature.data)
+ {
+ fprintf (stderr, "Unable to allocate memory for the signature in %s.\n", signatureName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ /* Read the signature. */
+ numBytes = PR_Read (local_file_fd, signature.data, info.size);
+ if (numBytes == 0) /* EOF */
+ {
+ fprintf (stderr, "EOF reading signature file %s.\n", signatureName);
+ return MODULE_CHECK_ERROR;
+ }
+ if (numBytes < 0)
+ {
+ fprintf (stderr, "Error reading signature file %s.\n", signatureName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+ if (numBytes != info.size)
+ {
+ fprintf (stderr, "Incomplete data while reading signature file %s.\n", signatureName);
+ return MODULE_CHECK_ERROR;
+ }
+ signature.len = info.size;
+
+ /* Done with the signature file. */
+ PR_Close (local_file_fd);
+
+ /* Create a verification context. */
+ vfy = VFY_CreateContextDirect (pubKey, & signature, SEC_OID_PKCS1_RSA_ENCRYPTION,
+ SEC_OID_UNKNOWN, NULL, NULL);
+ if (! vfy)
+ {
+ /* The key does not match the signature. This is not an error. It just means
+ we are currently trying the wrong certificate/key. i.e. the module
+ remains untrusted for now. */
+ return MODULE_UNTRUSTED;
+ }
+
+ /* Begin the verification process. */
+ secStatus = VFY_Begin(vfy);
+ if (secStatus != SECSuccess)
+ {
+ fprintf (stderr, "Unable to initialize verification context while verifying %s using the signature in %s.\n",
+ inputName, signatureName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ /* Now read the data and add it to the signature. */
+ local_file_fd = PR_Open (inputName, PR_RDONLY, 0);
+ if (local_file_fd == NULL)
+ {
+ fprintf (stderr, "Could not open module file %s.\n", inputName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ for (;;)
+ {
+ numBytes = PR_Read (local_file_fd, buffer, sizeof (buffer));
+ if (numBytes == 0)
+ break; /* EOF */
+
+ if (numBytes < 0)
+ {
+ fprintf (stderr, "Error reading module file %s.\n", inputName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ /* Add the data to the signature. */
+ secStatus = VFY_Update (vfy, buffer, numBytes);
+ if (secStatus != SECSuccess)
+ {
+ fprintf (stderr, "Error while verifying module file %s.\n", inputName);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+ }
+
+ PR_Close(local_file_fd);
+
+ /* Complete the verification. */
+ secStatus = VFY_End (vfy);
+ if (secStatus != SECSuccess) {
+ fprintf (stderr, "Unable to verify signed module %s. It may have been altered since it was created.\n", inputName);
+ nssError ();
+ return MODULE_ALTERED;
+ }
+
+ return MODULE_OK;
+}
+
+int verify_module (const char *module_name, const char *signature_name)
+{
+ const char *dbdir = SYSCONFDIR "/systemtap/staprun";
+ SECKEYPublicKey *pubKey;
+ SECStatus secStatus;
+ CERTCertList *certList;
+ CERTCertListNode *certListNode;
+ CERTCertificate *cert;
+ PRStatus prStatus;
+ PRFileInfo info;
+ int rc = 0;
+
+ /* Look for the certificate database. If it's not there, it's not an error, it
+ just means that the module can't be verified. */
+ prStatus = PR_GetFileInfo (dbdir, &info);
+ if (prStatus != PR_SUCCESS || info.type != PR_FILE_DIRECTORY)
+ return MODULE_UNTRUSTED;
+
+ /* Verify the permissions of the certificate database and its files. */
+ if (! check_cert_db_permissions (dbdir))
+ return MODULE_UNTRUSTED;
+
+ /* Call the NSPR initialization routines. */
+ PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+ /* Initialize NSS. */
+ secStatus = NSS_Init (dbdir);
+ if (secStatus != SECSuccess)
+ {
+ fprintf (stderr, "Unable to initialize nss library using the database in %s.\n",
+ dbdir);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ certList = PK11_ListCerts (PK11CertListAll, NULL);
+ if (certList == NULL)
+ {
+ fprintf (stderr, "Unable to find certificates in the certificate database in %s.\n",
+ dbdir);
+ nssError ();
+ return MODULE_UNTRUSTED;
+ }
+
+ /* We need to look at each certificate in the database. */
+ for (certListNode = CERT_LIST_HEAD (certList);
+ ! CERT_LIST_END (certListNode, certList);
+ certListNode = CERT_LIST_NEXT (certListNode))
+ {
+ cert = certListNode->cert;
+
+ pubKey = CERT_ExtractPublicKey (cert);
+ if (pubKey == NULL)
+ {
+ fprintf (stderr, "Unable to extract public key from the certificate with nickname %s from the certificate database in %s.\n",
+ cert->nickname, dbdir);
+ nssError ();
+ return MODULE_CHECK_ERROR;
+ }
+
+ /* Verify the file. */
+ rc = verify_it (module_name, signature_name, pubKey);
+ if (rc == MODULE_OK || rc == MODULE_ALTERED || rc == MODULE_CHECK_ERROR)
+ break; /* resolved or error */
+ }
+
+ /* Shutdown NSS and exit NSPR gracefully. */
+ nssCleanup ();
+
+ return rc;
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/runtime/staprun/modverify.h b/runtime/staprun/modverify.h
new file mode 100644
index 00000000..49b90bfe
--- /dev/null
+++ b/runtime/staprun/modverify.h
@@ -0,0 +1,9 @@
+int verify_module (const char *module_name, const char *signature_name);
+
+/* return codes for verify_module. */
+#define MODULE_OK 1
+#define MODULE_UNTRUSTED 0
+#define MODULE_CHECK_ERROR -1
+#define MODULE_ALTERED -2
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/runtime/staprun/relay.c b/runtime/staprun/relay.c
index 19621933..b9796241 100644
--- a/runtime/staprun/relay.c
+++ b/runtime/staprun/relay.c
@@ -17,6 +17,9 @@ static pthread_t reader[NR_CPUS];
static int relay_fd[NR_CPUS];
static int bulkmode = 0;
static volatile int stop_threads = 0;
+static time_t *time_backlog[NR_CPUS];
+static int backlog_order=0;
+#define BACKLOG_MASK ((1 << backlog_order) - 1)
/*
* ppoll exists in glibc >= 2.4
@@ -44,6 +47,90 @@ static int ppoll(struct pollfd *fds, nfds_t nfds,
}
#endif
+int init_backlog(int cpu)
+{
+ int order = 0;
+ if (!fnum_max)
+ return 0;
+ while (fnum_max >> order) order++;
+ if (fnum_max == 1<<(order-1)) order--;
+ time_backlog[cpu] = (time_t *)calloc(1<<order, sizeof(time_t));
+ if (time_backlog[cpu] == NULL) {
+ _err("Memory allocation failed\n");
+ return -1;
+ }
+ backlog_order = order;
+ return 0;
+}
+
+void write_backlog(int cpu, int fnum, time_t t)
+{
+ time_backlog[cpu][fnum & BACKLOG_MASK] = t;
+}
+
+time_t read_backlog(int cpu, int fnum)
+{
+ return time_backlog[cpu][fnum & BACKLOG_MASK];
+}
+
+int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t)
+{
+ int len;
+ len = stap_strfloctime(buf, max, outfile_name, t);
+ if (len < 0) {
+ err("Invalid FILE name format\n");
+ return -1;
+ }
+ if (bulkmode) {
+ /* special case: for testing we sometimes want to write to /dev/null */
+ if (strcmp(outfile_name, "/dev/null") == 0) {
+ strcpy(buf, "/dev/null");
+ } else {
+ if (snprintf_chk(&buf[len], PATH_MAX - len,
+ "_cpu%d.%d", cpu, fnum))
+ return -1;
+ }
+ } else {
+ /* stream mode */
+ if (snprintf_chk(&buf[len], PATH_MAX - len, ".%d", fnum))
+ return -1;
+ }
+ return 0;
+}
+
+static int open_outfile(int fnum, int cpu, int remove_file)
+{
+ char buf[PATH_MAX];
+ time_t t;
+ if (!outfile_name) {
+ _err("-S is set without -o. Please file a bug report.\n");
+ return -1;
+ }
+
+ time(&t);
+ if (fnum_max) {
+ if (remove_file) {
+ /* remove oldest file */
+ if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max,
+ cpu, read_backlog(cpu, fnum - fnum_max)) < 0)
+ return -1;
+ remove(buf); /* don't care */
+ }
+ write_backlog(cpu, fnum, t);
+ }
+
+ if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t) < 0)
+ return -1;
+ out_fd[cpu] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ if (out_fd[cpu] < 0) {
+ perr("Couldn't open output file %s", buf);
+ return -1;
+ }
+ if (set_clexec(out_fd[cpu]) < 0)
+ return -1;
+ return 0;
+}
+
/**
* reader_thread - per-cpu channel buffer reader
*/
@@ -57,6 +144,9 @@ static void *reader_thread(void *data)
struct timespec tim = {.tv_sec=0, .tv_nsec=200000000}, *timeout = &tim;
sigset_t sigs;
struct sigaction sa;
+ off_t wsize = 0;
+ int fnum = 0;
+ int remove_file = 0;
sigemptyset(&sigs);
sigaddset(&sigs,SIGUSR2);
@@ -95,17 +185,37 @@ static void *reader_thread(void *data)
dbug(3, "cpu=%d poll=%d errno=%d\n", cpu, rc, errno);
if (errno != EINTR) {
_perr("poll error");
- return(NULL);
+ goto error_out;
}
}
while ((rc = read(relay_fd[cpu], buf, sizeof(buf))) > 0) {
+ wsize += rc;
+ /* Switching file */
+ if (fsize_max && wsize > fsize_max) {
+ close(out_fd[cpu]);
+ fnum++;
+ if (fnum_max && fnum == fnum_max)
+ remove_file = 1;
+ if (open_outfile(fnum, cpu, remove_file) < 0) {
+ perr("Couldn't open file for cpu %d, exiting.", cpu);
+ goto error_out;
+ }
+ wsize = rc;
+ }
if (write(out_fd[cpu], buf, rc) != rc) {
- perr("Couldn't write to output %d for cpu %d, exiting.", out_fd[cpu], cpu);
- return(NULL);
+ if (errno != EPIPE)
+ perr("Couldn't write to output %d for cpu %d, exiting.", out_fd[cpu], cpu);
+ goto error_out;
}
}
} while (!stop_threads);
- dbug(3, "exiting thread %d\n", cpu);
+ dbug(3, "exiting thread for cpu %d\n", cpu);
+ return(NULL);
+
+error_out:
+ /* Signal the main thread that we need to quit */
+ kill(getpid(), SIGTERM);
+ dbug(2, "exiting thread for cpu %d after error\n", cpu);
return(NULL);
}
@@ -116,7 +226,7 @@ static void *reader_thread(void *data)
*/
int init_relayfs(void)
{
- int i;
+ int i, len;
struct statfs st;
char rqbuf[128];
char buf[PATH_MAX], relay_filebase[PATH_MAX];
@@ -163,14 +273,29 @@ int init_relayfs(void)
return -1;
}
- if (bulkmode) {
+ if (fsize_max) {
+ /* switch file mode */
+ for (i = 0; i < ncpus; i++) {
+ if (init_backlog(i) < 0)
+ return -1;
+ if (open_outfile(0, i, 0) < 0)
+ return -1;
+ }
+ } else if (bulkmode) {
for (i = 0; i < ncpus; i++) {
if (outfile_name) {
/* special case: for testing we sometimes want to write to /dev/null */
if (strcmp(outfile_name, "/dev/null") == 0) {
strcpy(buf, "/dev/null");
} else {
- if (sprintf_chk(buf, "%s_%d", outfile_name, i))
+ len = stap_strfloctime(buf, PATH_MAX,
+ outfile_name, time(NULL));
+ if (len < 0) {
+ err("Invalid FILE name format\n");
+ return -1;
+ }
+ if (snprintf_chk(&buf[len],
+ PATH_MAX - len, "_%d", i))
return -1;
}
} else {
@@ -189,9 +314,15 @@ int init_relayfs(void)
} else {
/* stream mode */
if (outfile_name) {
- out_fd[0] = open (outfile_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ len = stap_strfloctime(buf, PATH_MAX,
+ outfile_name, time(NULL));
+ if (len < 0) {
+ err("Invalid FILE name format\n");
+ return -1;
+ }
+ out_fd[0] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666);
if (out_fd[0] < 0) {
- perr("Couldn't open output file %s", outfile_name);
+ perr("Couldn't open output file %s", buf);
return -1;
}
if (set_clexec(out_fd[i]) < 0)
diff --git a/runtime/staprun/relay_old.c b/runtime/staprun/relay_old.c
index bd746f19..33d2daf3 100644
--- a/runtime/staprun/relay_old.c
+++ b/runtime/staprun/relay_old.c
@@ -23,6 +23,14 @@ static int bulkmode = 0;
unsigned subbuf_size = 0;
unsigned n_subbufs = 0;
+struct switchfile_ctrl_block {
+ off_t wsize;
+ int fnum;
+ int rmfile;
+};
+
+static struct switchfile_ctrl_block global_scb = {0, 0, 0};
+
/* per-cpu buffer info */
static struct buf_status
{
@@ -70,6 +78,41 @@ void close_oldrelayfs(int detach)
close_relayfs_files(i);
}
+static int open_oldoutfile(int fnum, int cpu, int remove_file)
+{
+ char buf[PATH_MAX];
+ time_t t;
+ if (outfile_name) {
+ time(&t);
+ if (fnum_max) {
+ if (remove_file) {
+ /* remove oldest file */
+ if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max,
+ cpu, read_backlog(cpu, fnum - fnum_max)) < 0)
+ return -1;
+ remove(buf); /* don't care */
+ }
+ write_backlog(cpu, fnum, t);
+ }
+ if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t) < 0)
+ return -1;
+ } else if (bulkmode) {
+ if (sprintf_chk(buf, "stpd_cpu%d.%d", cpu, fnum))
+ return -1;
+ } else { /* stream mode */
+ out_fd[cpu] = STDOUT_FILENO;
+ return 0;
+ }
+
+ out_fd[cpu] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ if (out_fd[cpu] < 0) {
+ perr("Couldn't open output file %s", buf);
+ return -1;
+ }
+ if (set_clexec(out_fd[cpu]) < 0)
+ return -1;
+ return 0;
+}
/**
* open_relayfs_files - open and mmap buffer and open output file.
* Returns -1 on unexpected failure, 0 if file not found, 1 on success.
@@ -104,18 +147,31 @@ static int open_relayfs_files(int cpu, const char *relay_filebase, const char *p
return -1;
}
+ if (fsize_max) {
+ if (init_backlog(cpu) < 0)
+ goto err2;
+ if (open_oldoutfile(0, cpu, 0) < 0)
+ goto err2;
+ goto opened;
+ }
if (outfile_name) {
/* special case: for testing we sometimes want to
* write to /dev/null */
if (strcmp(outfile_name, "/dev/null") == 0) {
strcpy(tmp, "/dev/null");
} else {
- if (sprintf_chk(tmp, "%s_%d", outfile_name, cpu))
- goto err1;
+ int len;
+ len = stap_strfloctime(tmp, PATH_MAX, outfile_name, time(NULL));
+ if (len < 0) {
+ err("Invalid FILE name format\n");
+ goto err2;
+ }
+ if (snprintf_chk(&tmp[len], PATH_MAX - len, "_%d", cpu))
+ goto err2;
}
} else {
if (sprintf_chk(tmp, "stpd_cpu%d", cpu))
- goto err1;
+ goto err2;
}
if((percpu_tmpfile[cpu] = fopen(tmp, "w+")) == NULL) {
@@ -126,6 +182,7 @@ static int open_relayfs_files(int cpu, const char *relay_filebase, const char *p
perr("Couldn't open output file %s", tmp);
goto err2;
}
+opened:
total_bufsize = subbuf_size * n_subbufs;
relay_buffer[cpu] = mmap(NULL, total_bufsize, PROT_READ,
@@ -155,7 +212,8 @@ err1:
/**
* process_subbufs - write ready subbufs to disk
*/
-static int process_subbufs(struct _stp_buf_info *info)
+static int process_subbufs(struct _stp_buf_info *info,
+ struct switchfile_ctrl_block *scb)
{
unsigned subbufs_ready, start_subbuf, end_subbuf, subbuf_idx, i;
int len, cpu = info->cpu;
@@ -173,10 +231,23 @@ static int process_subbufs(struct _stp_buf_info *info)
padding = *((unsigned *)subbuf_ptr);
subbuf_ptr += sizeof(padding);
len = (subbuf_size - sizeof(padding)) - padding;
+ scb->wsize += len;
+ if (fsize_max && scb->wsize > fsize_max) {
+ fclose(percpu_tmpfile[cpu]);
+ scb->fnum ++;
+ if (fnum_max && scb->fnum == fnum_max)
+ scb->rmfile = 1;
+ if (open_oldoutfile(scb->fnum, cpu, scb->rmfile) < 0) {
+ perr("Couldn't open file for cpu %d, exiting.", cpu);
+ return -1;
+ }
+ scb->wsize = len;
+ }
if (len) {
if (fwrite_unlocked (subbuf_ptr, len, 1, percpu_tmpfile[cpu]) != 1) {
- _perr("Couldn't write to output file for cpu %d, exiting:", cpu);
- exit(1);
+ if (errno != EPIPE)
+ _perr("Couldn't write to output file for cpu %d, exiting:", cpu);
+ return -1;
}
}
subbufs_consumed++;
@@ -196,6 +267,7 @@ static void *reader_thread(void *data)
struct _stp_consumed_info consumed_info;
unsigned subbufs_consumed;
cpu_set_t cpu_mask;
+ struct switchfile_ctrl_block scb = {0, 0, 0};
CPU_ZERO(&cpu_mask);
CPU_SET(cpu, &cpu_mask);
@@ -210,14 +282,17 @@ static void *reader_thread(void *data)
if (rc < 0) {
if (errno != EINTR) {
_perr("poll error");
- exit(1);
+ break;
}
err("WARNING: poll warning: %s\n", strerror(errno));
rc = 0;
}
rc = read(proc_fd[cpu], &status[cpu].info, sizeof(struct _stp_buf_info));
- subbufs_consumed = process_subbufs(&status[cpu].info);
+ rc = process_subbufs(&status[cpu].info, &scb);
+ if (rc < 0)
+ break;
+ subbufs_consumed = rc;
if (subbufs_consumed) {
if (subbufs_consumed > status[cpu].max_backlog)
status[cpu].max_backlog = subbufs_consumed;
@@ -230,6 +305,37 @@ static void *reader_thread(void *data)
if (status[cpu].info.flushing)
pthread_exit(NULL);
} while (1);
+
+ /* Signal the main thread that we need to quit */
+ kill(getpid(), SIGTERM);
+ pthread_exit(NULL);
+}
+
+/**
+ * write_realtime_data - write realtime data packet to disk
+ */
+int write_realtime_data(void *data, ssize_t nb)
+{
+ ssize_t bw;
+ global_scb.wsize += nb;
+ if (fsize_max && global_scb.wsize > fsize_max) {
+ close(out_fd[0]);
+ global_scb.fnum++;
+ if (fnum_max && global_scb.fnum == fnum_max)
+ global_scb.rmfile = 1;
+ if (open_oldoutfile(global_scb.fnum, 0,
+ global_scb.rmfile) < 0) {
+ perr("Couldn't open file, exiting.");
+ return -1;
+ }
+ global_scb.wsize = nb;
+ }
+ bw = write(out_fd[0], data, nb);
+ if (bw >= 0 && bw != nb) {
+ nb = nb - bw;
+ bw = write(out_fd[0], data, nb);
+ }
+ return bw != nb;
}
/**
@@ -249,10 +355,22 @@ int init_oldrelayfs(void)
bulkmode = 1;
if (!bulkmode) {
+ int len;
+ char tmp[PATH_MAX];
+ if (fsize_max) {
+ if (init_backlog(0))
+ return -1;
+ return open_oldoutfile(0, 0, 0);
+ }
if (outfile_name) {
- out_fd[0] = open (outfile_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+ len = stap_strfloctime(tmp, PATH_MAX, outfile_name, time(NULL));
+ if (len < 0) {
+ err("Invalid FILE name format\n");
+ return -1;
+ }
+ out_fd[0] = open (tmp, O_CREAT|O_TRUNC|O_WRONLY, 0666);
if (out_fd[0] < 0 || set_clexec(out_fd[0]) < 0) {
- perr("Couldn't open output file '%s'", outfile_name);
+ perr("Couldn't open output file '%s'", tmp);
return -1;
}
} else
diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h
index f49cc7db..bd6402e4 100644
--- a/runtime/staprun/staprun.h
+++ b/runtime/staprun/staprun.h
@@ -9,7 +9,7 @@
*
* Copyright (C) 2005-2008 Red Hat Inc.
*/
-
+#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
@@ -33,31 +33,35 @@
#include <sys/wait.h>
#include <sys/statfs.h>
#include <linux/version.h>
+#include <syslog.h>
/* Include config.h to pick up dependency for --prefix usage. */
#include "config.h"
-#define dbug(level, args...) {if (verbose>=level) {fprintf(stderr,"%s:%s:%d ",__name__,__FUNCTION__, __LINE__); fprintf(stderr,args);}}
+extern void eprintf(const char *fmt, ...);
+extern void switch_syslog(const char *name);
+
+#define dbug(level, args...) do {if (verbose>=level) {eprintf("%s:%s:%d ",__name__,__FUNCTION__, __LINE__); eprintf(args);}} while (0)
extern char *__name__;
/* print to stderr */
-#define err(args...) fprintf(stderr,args)
+#define err(args...) eprintf(args)
/* better perror() */
#define perr(args...) do { \
int _errno = errno; \
- fputs("ERROR: ", stderr); \
- fprintf(stderr, args); \
- fprintf(stderr, ": %s\n", strerror(_errno)); \
+ eprintf("ERROR: "); \
+ eprintf(args); \
+ eprintf(": %s\n", strerror(_errno)); \
} while (0)
/* Error messages. Use these for serious errors, not informational messages to stderr. */
-#define _err(args...) do {fprintf(stderr,"%s:%s:%d: ERROR: ",__name__, __FUNCTION__, __LINE__); fprintf(stderr,args);} while(0)
+#define _err(args...) do {eprintf("%s:%s:%d: ERROR: ",__name__, __FUNCTION__, __LINE__); eprintf(args);} while(0)
#define _perr(args...) do { \
int _errno = errno; \
_err(args); \
- fprintf(stderr, ": %s\n", strerror(_errno)); \
+ eprintf(": %s\n", strerror(_errno)); \
} while (0)
#define overflow_error() _err("Internal buffer overflow. Please file a bug report.\n")
@@ -114,7 +118,12 @@ int init_relayfs(void);
void close_relayfs(void);
int init_oldrelayfs(void);
void close_oldrelayfs(int);
+int write_realtime_data(void *data, ssize_t nb);
void setup_signals(void);
+int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t);
+int init_backlog(int cpu);
+void write_backlog(int cpu, int fnum, time_t t);
+time_t read_backlog(int cpu, int fnum);
/* staprun_funcs.c */
void setup_staprun_signals(void);
const char *moderror(int err);
@@ -126,6 +135,7 @@ void start_symbol_thread(void);
void stop_symbol_thread(void);
/* common.c functions */
+int stap_strfloctime(char *buf, size_t max, const char *fmt, time_t t);
void parse_args(int argc, char **argv);
void usage(char *prog);
void parse_modpath(const char *);
@@ -154,6 +164,9 @@ extern int attach_mod;
extern int delete_mod;
extern int load_only;
extern int need_uprobes;
+extern int daemon_mode;
+extern off_t fsize_max;
+extern int fnum_max;
/* getopt variables */
extern char *optarg;
diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c
index 5e7fa102..8da7e7e8 100644
--- a/runtime/staprun/staprun_funcs.c
+++ b/runtime/staprun/staprun_funcs.c
@@ -7,14 +7,20 @@
* Public License (GPL); either version 2, or (at your option) any
* later version.
*
- * Copyright (C) 2007-2008 Red Hat Inc.
+ * Copyright (C) 2007-2009 Red Hat Inc.
*/
+#include "config.h"
#include "staprun.h"
+#if HAVE_NSS
+#include "modverify.h"
+#endif
+
#include <sys/mount.h>
#include <sys/utsname.h>
#include <grp.h>
#include <pwd.h>
+#include <assert.h>
extern long init_module(void *, unsigned long, const char *);
@@ -199,6 +205,44 @@ int mountfs(void)
return 0;
}
+#if HAVE_NSS
+/*
+ * Modules which have been signed using a certificate and private key
+ * corresponding to a certificate and public key in the database in
+ * the '$sysconfdir/systemtap/staprun' directory may be loaded by
+ * anyone.
+ *
+ * Returns: -1 on errors, 0 on failure, 1 on success.
+ */
+static int
+check_signature(void)
+{
+ char module_realpath[PATH_MAX];
+ char signature_realpath[PATH_MAX];
+ int rc;
+
+ dbug(2, "checking signature for %s\n", modpath);
+
+ /* Use realpath() to canonicalize the module path. */
+ if (realpath(modpath, module_realpath) == NULL) {
+ perr("Unable to canonicalize module path \"%s\"", modpath);
+ return MODULE_CHECK_ERROR;
+ }
+
+ /* Now add the .sgn suffix to get the signature file name. */
+ if (strlen (module_realpath) > PATH_MAX - 4) {
+ err("Path \"%s\" is too long.", modpath);
+ return MODULE_CHECK_ERROR;
+ }
+ sprintf (signature_realpath, "%s.sgn", module_realpath);
+
+ rc = verify_module (module_realpath, signature_realpath);
+
+ dbug(2, "verify_module returns %d\n", rc);
+
+ return rc;
+}
+#endif /* HAVE_NSS */
/*
* Members of the 'stapusr' group can only use "blessed" modules -
@@ -269,6 +313,15 @@ check_path(void)
return -1;
}
+ /* Overwrite the modpath with the canonicalized one, to defeat
+ a possible race between path checking below and somewhat later
+ module loading. */
+ modpath = strdup (module_realpath);
+ if (modpath == NULL) {
+ _perr("allocating memory failed");
+ exit (1);
+ }
+
/* To make sure the user can't specify something like
* /lib/modules/`uname -r`/systemtapmod.ko, put a '/' on the
* end of staplib_dir_realpath. */
@@ -293,22 +346,23 @@ check_path(void)
}
/*
- * Check the user's permissions. Is he allowed to run staprun (or is
- * he limited to "blessed" modules)?
+ * Check the user's group membership. Is he allowed to run staprun (or is
*
- * Returns: -1 on errors, 0 on failure, 1 on success.
+ * o members of stapdev can do anything
+ * o members of stapusr can load modules from /lib/modules/KVER/systemtap
+ *
+ * Returns: -2 if neither group exists
+ * -1 for other errors
+ * 0 on failure
+ * 1 on success
*/
-int check_permissions(void)
+static int
+check_groups (void)
{
gid_t gid, gidlist[NGROUPS_MAX];
gid_t stapdev_gid, stapusr_gid;
int i, ngids;
struct group *stgr;
- int path_check = 0;
-
- /* If we're root, we can do anything. */
- if (getuid() == 0)
- return 1;
/* Lookup the gid for group "stapdev" */
errno = 0;
@@ -332,55 +386,42 @@ int check_permissions(void)
else
stapusr_gid = stgr->gr_gid;
- /* If neither group was found, just return an error. */
- if (stapdev_gid == (gid_t)-1 && stapusr_gid == (gid_t)-1) {
- err("ERROR: You are trying to run stap as a normal user.\n"
- "You should either be root, or be part of either "
- "group \"stapdev\" or group \"stapusr\".\n"
- "Your system doesn't seem to have either group.\n"
- "For more information, please consult the \"SAFETY AND SECURITY\" section of the \"stap(1)\" manpage\n");
- return -1;
- }
+ /* If neither group was found, then return -2. */
+ if (stapdev_gid == (gid_t)-1 && stapusr_gid == (gid_t)-1)
+ return -2;
/* According to the getgroups() man page, getgroups() may not
* return the effective gid, so try to match it first. */
gid = getegid();
if (gid == stapdev_gid)
return 1;
- else if (gid == stapusr_gid)
- path_check = 1;
- /* Get the list of the user's groups. */
- ngids = getgroups(NGROUPS_MAX, gidlist);
- if (ngids < 0) {
- perr("Unable to retrieve group list");
- return -1;
- }
-
- for (i = 0; i < ngids; i++) {
- /* If the user is a member of 'stapdev', then we're
- * done, since he can use staprun without any
- * restrictions. */
- if (gidlist[i] == stapdev_gid)
- return 1;
-
- /* If the user is a member of 'stapusr', then we'll
- * need to check the module path. However, we'll keep
- * checking groups since it is possible the user is a
- * member of both groups and we haven't seen the
- * 'stapdev' group yet. */
- if (gidlist[i] == stapusr_gid)
- path_check = 1;
- }
+ if (gid != stapusr_gid) {
+ /* Get the list of the user's groups. */
+ ngids = getgroups(NGROUPS_MAX, gidlist);
+ if (ngids < 0) {
+ perr("Unable to retrieve group list");
+ return -1;
+ }
- /* If path_check is 0, then the user isn't a member of either
- * group. Error out. */
- if (path_check == 0) {
- err("ERROR: You are trying to run stap as a normal user.\n"
- "You must be a member of either group \"stapdev\" or group \"stapusr\".\n"
- "Please contact your system administrator to get yourself membership to either of those groups.\n"
- "For more information, please consult the \"SAFETY AND SECURITY\" section of the \"stap(1)\" manpage.\n");
- return 0;
+ for (i = 0; i < ngids; i++) {
+ /* If the user is a member of 'stapdev', then we're
+ * done, since he can use staprun without any
+ * restrictions. */
+ if (gidlist[i] == stapdev_gid)
+ return 1;
+
+ /* If the user is a member of 'stapusr', then we'll
+ * need to check the module path. However, we'll keep
+ * checking groups since it is possible the user is a
+ * member of both groups and we haven't seen the
+ * 'stapdev' group yet. */
+ if (gidlist[i] == stapusr_gid)
+ gid = stapusr_gid;
+ }
+ /* Not a member of stapusr? */
+ if (gid != stapusr_gid)
+ return 0;
}
/* At this point the user is only a member of the 'stapusr'
@@ -389,3 +430,50 @@ int check_permissions(void)
* is in that directory. */
return check_path();
}
+
+/*
+ * Check the user's permissions. Is he allowed to run staprun (or is
+ * he limited to "blessed" modules)?
+ *
+ * There are several levels of possible permission:
+ *
+ * 1) root can do anything
+ * 2) members of stapdev can do anything
+ * 3) members of stapusr can load modules from /lib/modules/KVER/systemtap
+ *
+ * It is only an error if all 3 levels of checking fail
+ *
+ * Returns: -1 on errors, 0 on failure, 1 on success.
+ */
+int check_permissions(void)
+{
+ int check_groups_rc;
+#if HAVE_NSS
+ int check_signature_rc = 0;
+
+ /* Attempt to verify the module against its signature. Return failure
+ if the module has been tampered with (altered). */
+ check_signature_rc = check_signature ();
+ if (check_signature_rc == MODULE_ALTERED)
+ return 0;
+#endif
+
+ /* If we're root, we can do anything. */
+ if (getuid() == 0)
+ return 1;
+
+ /* Check permissions for group membership. */
+ check_groups_rc = check_groups ();
+ if (check_groups_rc == 1)
+ return 1;
+
+ err("ERROR: You are trying to run stap as a normal user.\n"
+ "You should either be root, or be part of either "
+ "group \"stapdev\" or group \"stapusr\".\n");
+ if (check_groups_rc == -2) {
+ err("Your system doesn't seem to have either group.\n");
+ check_groups_rc = -1;
+ }
+
+ return check_groups_rc;
+}