#define READ_BUF_SIZE LOAD_LEN + 1
#define SELF_LABEL_FILE "/proc/self/attr/current"
+#define DICT_HASH_SIZE 4096
+
extern char *smack_mnt;
typedef int (*getxattr_func)(void*, const char*, void*, size_t);
if (fd < 0)
return -1;
- memset(buf,0,CIPSO_MAX_SIZE);
for (m = cipso->first; m != NULL; m = m->next) {
+ memset(buf,0,CIPSO_MAX_SIZE);
+ offset = 0;
+
snprintf(buf, SMACK_LABEL_LEN + 1, "%s", m->label);
offset += strlen(buf) + 1;
}
}
+
+static inline ssize_t get_label(char *dest, const char *src, unsigned int *hash)
+{
+ int i;
+ unsigned int h = 5381;/*DJB2 hashing function magic number*/;
+
+ if (!src || src[0] == '\0' || src[0] == '-')
+ return -1;
+
+ for (i = 0; i < (SMACK_LABEL_LEN + 1) && src[i]; i++) {
+ if (src[i] <= ' ' || src[i] > '~')
+ return -1;
+ switch (src[i]) {
+ case '/':
+ case '"':
+ case '\\':
+ case '\'':
+ return -1;
+ default:
+ break;
+ }
+
+ if (dest)
+ dest[i] = src[i];
+ if (hash)
+ /* This efficient hash function,
+ * created by Daniel J. Bernstein,
+ * is known as DJB2 algorithm */
+ h = (h << 5) + h + src[i];
+ }
+
+ if (dest && i < (SMACK_LABEL_LEN + 1))
+ dest[i] = '\0';
+ if (hash)
+ *hash = h % DICT_HASH_SIZE;
+
+ return i < (SMACK_LABEL_LEN + 1) ? i : -1;
+}
+
+ssize_t smack_label_length(const char *label)
+{
+ return get_label(NULL, label, NULL);
+}
+
+ssize_t smack_new_label_from_path(const char *path, const char *xattr,
+ int follow, char **label)
+{
+ char buf[SMACK_LABEL_LEN + 1];
+ char *result;
+ ssize_t ret = 0;
+
+ ret = follow ?
+ getxattr(path, xattr, buf, SMACK_LABEL_LEN + 1) :
+ lgetxattr(path, xattr, buf, SMACK_LABEL_LEN + 1);
+ if (ret < 0)
+ return -1;
+ buf[ret] = '\0';
+
+ result = calloc(ret + 1, 1);
+ if (result == NULL)
+ return -1;
+
+ ret = get_label(result, buf, NULL);
+ if (ret < 0) {
+ free(result);
+ return -1;
+ }
+
+ *label = result;
+ return ret;
+}
+
+int smack_set_label_for_path(const char *path,
+ const char *xattr,
+ int follow,
+ const char *label)
+{
+ int len;
+ int ret;
+
+ len = (int)smack_label_length(label);
+ if (len < 0)
+ return -2;
+
+ ret = follow ?
+ setxattr(path, xattr, label, len, 0) :
+ lsetxattr(path, xattr, label, len, 0);
+ return ret;
+}
+
+int smack_remove_label_for_path(const char *path,
+ const char *xattr,
+ int follow)
+{
+ return follow ? removexattr(path, xattr) : lremovexattr(path, xattr);
+}
* chsmack - Set smack attributes on files
*
* Copyright (C) 2011 Nokia Corporation.
- * Copyright (C) 2012 Samsung Electronics Co.
+ * Copyright (C) 2011, 2012, 2013 Intel Corporation
*
- * This program 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, version 2.
+ * This program 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, version 2.
*
- * 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.
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- * Author:
- * Casey Schaufler <casey@schaufler-ca.com>
- * Rafal Krypa <r.krypa@samsung.com>
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
*/
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
#include <sys/smack.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <libgen.h>
+static const char usage[] =
+ "Usage: %s [options] <path>\n"
+ "options:\n"
+ " -v --version output version information and exit\n"
+ " -h --help output usage information and exit\n"
+ " -a --access set/remove "XATTR_NAME_SMACK"\n"
+ " -e --exec set/remove "XATTR_NAME_SMACKEXEC"\n"
+ " -m --mmap set/remove "XATTR_NAME_SMACKMMAP"\n"
+ " -t --transmute set/remove "XATTR_NAME_SMACKTRANSMUTE"\n"
+ " -d --remove tell to remove the attribute\n"
+ " -L --dereference tell to follow the symbolic links\n"
+;
-static inline int leads(char *in, char *lead)
+/* main */
+int main(int argc, char *argv[])
{
- return (strncmp(in, lead, strlen(lead)) == 0);
-}
+ static const char shortoptions[] = "vha::e::m::tdL";
+ static struct option options[] = {
+ {"version", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {"access", optional_argument, 0, 'a'},
+ {"exec", optional_argument, 0, 'e'},
+ {"mmap", optional_argument, 0, 'm'},
+ {"transmute", no_argument, 0, 't'},
+ {"dereference", no_argument, 0, 'L'},
+ {NULL, 0, 0, 0}
+ };
-int
-main(int argc, char *argv[])
-{
+ /* Buffers are zeroed automatically by keeping them static variables.
+ * No separate memset is needed this way.
+ */
+ static int options_map[128];
+
+ /* structure for recording options of label and their init */
+ struct labelset {
+ int isset; /* 0 if option not set, 1 if option set */
+ const char *value; /* value of the option set if any or NULL else */
+ };
+ struct labelset access_set = { 0, NULL }; /* for option "access" */
+ struct labelset exec_set = { 0, NULL }; /* for option "exec" */
+ struct labelset mmap_set = { 0, NULL }; /* for option "mmap" */
+
+ struct labelset *labelset;
+ struct stat st;
+ char *label;
+
+ int delete_flag = 0;
+ int follow_flag = 0;
+ int transmute_flag = 0;
+ int option_flag = 0;
int rc;
- int argi;
- int transmute = 0;
- char *buffer;
- char *access = NULL;
- char *mm = NULL;
- char *execute = NULL;
-
- for (argi = 1; argi < argc; argi++) {
- if (strcmp(argv[argi], "-a") == 0)
- access = argv[++argi];
- else if (leads(argv[argi], "--access="))
- access = argv[argi] + strlen("--access=");
- else if (strcmp(argv[argi], "-e") == 0)
- execute = argv[++argi];
- else if (leads(argv[argi], "--exec="))
- execute = argv[argi] + strlen("--exec=");
- else if (leads(argv[argi], "--execute="))
- execute = argv[argi] + strlen("--execute=");
- else if (strcmp(argv[argi], "-m") == 0)
- mm = argv[++argi];
- else if (leads(argv[argi], "--mmap="))
- mm = argv[argi] + strlen("--mmap=");
- else if (strcmp(argv[argi], "-t") == 0)
- transmute = 1;
- else if (strcmp(argv[argi], "--transmute") == 0)
- transmute = 1;
- else if (*argv[argi] == '-') {
- fprintf(stderr, "Invalid argument \"%s\".\n",
- argv[argi]);
- exit(1);
+ int c;
+ int i;
+
+ for (i = 0; options[i].name != NULL; i++)
+ options_map[options[i].val] = i;
+
+ /* scan options without argument */
+ while ((c = getopt_long(argc, argv, shortoptions, options, NULL)) != -1) {
+
+ switch (c) {
+ case 'a':
+ case 'e':
+ case 'm':
+ /* greedy on optional arguments */
+ if (optarg == NULL && argv[optind] != NULL
+ && argv[optind][0] != '-') {
+ optind++;
+ }
+ break;
+ case 't':
+ if (transmute_flag)
+ fprintf(stderr, "%s: %s: option set many times.\n",
+ basename(argv[0]), options[options_map[c]].name);
+ transmute_flag = 1;
+ option_flag = 1;
+ break;
+ case 'd':
+ if (delete_flag)
+ fprintf(stderr, "%s: %s: option set many times.\n",
+ basename(argv[0]), options[options_map[c]].name);
+ delete_flag = 1;
+ break;
+ case 'L':
+ if (follow_flag)
+ fprintf(stderr, "%s: %s: option set many times.\n",
+ basename(argv[0]), options[options_map[c]].name);
+ follow_flag = 1;
+ break;
+ case 'v':
+ printf("%s (libsmack) version " PACKAGE_VERSION "\n",
+ basename(argv[0]));
+ exit(0);
+ case 'h':
+ printf(usage, basename(argv[0]));
+ exit(0);
+ default:
+ printf(usage, basename(argv[0]));
+ exit(1);
}
- /*
- * Indicates the start of filenames.
- */
- else
- break;
}
- if (argi >= argc) {
- fprintf(stderr, "No files specified.\n");
- exit(1);
- }
- if (access != NULL && strlen(access) > SMACK_LABEL_LEN) {
- fprintf(stderr, "Access label \"%s\" exceeds %d characters.\n",
- access, SMACK_LABEL_LEN);
- exit(1);
+
+ /* scan options with argument (possibly) */
+ optind = 1;
+ while ((c = getopt_long(argc, argv, shortoptions, options, NULL)) != -1) {
+
+ switch (c) {
+ case 'a':
+ labelset = &access_set;
+ break;
+ case 'e':
+ labelset = &exec_set;
+ break;
+ case 'm':
+ labelset = &mmap_set;
+ break;
+ default:
+ continue;
+ }
+
+ if (labelset->isset) {
+ fprintf(stderr, "%s: %s: option set many times.\n",
+ basename(argv[0]), options[options_map[c]].name);
+ exit(1);
+ }
+ /* greedy on optional arguments */
+ if (optarg == NULL && argv[optind] != NULL && argv[optind][0] != '-') {
+ optarg = argv[optind++];
+ }
+ if (optarg == NULL) {
+ if (!delete_flag) {
+ fprintf(stderr, "%s: %s: requires a label when setting.\n",
+ basename(argv[0]), options[options_map[c]].name);
+ exit(1);
+ }
+ }
+ else if (delete_flag) {
+ fprintf(stderr, "%s: %s: requires no label when deleting.\n",
+ basename(argv[0]), options[options_map[c]].name);
+ exit(1);
+ }
+ else if (strnlen(optarg, SMACK_LABEL_LEN + 1) == SMACK_LABEL_LEN + 1) {
+ fprintf(stderr, "%s: %s: \"%s\" exceeds %d characters.\n",
+ basename(argv[0]), options[options_map[c]].name, optarg,
+ SMACK_LABEL_LEN);
+ exit(1);
+ }
+ else if (smack_label_length(optarg) < 0) {
+ fprintf(stderr, "%s: %s: \"%s\" is an invalid Smack label.\n",
+ basename(argv[0]), options[options_map[c]].name, optarg);
+ exit(1);
+ }
+ labelset->isset = 1;
+ labelset->value = optarg;
+ option_flag = 1;
}
- if (mm != NULL && strlen(mm) > SMACK_LABEL_LEN) {
- fprintf(stderr, "mmap label \"%s\" exceeds %d characters.\n",
- mm, SMACK_LABEL_LEN);
- exit(1);
+
+ /* deleting labels */
+ if (delete_flag) {
+ if (!option_flag) {
+ access_set.isset = 1;
+ exec_set.isset = 1;
+ mmap_set.isset = 1;
+ transmute_flag = 1;
+ }
+ for (i = optind; i < argc; i++) {
+ if (access_set.isset) {
+ rc = smack_remove_label_for_path(argv[i],
+ XATTR_NAME_SMACK, follow_flag);
+ if (rc < 0 && (option_flag || errno != ENODATA))
+ perror(argv[i]);
+ }
+
+ if (exec_set.isset) {
+ rc = smack_remove_label_for_path(argv[i],
+ XATTR_NAME_SMACKEXEC, follow_flag);
+ if (rc < 0 && (option_flag || errno != ENODATA))
+ perror(argv[i]);
+ }
+
+ if (mmap_set.isset) {
+ rc = smack_remove_label_for_path(argv[i],
+ XATTR_NAME_SMACKMMAP, follow_flag);
+ if (rc < 0 && (option_flag || errno != ENODATA))
+ perror(argv[i]);
+ }
+
+ if (transmute_flag) {
+ rc = smack_remove_label_for_path(argv[i],
+ XATTR_NAME_SMACKTRANSMUTE, follow_flag);
+ if (rc < 0 && (option_flag || errno != ENODATA))
+ perror(argv[i]);
+ }
+ }
}
- if (execute != NULL && strlen(execute) > SMACK_LABEL_LEN) {
- fprintf(stderr, "execute label \"%s\" exceeds %d characters.\n",
- execute, SMACK_LABEL_LEN);
- exit(1);
+
+ /* setting labels */
+ else if (option_flag) {
+ for (i = optind; i < argc; i++) {
+ if (access_set.isset) {
+ rc = smack_set_label_for_path(argv[i],
+ XATTR_NAME_SMACK, follow_flag, access_set.value);
+ if (rc < 0)
+ perror(argv[i]);
+ }
+
+ if (exec_set.isset) {
+ rc = smack_set_label_for_path(argv[i],
+ XATTR_NAME_SMACKEXEC, follow_flag, exec_set.value);
+ if (rc < 0)
+ perror(argv[i]);
+ }
+
+ if (mmap_set.isset) {
+ rc = smack_set_label_for_path(argv[i],
+ XATTR_NAME_SMACKMMAP, follow_flag, mmap_set.value);
+ if (rc < 0)
+ perror(argv[i]);
+ }
+
+ if (transmute_flag) {
+ rc = follow_flag ? stat(argv[i], &st) : lstat(argv[i], &st);
+ if (rc < 0)
+ perror(argv[i]);
+ else if (!S_ISDIR(st.st_mode)) {
+ fprintf(stderr, "%s: transmute: not a directory %s\n",
+ basename(argv[0]), argv[i]);
+ }
+ else {
+ rc = smack_set_label_for_path(argv[i],
+ XATTR_NAME_SMACKTRANSMUTE, follow_flag, "TRUE");
+ if (rc < 0)
+ perror(argv[i]);
+ }
+ }
+ }
}
- for (; argi < argc; argi++) {
- if (access == NULL && mm == NULL &&
- execute == NULL && !transmute) {
- printf("%s", argv[argi]);
- rc = smack_lgetlabel(argv[argi], &buffer, SMACK_LABEL_ACCESS);
- if (rc == 0 && buffer != NULL) {
- printf(" access=\"%s\"", buffer);
- free(buffer);
+
+ /* listing labels */
+ else {
+ for (i = optind; i < argc; i++) {
+
+ /* Print file path. */
+ printf("%s", argv[i]);
+
+ rc = (int)smack_new_label_from_path(argv[i],
+ XATTR_NAME_SMACK, follow_flag, &label);
+ if (rc > 0) {
+ printf(" access=\"%s\"", label);
+ free(label);
}
- rc = smack_lgetlabel(argv[argi], &buffer, SMACK_LABEL_EXEC);
- if (rc == 0 && buffer != NULL) {
- printf(" execute=\"%s\"", buffer);
- free(buffer);
+
+ rc = (int)smack_new_label_from_path(argv[i],
+ XATTR_NAME_SMACKEXEC, follow_flag, &label);
+ if (rc > 0) {
+ printf(" execute=\"%s\"", label);
+ free(label);
}
- rc = smack_lgetlabel(argv[argi], &buffer, SMACK_LABEL_MMAP);
- if (rc == 0 && buffer != NULL) {
- printf(" mmap=\"%s\"", buffer);
- free(buffer);
+
+ rc = (int)smack_new_label_from_path(argv[i],
+ XATTR_NAME_SMACKMMAP, follow_flag, &label);
+ if (rc > 0) {
+ printf(" mmap=\"%s\"", label);
+ free(label);
}
- rc = smack_lgetlabel(argv[argi], &buffer, SMACK_LABEL_TRANSMUTE);
- if (rc == 0 && buffer != NULL) {
- printf(" transmute=\"%s\"", buffer);
- free(buffer);
+
+ rc = (int)smack_new_label_from_path(argv[i],
+ XATTR_NAME_SMACKTRANSMUTE, follow_flag, &label);
+ if (rc > 0) {
+ printf(" transmute=\"%s\"", label);
+ free(label);
}
+
printf("\n");
- continue;
- }
- if (access != NULL) {
- rc = smack_lsetlabel(argv[argi], access, SMACK_LABEL_ACCESS);
- if (rc < 0)
- perror(argv[argi]);
- }
- if (execute != NULL) {
- rc = smack_lsetlabel(argv[argi], execute, SMACK_LABEL_EXEC);
- if (rc < 0)
- perror(argv[argi]);
- }
- if (mm != NULL) {
- rc = smack_lsetlabel(argv[argi], mm, SMACK_LABEL_MMAP);
- if (rc < 0)
- perror(argv[argi]);
- }
- if (transmute) {
- rc = smack_lsetlabel(argv[argi], "1", SMACK_LABEL_TRANSMUTE);
- if (rc < 0)
- perror(argv[argi]);
}
}
+
exit(0);
}