1 /* 2 * Copyright (C) 2004 IBM Corporation 3 * 4 * Author: Serge Hallyn <serue@us.ibm.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation, version 2 of the 9 * License. 10 */ 11 12 #include <linux/export.h> 13 #include <linux/uts.h> 14 #include <linux/utsname.h> 15 #include <linux/err.h> 16 #include <linux/slab.h> 17 #include <linux/cred.h> 18 #include <linux/user_namespace.h> 19 #include <linux/proc_ns.h> 20 #include <linux/sched/task.h> 21 22 static struct ucounts *inc_uts_namespaces(struct user_namespace *ns) 23 { 24 return inc_ucount(ns, current_euid(), UCOUNT_UTS_NAMESPACES); 25 } 26 27 static void dec_uts_namespaces(struct ucounts *ucounts) 28 { 29 dec_ucount(ucounts, UCOUNT_UTS_NAMESPACES); 30 } 31 32 static struct uts_namespace *create_uts_ns(void) 33 { 34 struct uts_namespace *uts_ns; 35 36 uts_ns = kmalloc(sizeof(struct uts_namespace), GFP_KERNEL); 37 if (uts_ns) 38 kref_init(&uts_ns->kref); 39 return uts_ns; 40 } 41 42 /* 43 * Clone a new ns copying an original utsname, setting refcount to 1 44 * @old_ns: namespace to clone 45 * Return ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise 46 */ 47 static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns, 48 struct uts_namespace *old_ns) 49 { 50 struct uts_namespace *ns; 51 struct ucounts *ucounts; 52 int err; 53 54 err = -ENOSPC; 55 ucounts = inc_uts_namespaces(user_ns); 56 if (!ucounts) 57 goto fail; 58 59 err = -ENOMEM; 60 ns = create_uts_ns(); 61 if (!ns) 62 goto fail_dec; 63 64 err = ns_alloc_inum(&ns->ns); 65 if (err) 66 goto fail_free; 67 68 ns->ucounts = ucounts; 69 ns->ns.ops = &utsns_operations; 70 71 down_read(&uts_sem); 72 memcpy(&ns->name, &old_ns->name, sizeof(ns->name)); 73 ns->user_ns = get_user_ns(user_ns); 74 up_read(&uts_sem); 75 return ns; 76 77 fail_free: 78 kfree(ns); 79 fail_dec: 80 dec_uts_namespaces(ucounts); 81 fail: 82 return ERR_PTR(err); 83 } 84 85 /* 86 * Copy task tsk's utsname namespace, or clone it if flags 87 * specifies CLONE_NEWUTS. In latter case, changes to the 88 * utsname of this process won't be seen by parent, and vice 89 * versa. 90 */ 91 struct uts_namespace *copy_utsname(unsigned long flags, 92 struct user_namespace *user_ns, struct uts_namespace *old_ns) 93 { 94 struct uts_namespace *new_ns; 95 96 BUG_ON(!old_ns); 97 get_uts_ns(old_ns); 98 99 if (!(flags & CLONE_NEWUTS)) 100 return old_ns; 101 102 new_ns = clone_uts_ns(user_ns, old_ns); 103 104 put_uts_ns(old_ns); 105 return new_ns; 106 } 107 108 void free_uts_ns(struct kref *kref) 109 { 110 struct uts_namespace *ns; 111 112 ns = container_of(kref, struct uts_namespace, kref); 113 dec_uts_namespaces(ns->ucounts); 114 put_user_ns(ns->user_ns); 115 ns_free_inum(&ns->ns); 116 kfree(ns); 117 } 118 119 static inline struct uts_namespace *to_uts_ns(struct ns_common *ns) 120 { 121 return container_of(ns, struct uts_namespace, ns); 122 } 123 124 static struct ns_common *utsns_get(struct task_struct *task) 125 { 126 struct uts_namespace *ns = NULL; 127 struct nsproxy *nsproxy; 128 129 task_lock(task); 130 nsproxy = task->nsproxy; 131 if (nsproxy) { 132 ns = nsproxy->uts_ns; 133 get_uts_ns(ns); 134 } 135 task_unlock(task); 136 137 return ns ? &ns->ns : NULL; 138 } 139 140 static void utsns_put(struct ns_common *ns) 141 { 142 put_uts_ns(to_uts_ns(ns)); 143 } 144 145 static int utsns_install(struct nsproxy *nsproxy, struct ns_common *new) 146 { 147 struct uts_namespace *ns = to_uts_ns(new); 148 149 if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || 150 !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) 151 return -EPERM; 152 153 get_uts_ns(ns); 154 put_uts_ns(nsproxy->uts_ns); 155 nsproxy->uts_ns = ns; 156 return 0; 157 } 158 159 static struct user_namespace *utsns_owner(struct ns_common *ns) 160 { 161 return to_uts_ns(ns)->user_ns; 162 } 163 164 const struct proc_ns_operations utsns_operations = { 165 .name = "uts", 166 .type = CLONE_NEWUTS, 167 .get = utsns_get, 168 .put = utsns_put, 169 .install = utsns_install, 170 .owner = utsns_owner, 171 }; 172