summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/staprun/mainloop.c10
-rw-r--r--runtime/staprun/modverify.c389
-rw-r--r--runtime/staprun/modverify.h8
-rw-r--r--runtime/staprun/staprun_funcs.c194
4 files changed, 532 insertions, 69 deletions
diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c
index c80bbba4..205fdf37 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"
@@ -395,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);
}
@@ -427,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);
}
diff --git a/runtime/staprun/modverify.c b/runtime/staprun/modverify.c
new file mode 100644
index 00000000..2f3b96d5
--- /dev/null
+++ b/runtime/staprun/modverify.c
@@ -0,0 +1,389 @@
+/*
+ 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;
+}
diff --git a/runtime/staprun/modverify.h b/runtime/staprun/modverify.h
new file mode 100644
index 00000000..9abf62d4
--- /dev/null
+++ b/runtime/staprun/modverify.h
@@ -0,0 +1,8 @@
+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
+
diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c
index 49b37988..40af1678 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 -
@@ -302,35 +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) {
- /* ... like overriding the real UID */
- const char *env_id = getenv("SYSTEMTAP_REAL_UID");
- if (env_id && setreuid(atoi(env_id), -1))
- err("WARNING: couldn't set staprun UID to '%s': %s",
- env_id, strerror(errno));
-
- /* ... or overriding the real GID */
- env_id = getenv("SYSTEMTAP_REAL_GID");
- if (env_id && setregid(atoi(env_id), -1))
- err("WARNING: couldn't set staprun GID to '%s': %s",
- env_id, strerror(errno));
-
- return 1;
- }
/* Lookup the gid for group "stapdev" */
errno = 0;
@@ -354,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'
@@ -411,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;
+ int check_signature_rc = 0;
+
+#if HAVE_NSS
+ /* 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;
+}