/** * ccs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root. * * @path: Pointer to "struct path". * * Returns the realpath of the given @path on success, NULL otherwise. * * This function uses kzalloc(), so caller must kfree() if this function * didn't return NULL. */ char *ccs_realpath(struct path *path) { char *buf = NULL; char *name = NULL; unsigned int buf_len = PAGE_SIZE / 2; struct dentry *dentry = path->dentry; struct super_block *sb; if (!dentry) return NULL; sb = dentry->d_sb; while (1) { char *pos; struct inode *inode; buf_len <<= 1; kfree(buf); buf = kmalloc(buf_len, CCS_GFP_FLAGS); if (!buf) break; /* To make sure that pos is '\0' terminated. */ buf[buf_len - 1] = '\0'; /* Get better name for socket. */ if (sb->s_magic == SOCKFS_MAGIC) { pos = ccs_get_socket_name(path, buf, buf_len - 1); goto encode; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) /* For "pipe:[\$]". */ if (dentry->d_op && dentry->d_op->d_dname) { pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); goto encode; } #endif inode = sb->s_root->d_inode; /* * Use local name for "filesystems without rename() operation" * or "path without vfsmount" or "absolute name is unavailable" * cases. */ if (!path->mnt || (inode->i_op && !inode->i_op->rename)) pos = ERR_PTR(-EINVAL); else { /* Get absolute name for the rest. */ ccs_realpath_lock(); pos = ccs_get_absolute_path(path, buf, buf_len - 1); ccs_realpath_unlock(); } if (pos == ERR_PTR(-EINVAL)) pos = ccs_get_local_path(path->dentry, buf, buf_len - 1); encode: if (IS_ERR(pos)) continue; name = ccs_encode(pos); break; } kfree(buf); if (!name) ccs_warn_oom(__func__); return name; }
/** * ccs_write_transition - write() for /proc/ccs/.transition interface. * * @file: Pointer to "struct file". * @buf: Domainname to transit to. Must ends with '\0'. * @count: Size of @buf. * @ppos: Unused. * * Returns @count on success, negative value otherwise. */ static ssize_t ccs_write_transition(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { const char *self_domain = ccs_current_domain()->domainname->name; const int self_domain_len = strlen(self_domain); char *data; int data_len; char *tmp; int idx; int error = -ENOMEM; if (!count || count + self_domain_len >= CCS_EXEC_TMPSIZE - 10) return -ENOMEM; data = kmalloc(count, CCS_GFP_FLAGS); if (!data) return -ENOMEM; if (copy_from_user(data, buf, count)) { error = -EFAULT; goto out; } if (memchr(data, '\0', count) != data + count - 1) { error = -EINVAL; goto out; } tmp = ccs_encode(data); kfree(data); data = tmp; if (!data) goto out; data_len = strlen(data); tmp = kzalloc(self_domain_len + data_len + 5, CCS_GFP_FLAGS); if (!tmp) goto out; /* * Add "//" prefix to requested name in order to distinguish domain * transitions with execve(). */ snprintf(tmp, self_domain_len + data_len + 4, "%s //%s", self_domain, data); kfree(data); data = tmp; idx = ccs_read_lock(); error = ccs_may_transit(data, data + self_domain_len + 1); ccs_read_unlock(idx); out: kfree(data); return error ? error : count; }