Merge branch 'upstream' into tizen
authorJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Thu, 13 Mar 2014 13:44:42 +0000 (15:44 +0200)
committerJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Thu, 13 Mar 2014 13:45:33 +0000 (15:45 +0200)
Conflicts:
libsmack/libsmack.c

1  2 
libsmack/libsmack.c

@@@ -29,9 -30,7 +30,8 @@@
  #include <sys/socket.h>
  #include <sys/stat.h>
  #include <sys/types.h>
 +#include <sys/xattr.h>
  #include <unistd.h>
- #include <limits.h>
  #include <sys/xattr.h>
  
  #define SELF_LABEL_FILE "/proc/self/attr/current"
@@@ -89,15 -120,24 +121,30 @@@ struct smack_cipso 
        struct cipso_mapping *last;
  };
  
 +typedef int (*getxattr_func)(void*, const char*, void*, size_t);
 +typedef int (*setxattr_func)(const void*, const char*, const void*, size_t, int);
 +typedef int (*removexattr_func)(void*, const char*);
 +
 +static inline char* get_xattr_name(enum smack_label_type type);
++
+ struct smack_file_buffer {
+       int fd;
+       int pos;
+       int flush_pos;
+       char *buf;
+ };
+ static int open_smackfs_file(const char *long_name, const char *short_name,
+                            int *use_long);
  static int accesses_apply(struct smack_accesses *handle, int clear);
- static inline ssize_t get_label(char *dest, const char *src);
+ static int accesses_print(struct smack_accesses *handle,
+                         int clear, int use_long, int multiline,
+                         struct smack_file_buffer *load_buffer,
+                         struct smack_file_buffer *change_buffer);
+ static inline ssize_t get_label(char *dest, const char *src, unsigned int *hash);
  static inline int str_to_access_code(const char *str);
  static inline void access_code_to_str(unsigned code, char *str);
+ static struct smack_label *label_add(struct smack_accesses *handle, const char *src);
  
  int smack_accesses_new(struct smack_accesses **accesses)
  {
@@@ -639,171 -689,174 +696,267 @@@ int smack_revoke_subject(const char *su
        return (ret < 0) ? -1 : 0;
  }
  
- static int accesses_apply(struct smack_accesses *handle, int clear)
 +static int internal_getlabel(void* file, char** label,
 +              enum smack_label_type type,
 +              getxattr_func getfunc)
 +{
 +      char* xattr_name = get_xattr_name(type);
 +      char value[SMACK_LABEL_LEN + 1];
 +      int ret;
 +
 +      ret = getfunc(file, xattr_name, value, SMACK_LABEL_LEN + 1);
 +      if (ret == -1) {
 +              if (errno == ENODATA) {
 +                      *label = NULL;
 +                      return 0;
 +              }
 +              return -1;
 +      }
 +
 +      value[ret] = '\0';
 +      *label = calloc(ret + 1, 1);
 +      if (*label == NULL)
 +              return -1;
 +      strncpy(*label, value, ret);
 +      return 0;
 +}
 +
 +static int internal_setlabel(void* file, const char* label,
 +              enum smack_label_type type,
 +              setxattr_func setfunc, removexattr_func removefunc)
 +{
 +      char* xattr_name = get_xattr_name(type);
 +
 +      /* Check validity of labels for LABEL_TRANSMUTE */
 +      if (type == SMACK_LABEL_TRANSMUTE && label != NULL) {
 +              if (!strcmp(label, "0"))
 +                      label = NULL;
 +              else if (!strcmp(label, "1"))
 +                      label = "TRUE";
 +              else
 +                      return -1;
 +      }
 +
 +      if (label == NULL || label[0] == '\0') {
 +              return removefunc(file, xattr_name);
 +      } else {
 +              int len = strnlen(label, SMACK_LABEL_LEN + 1);
 +              if (len > SMACK_LABEL_LEN)
 +                      return -1;
 +              return setfunc(file, xattr_name, label, len, 0);
 +      }
 +}
 +
 +int smack_getlabel(const char *path, char** label,
 +              enum smack_label_type type)
 +{
 +      return internal_getlabel((void*) path, label, type,
 +                      (getxattr_func) getxattr);
 +}
 +
 +int smack_lgetlabel(const char *path, char** label,
 +              enum smack_label_type type)
 +{
 +      return internal_getlabel((void*) path, label, type,
 +                      (getxattr_func) lgetxattr);
 +}
 +
 +int smack_fgetlabel(int fd, char** label,
 +              enum smack_label_type type)
 +{
 +      return internal_getlabel((void*) ((unsigned long) fd), label, type,
 +                      (getxattr_func) fgetxattr);
 +}
 +
 +int smack_setlabel(const char *path, const char* label,
 +              enum smack_label_type type)
 +{
 +      return internal_setlabel((void*) path, label, type,
 +                      (setxattr_func) setxattr, (removexattr_func) removexattr);
 +}
 +
 +int smack_lsetlabel(const char *path, const char* label,
 +              enum smack_label_type type)
 +{
 +      return internal_setlabel((void*) path, label, type,
 +                      (setxattr_func) lsetxattr, (removexattr_func) lremovexattr);
 +}
 +
 +int smack_fsetlabel(int fd, const char* label,
 +              enum smack_label_type type)
 +{
 +      return internal_setlabel((void*) ((unsigned long) fd), label, type,
 +                      (setxattr_func) fsetxattr, (removexattr_func) fremovexattr);
 +}
 +
+ static int open_smackfs_file(const char *long_name, const char *short_name,
+                            int *use_long)
  {
-       char buf[LOAD_LEN + 1];
-       char allow_str[ACC_LEN + 1];
-       char deny_str[ACC_LEN + 1];
-       struct smack_rule *rule;
-       int ret;
        int fd;
-       int load_fd;
-       int change_fd;
-       int load2 = 1;
-       char path[PATH_MAX];
  
-       if (!smackfs_mnt)
-               return -1;
-       snprintf(path, sizeof path, "%s/load2", smackfs_mnt);
-       load_fd = open(path, O_WRONLY);
-       if (load_fd < 0) {
+       fd = openat(smackfs_mnt_dirfd, long_name, O_WRONLY);
+       if (fd < 0) {
                if (errno != ENOENT)
                        return -1;
-               /* fallback */
-               snprintf(path, sizeof path, "%s/load", smackfs_mnt);
-               load_fd = open(path, O_WRONLY);
-               /* Try to continue if the file doesn't exist, we might not need it. */
-               if (load_fd < 0 && errno != ENOENT)
+               fd = openat(smackfs_mnt_dirfd, short_name, O_WRONLY);
+               if (fd < 0)
                        return -1;
-               load2 = 0;
+               *use_long = 0;
+               return fd;
        }
  
-       snprintf(path, sizeof path, "%s/change-rule", smackfs_mnt);
-       change_fd = open(path, O_WRONLY);
-       /* Try to continue if the file doesn't exist, we might not need it. */
-       if (change_fd < 0 && errno != ENOENT) {
-               ret = -1;
+       *use_long = 1;
+       return fd;
+ }
+ static inline int check_multiline(int change_fd)
+ {
+       /* This string will be written to kernel Smack "change-rule" interface
+        * to check if it can handle multiple rules in one write.
+        * It consists of two rules, separated by '\n': first that does nothing
+        * and second that has invalid format. If kernel parses only the first
+        * line (pre-3.12 behavior), it won't see the invalid rule and succeed.
+        * If it parses both lines, an error will be returned.
+        */
+       static const char test_str[] = "^ ^ - -\n-";
+       int ret;
+       ret = write(change_fd, test_str, sizeof(test_str) - 1);
+       if (ret == -1 && errno == EINVAL)
+               return 1;
+       return 0;
+ }
+ static int accesses_apply(struct smack_accesses *handle, int clear)
+ {
+       int ret;
+       int use_long = 1;
+       int multiline = 0;
+       struct smack_file_buffer load_buffer = {.fd = -1, .buf = NULL};
+       struct smack_file_buffer change_buffer = {.fd = -1, .buf = NULL};
+       if (init_smackfs_mnt())
+               return -1;
+       load_buffer.fd = open_smackfs_file("load2", "load", &use_long);
+       if (load_buffer.fd < 0)
+               return -1;
+       load_buffer.buf = malloc(handle->page_size + LOAD_LEN);
+       if (load_buffer.buf == NULL)
                goto err_out;
-       }
  
-       for (rule = handle->first; rule != NULL; rule = rule->next) {
-               /* Fail immediately without doing any further processing
-                  if modify rules are not supported. */
-               if (rule->deny_code >= 0 && change_fd < 0) {
-                       ret = -1;
+       change_buffer.fd = openat(smackfs_mnt_dirfd, "change-rule", O_WRONLY);
+       if (change_buffer.fd >= 0) {
+               change_buffer.buf = malloc(handle->page_size + LOAD_LEN);
+               if (change_buffer.buf == NULL)
                        goto err_out;
-               }
  
-               access_code_to_str(clear ? 0 : rule->allow_code, allow_str);
+               multiline = check_multiline(change_buffer.fd);
+       } else {
+               /* Try to continue if "change-rule" doesn't exist, we might
+                * not need it. */
+               if (errno != ENOENT)
+                       goto err_out;
+       }
  
-               if (rule->deny_code != -1 && !clear) {
-                       access_code_to_str(rule->deny_code, deny_str);
+       ret = accesses_print(handle, clear, use_long, multiline,
+               &load_buffer, &change_buffer);
+       goto out;
  
-                       fd = change_fd;
-                       ret = snprintf(buf, LOAD_LEN + 1, KERNEL_MODIFY_FORMAT,
-                                      rule->subject, rule->object,
-                                      allow_str,
-                                      deny_str);
-               } else {
-                       fd = load_fd;
-                       if (load2)
-                               ret = snprintf(buf, LOAD_LEN + 1, KERNEL_LONG_FORMAT,
-                                              rule->subject, rule->object,
-                                              allow_str);
-                       else {
-                               if (rule->subject_len > SHORT_LABEL_LEN ||
-                                   rule->object_len > SHORT_LABEL_LEN) {
-                                       ret = -1;
-                                       goto err_out;
-                               }
-                               ret = snprintf(buf, LOAD_LEN + 1, KERNEL_SHORT_FORMAT,
-                                              rule->subject, rule->object,
-                                              allow_str);
+ err_out:
+       ret = -1;
+ out:
+       if (load_buffer.fd >= 0)
+               close(load_buffer.fd);
+       if (change_buffer.fd >= 0)
+               close(change_buffer.fd);
+       free(load_buffer.buf);
+       free(change_buffer.buf);
+       return ret;
+ }
+ static int buffer_flush(struct smack_file_buffer *buf)
+ {
+       int pos;
+       int ret;
+       /* Write buffered bytes to kernel, up to flush_pos */
+       for (pos = 0; pos < buf->flush_pos; ) {
+               ret = write(buf->fd, buf->buf + pos, buf->flush_pos - pos);
+               if (ret == -1) {
+                       if (errno != EINTR)
+                               return -1;
+               } else
+                       pos += ret;
+       }
+       /* Move remaining, not flushed bytes to the buffer start */
+       memcpy(buf->buf, buf->buf + pos, buf->pos - pos);
+       buf->pos -= pos;
+       buf->flush_pos = 0;
+       return 0;
+ }
+ static inline void rule_print_long(char *buf, int *pos,
+       struct smack_label *subject_label, struct smack_label *object_label,
+       const char *allow_str, const char *deny_str)
+ {
+       memcpy(buf + *pos, subject_label->label, subject_label->len);
+       *pos += subject_label->len;
+       buf[(*pos)++] = ' ';
+       memcpy(buf + *pos, object_label->label, object_label->len);
+       *pos += object_label->len;
+       buf[(*pos)++] = ' ';
+       memcpy(buf + *pos, allow_str, ACC_LEN);
+       *pos += ACC_LEN;
+       if (deny_str != NULL) {
+               buf[(*pos)++] = ' ';
+               memcpy(buf + *pos, deny_str, ACC_LEN);
+               *pos += ACC_LEN;
+       }
+ }
+ static int accesses_print(struct smack_accesses *handle, int clear,
+                         int use_long, int multiline,
+                         struct smack_file_buffer *load_buffer,
+                         struct smack_file_buffer *change_buffer)
+ {
+       struct smack_file_buffer *buffer;
+       char allow_str[ACC_LEN + 1];
+       char deny_str[ACC_LEN + 1];
+       struct smack_label *subject_label;
+       struct smack_label *object_label;
+       struct smack_rule *rule;
+       union smack_perm *perm;
+       int merge_cnt;
+       int x;
+       int y;
+       if (!use_long && handle->has_long)
+               return -1;
+       load_buffer->pos = 0;
+       change_buffer->pos = 0;
+       bzero(handle->merge_perms, handle->labels_cnt * sizeof(union smack_perm));
+       for (x = 0; x < handle->labels_cnt; ++x) {
+               subject_label = handle->labels[x];
+               merge_cnt = 0;
+               for (rule = subject_label->first_rule; rule != NULL; rule = rule->next_rule) {
+                       perm = &(handle->merge_perms[rule->object_id]);
+                       if (perm->allow_deny_code == 0)
+                               handle->merge_object_ids[merge_cnt++] = rule->object_id;
+                       if (clear) {
+                               perm->allow_code = 0;
+                               perm->deny_code  = ACCESS_TYPE_ALL;
+                       } else {
+                               perm->allow_code |=  rule->perm.allow_code;
+                               perm->allow_code &= ~rule->perm.deny_code;
+                               perm->deny_code  &= ~rule->perm.allow_code;
+                               perm->deny_code  |=  rule->perm.deny_code;
                        }
                }
  
@@@ -910,24 -1010,81 +1110,102 @@@ static inline void access_code_to_str(u
        str[6] = '\0';
  }
  
 +static inline char* get_xattr_name(enum smack_label_type type)
 +{
 +      switch (type) {
 +      case SMACK_LABEL_ACCESS:
 +              return "security.SMACK64";
 +      case SMACK_LABEL_EXEC:
 +              return "security.SMACK64EXEC";
 +      case SMACK_LABEL_MMAP:
 +              return "security.SMACK64MMAP";
 +      case SMACK_LABEL_TRANSMUTE:
 +              return "security.SMACK64TRANSMUTE";
 +      case SMACK_LABEL_IPIN:
 +              return "security.SMACK64IPIN";
 +      case SMACK_LABEL_IPOUT:
 +              return "security.SMACK64IPOUT";
 +      default:
 +              /* Should not reach this point */
 +              return NULL;
 +      }
++}
++
+ static inline struct smack_label *
+ is_label_known(struct smack_accesses *handle, const char *label, int hash)
+ {
+       struct smack_label *lab = handle->label_hash[hash].first;
+       while (lab != NULL && strcmp(label, lab->label) != 0)
+               lab = lab->next_label;
+       return lab;
+ }
+ static inline int accesses_resize(struct smack_accesses *handle)
+ {
+       struct smack_label **labels;
+       union smack_perm *merge_perms;
+       int *merge_object_ids;
+       int alloc = handle->labels_alloc << 1;
+       labels = realloc(handle->labels, alloc * sizeof(struct smack_label *));
+       if (labels == NULL)
+               return -1;
+       handle->labels = labels;
+       merge_perms = realloc(handle->merge_perms, alloc * sizeof(union smack_perm));
+       if (merge_perms == NULL)
+               return -1;
+       handle->merge_perms = merge_perms;
+       merge_object_ids = realloc(handle->merge_object_ids, alloc * sizeof(int));
+       if (merge_object_ids == NULL)
+               return -1;
+       handle->merge_object_ids = merge_object_ids;
+       handle->labels_alloc = alloc;
+       return 0;
+ }
+ static struct smack_label *label_add(struct smack_accesses *handle, const char *label)
+ {
+       struct smack_hash_entry *hash_entry;
+       unsigned int hash_value = 0;
+       struct smack_label *new_label;
+       int len;
+       len = get_label(NULL, label, &hash_value);
+       if (len == -1)
+               return NULL;
+       new_label = is_label_known(handle, label, hash_value);
+       if (new_label == NULL) {/*no entry added yet*/
+               if (handle->labels_cnt == handle->labels_alloc)
+                       if (accesses_resize(handle))
+                               return NULL;
+               new_label = malloc(sizeof(struct smack_label));
+               if (new_label == NULL)
+                       return NULL;
+               new_label->label = malloc(len + 1);
+               if (new_label->label == NULL)
+                       return NULL;
+               memcpy(new_label->label, label, len + 1);
+               new_label->id = handle->labels_cnt;
+               new_label->len = len;
+               new_label->first_rule = NULL;
+               new_label->last_rule = NULL;
+               new_label->next_label = NULL;
+               hash_entry = &(handle->label_hash[hash_value]);
+               if (hash_entry->first == NULL) {
+                       hash_entry->first = new_label;
+                       hash_entry->last = new_label;
+               } else {
+                       hash_entry->last->next_label = new_label;
+                       hash_entry->last = new_label;
+               }
+               handle->labels[handle->labels_cnt++] = new_label;
+       }
  
+       return new_label;
  }