xref: /openbmc/linux/kernel/utsname.c (revision 3ea056c50476f877f8bceb560ab69871098cb3a9)
14865ecf1SSerge E. Hallyn /*
24865ecf1SSerge E. Hallyn  *  Copyright (C) 2004 IBM Corporation
34865ecf1SSerge E. Hallyn  *
44865ecf1SSerge E. Hallyn  *  Author: Serge Hallyn <serue@us.ibm.com>
54865ecf1SSerge E. Hallyn  *
64865ecf1SSerge E. Hallyn  *  This program is free software; you can redistribute it and/or
74865ecf1SSerge E. Hallyn  *  modify it under the terms of the GNU General Public License as
84865ecf1SSerge E. Hallyn  *  published by the Free Software Foundation, version 2 of the
94865ecf1SSerge E. Hallyn  *  License.
104865ecf1SSerge E. Hallyn  */
114865ecf1SSerge E. Hallyn 
129984de1aSPaul Gortmaker #include <linux/export.h>
134865ecf1SSerge E. Hallyn #include <linux/uts.h>
144865ecf1SSerge E. Hallyn #include <linux/utsname.h>
15467e9f4bSCedric Le Goater #include <linux/err.h>
161aeb272cSRobert P. J. Day #include <linux/slab.h>
175b825c3aSIngo Molnar #include <linux/cred.h>
1859607db3SSerge E. Hallyn #include <linux/user_namespace.h>
190bb80f24SDavid Howells #include <linux/proc_ns.h>
20f719ff9bSIngo Molnar #include <linux/sched/task.h>
214865ecf1SSerge E. Hallyn 
22*3ea056c5SAlexey Dobriyan static struct kmem_cache *uts_ns_cache __ro_after_init;
23*3ea056c5SAlexey Dobriyan 
24f7af3d1cSEric W. Biederman static struct ucounts *inc_uts_namespaces(struct user_namespace *ns)
25f7af3d1cSEric W. Biederman {
26f7af3d1cSEric W. Biederman 	return inc_ucount(ns, current_euid(), UCOUNT_UTS_NAMESPACES);
27f7af3d1cSEric W. Biederman }
28f7af3d1cSEric W. Biederman 
29f7af3d1cSEric W. Biederman static void dec_uts_namespaces(struct ucounts *ucounts)
30f7af3d1cSEric W. Biederman {
31f7af3d1cSEric W. Biederman 	dec_ucount(ucounts, UCOUNT_UTS_NAMESPACES);
32f7af3d1cSEric W. Biederman }
33f7af3d1cSEric W. Biederman 
344c2a7e72SAlexey Dobriyan static struct uts_namespace *create_uts_ns(void)
354c2a7e72SAlexey Dobriyan {
364c2a7e72SAlexey Dobriyan 	struct uts_namespace *uts_ns;
374c2a7e72SAlexey Dobriyan 
38*3ea056c5SAlexey Dobriyan 	uts_ns = kmem_cache_alloc(uts_ns_cache, GFP_KERNEL);
394c2a7e72SAlexey Dobriyan 	if (uts_ns)
404c2a7e72SAlexey Dobriyan 		kref_init(&uts_ns->kref);
414c2a7e72SAlexey Dobriyan 	return uts_ns;
424c2a7e72SAlexey Dobriyan }
434c2a7e72SAlexey Dobriyan 
444865ecf1SSerge E. Hallyn /*
45071df104SSerge E. Hallyn  * Clone a new ns copying an original utsname, setting refcount to 1
46071df104SSerge E. Hallyn  * @old_ns: namespace to clone
47*3ea056c5SAlexey Dobriyan  * Return ERR_PTR(-ENOMEM) on error (failure to allocate), new ns otherwise
48071df104SSerge E. Hallyn  */
49bcf58e72SEric W. Biederman static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,
50bb96a6f5SSerge E. Hallyn 					  struct uts_namespace *old_ns)
51071df104SSerge E. Hallyn {
52071df104SSerge E. Hallyn 	struct uts_namespace *ns;
53f7af3d1cSEric W. Biederman 	struct ucounts *ucounts;
5498f842e6SEric W. Biederman 	int err;
55071df104SSerge E. Hallyn 
56df75e774SEric W. Biederman 	err = -ENOSPC;
57f7af3d1cSEric W. Biederman 	ucounts = inc_uts_namespaces(user_ns);
58f7af3d1cSEric W. Biederman 	if (!ucounts)
59f7af3d1cSEric W. Biederman 		goto fail;
60f7af3d1cSEric W. Biederman 
61f7af3d1cSEric W. Biederman 	err = -ENOMEM;
624c2a7e72SAlexey Dobriyan 	ns = create_uts_ns();
63467e9f4bSCedric Le Goater 	if (!ns)
64f7af3d1cSEric W. Biederman 		goto fail_dec;
65467e9f4bSCedric Le Goater 
666344c433SAl Viro 	err = ns_alloc_inum(&ns->ns);
67f7af3d1cSEric W. Biederman 	if (err)
68f7af3d1cSEric W. Biederman 		goto fail_free;
6998f842e6SEric W. Biederman 
70f7af3d1cSEric W. Biederman 	ns->ucounts = ucounts;
7133c42940SAl Viro 	ns->ns.ops = &utsns_operations;
7233c42940SAl Viro 
73efc63c4fSAlexey Dobriyan 	down_read(&uts_sem);
74071df104SSerge E. Hallyn 	memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
75bcf58e72SEric W. Biederman 	ns->user_ns = get_user_ns(user_ns);
76efc63c4fSAlexey Dobriyan 	up_read(&uts_sem);
77071df104SSerge E. Hallyn 	return ns;
78f7af3d1cSEric W. Biederman 
79f7af3d1cSEric W. Biederman fail_free:
80*3ea056c5SAlexey Dobriyan 	kmem_cache_free(uts_ns_cache, ns);
81f7af3d1cSEric W. Biederman fail_dec:
82f7af3d1cSEric W. Biederman 	dec_uts_namespaces(ucounts);
83f7af3d1cSEric W. Biederman fail:
84f7af3d1cSEric W. Biederman 	return ERR_PTR(err);
85071df104SSerge E. Hallyn }
86071df104SSerge E. Hallyn 
87071df104SSerge E. Hallyn /*
884865ecf1SSerge E. Hallyn  * Copy task tsk's utsname namespace, or clone it if flags
894865ecf1SSerge E. Hallyn  * specifies CLONE_NEWUTS.  In latter case, changes to the
904865ecf1SSerge E. Hallyn  * utsname of this process won't be seen by parent, and vice
914865ecf1SSerge E. Hallyn  * versa.
924865ecf1SSerge E. Hallyn  */
93bb96a6f5SSerge E. Hallyn struct uts_namespace *copy_utsname(unsigned long flags,
94bcf58e72SEric W. Biederman 	struct user_namespace *user_ns, struct uts_namespace *old_ns)
954865ecf1SSerge E. Hallyn {
96071df104SSerge E. Hallyn 	struct uts_namespace *new_ns;
974865ecf1SSerge E. Hallyn 
98e3222c4eSBadari Pulavarty 	BUG_ON(!old_ns);
994865ecf1SSerge E. Hallyn 	get_uts_ns(old_ns);
1004865ecf1SSerge E. Hallyn 
101071df104SSerge E. Hallyn 	if (!(flags & CLONE_NEWUTS))
102e3222c4eSBadari Pulavarty 		return old_ns;
103071df104SSerge E. Hallyn 
104bcf58e72SEric W. Biederman 	new_ns = clone_uts_ns(user_ns, old_ns);
105071df104SSerge E. Hallyn 
106071df104SSerge E. Hallyn 	put_uts_ns(old_ns);
107e3222c4eSBadari Pulavarty 	return new_ns;
1084865ecf1SSerge E. Hallyn }
1094865ecf1SSerge E. Hallyn 
1104865ecf1SSerge E. Hallyn void free_uts_ns(struct kref *kref)
1114865ecf1SSerge E. Hallyn {
1124865ecf1SSerge E. Hallyn 	struct uts_namespace *ns;
1134865ecf1SSerge E. Hallyn 
1144865ecf1SSerge E. Hallyn 	ns = container_of(kref, struct uts_namespace, kref);
115f7af3d1cSEric W. Biederman 	dec_uts_namespaces(ns->ucounts);
11659607db3SSerge E. Hallyn 	put_user_ns(ns->user_ns);
1176344c433SAl Viro 	ns_free_inum(&ns->ns);
118*3ea056c5SAlexey Dobriyan 	kmem_cache_free(uts_ns_cache, ns);
1194865ecf1SSerge E. Hallyn }
12034482e89SEric W. Biederman 
1213c041184SAl Viro static inline struct uts_namespace *to_uts_ns(struct ns_common *ns)
1223c041184SAl Viro {
1233c041184SAl Viro 	return container_of(ns, struct uts_namespace, ns);
1243c041184SAl Viro }
1253c041184SAl Viro 
12664964528SAl Viro static struct ns_common *utsns_get(struct task_struct *task)
12734482e89SEric W. Biederman {
12834482e89SEric W. Biederman 	struct uts_namespace *ns = NULL;
12934482e89SEric W. Biederman 	struct nsproxy *nsproxy;
13034482e89SEric W. Biederman 
131728dba3aSEric W. Biederman 	task_lock(task);
132728dba3aSEric W. Biederman 	nsproxy = task->nsproxy;
13334482e89SEric W. Biederman 	if (nsproxy) {
13434482e89SEric W. Biederman 		ns = nsproxy->uts_ns;
13534482e89SEric W. Biederman 		get_uts_ns(ns);
13634482e89SEric W. Biederman 	}
137728dba3aSEric W. Biederman 	task_unlock(task);
13834482e89SEric W. Biederman 
1393c041184SAl Viro 	return ns ? &ns->ns : NULL;
14034482e89SEric W. Biederman }
14134482e89SEric W. Biederman 
14264964528SAl Viro static void utsns_put(struct ns_common *ns)
14334482e89SEric W. Biederman {
1443c041184SAl Viro 	put_uts_ns(to_uts_ns(ns));
14534482e89SEric W. Biederman }
14634482e89SEric W. Biederman 
14764964528SAl Viro static int utsns_install(struct nsproxy *nsproxy, struct ns_common *new)
14834482e89SEric W. Biederman {
1493c041184SAl Viro 	struct uts_namespace *ns = to_uts_ns(new);
150142e1d1dSEric W. Biederman 
1515e4a0847SEric W. Biederman 	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
152c7b96acfSEric W. Biederman 	    !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
153142e1d1dSEric W. Biederman 		return -EPERM;
154142e1d1dSEric W. Biederman 
15534482e89SEric W. Biederman 	get_uts_ns(ns);
15634482e89SEric W. Biederman 	put_uts_ns(nsproxy->uts_ns);
15734482e89SEric W. Biederman 	nsproxy->uts_ns = ns;
15834482e89SEric W. Biederman 	return 0;
15934482e89SEric W. Biederman }
16034482e89SEric W. Biederman 
161bcac25a5SAndrey Vagin static struct user_namespace *utsns_owner(struct ns_common *ns)
162bcac25a5SAndrey Vagin {
163bcac25a5SAndrey Vagin 	return to_uts_ns(ns)->user_ns;
164bcac25a5SAndrey Vagin }
165bcac25a5SAndrey Vagin 
16634482e89SEric W. Biederman const struct proc_ns_operations utsns_operations = {
16734482e89SEric W. Biederman 	.name		= "uts",
16834482e89SEric W. Biederman 	.type		= CLONE_NEWUTS,
16934482e89SEric W. Biederman 	.get		= utsns_get,
17034482e89SEric W. Biederman 	.put		= utsns_put,
17134482e89SEric W. Biederman 	.install	= utsns_install,
172bcac25a5SAndrey Vagin 	.owner		= utsns_owner,
17334482e89SEric W. Biederman };
174*3ea056c5SAlexey Dobriyan 
175*3ea056c5SAlexey Dobriyan void __init uts_ns_init(void)
176*3ea056c5SAlexey Dobriyan {
177*3ea056c5SAlexey Dobriyan 	uts_ns_cache = kmem_cache_create_usercopy(
178*3ea056c5SAlexey Dobriyan 			"uts_namespace", sizeof(struct uts_namespace), 0,
179*3ea056c5SAlexey Dobriyan 			SLAB_PANIC|SLAB_ACCOUNT,
180*3ea056c5SAlexey Dobriyan 			offsetof(struct uts_namespace, name),
181*3ea056c5SAlexey Dobriyan 			sizeof_field(struct uts_namespace, name),
182*3ea056c5SAlexey Dobriyan 			NULL);
183*3ea056c5SAlexey Dobriyan }
184