162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/proc_fs.h> 362306a36Sopenharmony_ci#include <linux/nsproxy.h> 462306a36Sopenharmony_ci#include <linux/ptrace.h> 562306a36Sopenharmony_ci#include <linux/namei.h> 662306a36Sopenharmony_ci#include <linux/file.h> 762306a36Sopenharmony_ci#include <linux/utsname.h> 862306a36Sopenharmony_ci#include <net/net_namespace.h> 962306a36Sopenharmony_ci#include <linux/ipc_namespace.h> 1062306a36Sopenharmony_ci#include <linux/pid_namespace.h> 1162306a36Sopenharmony_ci#include <linux/user_namespace.h> 1262306a36Sopenharmony_ci#include "internal.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic const struct proc_ns_operations *ns_entries[] = { 1662306a36Sopenharmony_ci#ifdef CONFIG_NET_NS 1762306a36Sopenharmony_ci &netns_operations, 1862306a36Sopenharmony_ci#endif 1962306a36Sopenharmony_ci#ifdef CONFIG_UTS_NS 2062306a36Sopenharmony_ci &utsns_operations, 2162306a36Sopenharmony_ci#endif 2262306a36Sopenharmony_ci#ifdef CONFIG_IPC_NS 2362306a36Sopenharmony_ci &ipcns_operations, 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci#ifdef CONFIG_PID_NS 2662306a36Sopenharmony_ci &pidns_operations, 2762306a36Sopenharmony_ci &pidns_for_children_operations, 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci#ifdef CONFIG_USER_NS 3062306a36Sopenharmony_ci &userns_operations, 3162306a36Sopenharmony_ci#endif 3262306a36Sopenharmony_ci &mntns_operations, 3362306a36Sopenharmony_ci#ifdef CONFIG_CGROUPS 3462306a36Sopenharmony_ci &cgroupns_operations, 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci#ifdef CONFIG_TIME_NS 3762306a36Sopenharmony_ci &timens_operations, 3862306a36Sopenharmony_ci &timens_for_children_operations, 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const char *proc_ns_get_link(struct dentry *dentry, 4362306a36Sopenharmony_ci struct inode *inode, 4462306a36Sopenharmony_ci struct delayed_call *done) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; 4762306a36Sopenharmony_ci struct task_struct *task; 4862306a36Sopenharmony_ci struct path ns_path; 4962306a36Sopenharmony_ci int error = -EACCES; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (!dentry) 5262306a36Sopenharmony_ci return ERR_PTR(-ECHILD); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci task = get_proc_task(inode); 5562306a36Sopenharmony_ci if (!task) 5662306a36Sopenharmony_ci return ERR_PTR(-EACCES); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) 5962306a36Sopenharmony_ci goto out; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci error = ns_get_path(&ns_path, task, ns_ops); 6262306a36Sopenharmony_ci if (error) 6362306a36Sopenharmony_ci goto out; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci error = nd_jump_link(&ns_path); 6662306a36Sopenharmony_ciout: 6762306a36Sopenharmony_ci put_task_struct(task); 6862306a36Sopenharmony_ci return ERR_PTR(error); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 7462306a36Sopenharmony_ci const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; 7562306a36Sopenharmony_ci struct task_struct *task; 7662306a36Sopenharmony_ci char name[50]; 7762306a36Sopenharmony_ci int res = -EACCES; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci task = get_proc_task(inode); 8062306a36Sopenharmony_ci if (!task) 8162306a36Sopenharmony_ci return res; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { 8462306a36Sopenharmony_ci res = ns_get_name(name, sizeof(name), task, ns_ops); 8562306a36Sopenharmony_ci if (res >= 0) 8662306a36Sopenharmony_ci res = readlink_copy(buffer, buflen, name); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci put_task_struct(task); 8962306a36Sopenharmony_ci return res; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic const struct inode_operations proc_ns_link_inode_operations = { 9362306a36Sopenharmony_ci .readlink = proc_ns_readlink, 9462306a36Sopenharmony_ci .get_link = proc_ns_get_link, 9562306a36Sopenharmony_ci .setattr = proc_setattr, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct dentry *proc_ns_instantiate(struct dentry *dentry, 9962306a36Sopenharmony_ci struct task_struct *task, const void *ptr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci const struct proc_ns_operations *ns_ops = ptr; 10262306a36Sopenharmony_ci struct inode *inode; 10362306a36Sopenharmony_ci struct proc_inode *ei; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK | S_IRWXUGO); 10662306a36Sopenharmony_ci if (!inode) 10762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ei = PROC_I(inode); 11062306a36Sopenharmony_ci inode->i_op = &proc_ns_link_inode_operations; 11162306a36Sopenharmony_ci ei->ns_ops = ns_ops; 11262306a36Sopenharmony_ci pid_update_inode(task, inode); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci d_set_d_op(dentry, &pid_dentry_operations); 11562306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct task_struct *task = get_proc_task(file_inode(file)); 12162306a36Sopenharmony_ci const struct proc_ns_operations **entry, **last; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!task) 12462306a36Sopenharmony_ci return -ENOENT; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci if (ctx->pos >= 2 + ARRAY_SIZE(ns_entries)) 12962306a36Sopenharmony_ci goto out; 13062306a36Sopenharmony_ci entry = ns_entries + (ctx->pos - 2); 13162306a36Sopenharmony_ci last = &ns_entries[ARRAY_SIZE(ns_entries) - 1]; 13262306a36Sopenharmony_ci while (entry <= last) { 13362306a36Sopenharmony_ci const struct proc_ns_operations *ops = *entry; 13462306a36Sopenharmony_ci if (!proc_fill_cache(file, ctx, ops->name, strlen(ops->name), 13562306a36Sopenharmony_ci proc_ns_instantiate, task, ops)) 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci ctx->pos++; 13862306a36Sopenharmony_ci entry++; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ciout: 14162306a36Sopenharmony_ci put_task_struct(task); 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciconst struct file_operations proc_ns_dir_operations = { 14662306a36Sopenharmony_ci .read = generic_read_dir, 14762306a36Sopenharmony_ci .iterate_shared = proc_ns_dir_readdir, 14862306a36Sopenharmony_ci .llseek = generic_file_llseek, 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct dentry *proc_ns_dir_lookup(struct inode *dir, 15262306a36Sopenharmony_ci struct dentry *dentry, unsigned int flags) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct task_struct *task = get_proc_task(dir); 15562306a36Sopenharmony_ci const struct proc_ns_operations **entry, **last; 15662306a36Sopenharmony_ci unsigned int len = dentry->d_name.len; 15762306a36Sopenharmony_ci struct dentry *res = ERR_PTR(-ENOENT); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!task) 16062306a36Sopenharmony_ci goto out_no_task; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci last = &ns_entries[ARRAY_SIZE(ns_entries)]; 16362306a36Sopenharmony_ci for (entry = ns_entries; entry < last; entry++) { 16462306a36Sopenharmony_ci if (strlen((*entry)->name) != len) 16562306a36Sopenharmony_ci continue; 16662306a36Sopenharmony_ci if (!memcmp(dentry->d_name.name, (*entry)->name, len)) 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci if (entry == last) 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci res = proc_ns_instantiate(dentry, task, *entry); 17362306a36Sopenharmony_ciout: 17462306a36Sopenharmony_ci put_task_struct(task); 17562306a36Sopenharmony_ciout_no_task: 17662306a36Sopenharmony_ci return res; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciconst struct inode_operations proc_ns_dir_inode_operations = { 18062306a36Sopenharmony_ci .lookup = proc_ns_dir_lookup, 18162306a36Sopenharmony_ci .getattr = pid_getattr, 18262306a36Sopenharmony_ci .setattr = proc_setattr, 18362306a36Sopenharmony_ci}; 184