From orenl at cellrox.com Fri Oct 2 17:40:58 2015 From: orenl at cellrox.com (Oren Laadan) Date: Fri, 2 Oct 2015 13:40:58 -0400 Subject: [PATCH 1/3] lib/kobject_uevent.c: disable broadcast of uevents to other namespaces In-Reply-To: <51c185b6fa89f0b8e9e7dcaffb3c21c975c84302.1441762578.git.michael.coss@alcatel-lucent.com> References: <51c185b6fa89f0b8e9e7dcaffb3c21c975c84302.1441762578.git.michael.coss@alcatel-lucent.com> Message-ID: Hi Michael, While experimenting with your patches, I discovered a couple of issues: 1) One problem is that the test to disable broadcast has an undesired side-effect: it silently drops kernel uevents designated to specific net namespace(s). For example, uevents related to the "net" subsystem are now gone. More specifically, kobject_uevent_env() eventually calls netlink_broadcast_filtered() with "kobj_bcast_filter()" as the @filter argument; This filter is invoked by the netlink delivery code (for each target socket): if the respective kobject has a valid "struct kobj_ns_type_operations ops" then it will use the ops->netlink_ns() as the target network namespace, and only post to sockets that belong to that target network namespace. To remedy this, I suggest to move the test into "kobj_bcast_filter()", by replacing the final "return 0;" with "return !net_eq(sock_net(dsk), &init_net);". 2) Another problem is that when a task writes to the special file "uevent" in /sys/..., e.g. "/sys/devices/virtual/block/dm-0/uevent", it should ideally expect to see the resulting uevent in the network namespace to which it belongs, and only there. With broadcast disabled it will instead reach only the init network namespace (while before the patch it would reach all network namespaces). This could be fixed by having the userspace daemon that listens in the init network namespace forward such uevents to the "origin" network namespace (i.e. where the task belongs). However, I couldn't figure out a way for userspace to tell whether a particular uevent was "task made" via the respective "uevent" file and if so, in which network namespace - or by which task/pid - it was done. So I can't think of another solution but to do it in the kernel: handle writes to "uevent" in a way that only posts them in the network namespace of the writer task. Do you see a better option? Thanks, Oren. On Wed, Sep 9, 2015 at 2:53 PM, Michael J. Coss < michael.coss at alcatel-lucent.com> wrote: > Restrict sending uevents to only those listeners operating in the same > network namespace as the system init process. This is the first step > toward allowing policy control of the forwarding of events to other > namespaces in userspace. > > Signed-off-by: Michael J. Coss > --- > lib/kobject_uevent.c | 4 ++++ > 1 file changed, 4 insertions(+) > > diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c > index f6c2c1e..d791e33 100644 > --- a/lib/kobject_uevent.c > +++ b/lib/kobject_uevent.c > @@ -295,6 +295,10 @@ int kobject_uevent_env(struct kobject *kobj, enum > kobject_action action, > if (!netlink_has_listeners(uevent_sock, 1)) > continue; > > + /* forward event only to the host systems network > namespaces */ > + if (!net_eq(sock_net(uevent_sock), &init_net)) > + continue; > + > /* allocate message with the maximum possible size */ > len = strlen(action_string) + strlen(devpath) + 2; > skb = alloc_skb(len + env->buflen, GFP_KERNEL); > -- > 2.4.6 > > _______________________________________________ > Containers mailing list > Containers at lists.linux-foundation.org > https://lists.linuxfoundation.org/mailman/listinfo/containers > From orenl at cellrox.com Fri Oct 2 18:00:45 2015 From: orenl at cellrox.com (Oren Laadan) Date: Fri, 2 Oct 2015 14:00:45 -0400 Subject: [PATCH 2/3] lib/kobject_uevent.c: add uevent forwarding function In-Reply-To: <3456750fe7a5a5eb709e315618facf5704cc1885.1441762578.git.michael.coss@alcatel-lucent.com> References: <3456750fe7a5a5eb709e315618facf5704cc1885.1441762578.git.michael.coss@alcatel-lucent.com> Message-ID: On Wed, Sep 9, 2015 at 2:53 PM, Michael J. Coss < michael.coss at alcatel-lucent.com> wrote: > Adds capability to allow userspace programs to forward a given event to > a specific network namespace as determined by the provided pid. In > addition, support for a per-namespace kobject_sequence counter was > added. Sysfs was modified to return the correct event counter based on > the current network namespace. > > Signed-off-by: Michael J. Coss > --- > include/linux/kobject.h | 3 ++ > include/net/net_namespace.h | 3 ++ > kernel/ksysfs.c | 12 ++++++ > lib/kobject_uevent.c | 90 > +++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 108 insertions(+) > > diff --git a/include/linux/kobject.h b/include/linux/kobject.h > index 637f670..d1bb509 100644 > --- a/include/linux/kobject.h > +++ b/include/linux/kobject.h > @@ -215,6 +215,9 @@ extern struct kobject *firmware_kobj; > int kobject_uevent(struct kobject *kobj, enum kobject_action action); > int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, > char *envp[]); > +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) > +int kobject_uevent_forward(char *buf, size_t len, pid_t pid); > +#endif > > __printf(2, 3) > int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); > diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h > index e951453..a4013e5 100644 > --- a/include/net/net_namespace.h > +++ b/include/net/net_namespace.h > @@ -134,6 +134,9 @@ struct net { > #if IS_ENABLED(CONFIG_MPLS) > struct netns_mpls mpls; > #endif > +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) > + u64 kevent_seqnum; > +#endif > struct sock *diag_nlsk; > atomic_t fnhe_genid; > }; > diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c > index 6683cce..4bc15fd 100644 > --- a/kernel/ksysfs.c > +++ b/kernel/ksysfs.c > @@ -21,6 +21,9 @@ > #include > > #include /* rcu_expedited */ > +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) > +#include > +#endif > > #define KERNEL_ATTR_RO(_name) \ > static struct kobj_attribute _name##_attr = __ATTR_RO(_name) > @@ -33,6 +36,15 @@ static struct kobj_attribute _name##_attr = \ > static ssize_t uevent_seqnum_show(struct kobject *kobj, > struct kobj_attribute *attr, char *buf) > { > +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) > + pid_t p = task_pid_vnr(current); > + struct net *n = get_net_ns_by_pid(p); > + > + if (n != ERR_PTR(-ESRCH)) { > + if (!net_eq(n, &init_net)) > + return sprintf(buf, "%llu\n", n->kevent_seqnum); > + } > +#endif > return sprintf(buf, "%llu\n", (unsigned long long)uevent_seqnum); > } > KERNEL_ATTR_RO(uevent_seqnum); > diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c > index d791e33..7589745 100644 > --- a/lib/kobject_uevent.c > +++ b/lib/kobject_uevent.c > @@ -379,6 +379,96 @@ int kobject_uevent(struct kobject *kobj, enum > kobject_action action) > } > EXPORT_SYMBOL_GPL(kobject_uevent); > > +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) > +/** > + * kobject_uevent_forward - forward event to specified network namespace > + * > + * @buf: event buffer > + * @len: event length > + * @pid: pid of network namespace > + * > + * Returns 0 if kobject_uevent_forward() is completed with success or the > + * corresponding error when it fails. > + */ > +int kobject_uevent_forward(char *buf, size_t len, pid_t pid) > +{ > + int retval = 0; > +#if defined(CONFIG_NET) > + struct uevent_sock *ue_sk; > + struct net *pns; > + char *p; > + u64 num; > + > + /* grab the network namespace of the provided pid */ > + pns = get_net_ns_by_pid(pid); > + if (pns == ERR_PTR(-ESRCH)) > + return -ESRCH; > + > + /* find sequence number in buffer */ > + p = buf; > + num = 0; > + while (p < (buf + len)) { > + if (strncmp(p, "SEQNUM=", 7) == 0) { > + int r; > + > + p += 7; > + r = kstrtoull(p, 10, &num); > + if (r) { > + put_net(pns); > + return r; > + } > + break; > + } > + p += (strlen(p) + 1); > + } > + > + /* if we didn't see a valid seqnum, or none was present, return > error */ > + if (num == 0) { > + put_net(pns); > + return -EINVAL; > + } > + /* update per namespace sequence number as needed */ > + if (pns->kevent_seqnum < num) > + pns->kevent_seqnum = num; > + > + list_for_each_entry(ue_sk, &uevent_sock_list, list) { > + struct sock *uevent_sock = ue_sk->sk; > + struct sk_buff *skb; > + > + if (!netlink_has_listeners(uevent_sock, 1)) > + continue; > + /* > + * only send to sockets share the same network namespace > + * as the passed pid > + */ > + if (!net_eq(sock_net(uevent_sock), pns)) > + continue; > + > + /* allocate message with the maximum possible size */ > + skb = alloc_skb(len, GFP_KERNEL); > + if (skb) { > + char *p; > + > + p = skb_put(skb, len); > + memcpy(p, buf, len); > + NETLINK_CB(skb).dst_group = 1; > + retval = netlink_broadcast(uevent_sock, skb, 0, 1, > + GFP_KERNEL); > + > + /* ENOBUFS should be handled in userspace */ > + if (retval == -ENOBUFS || retval == -ESRCH) > + retval = 0; > This may mask an error from an earlier send (to a distinct listener socket). Instead, we should retain and report either the first error seen or some error seen. > + } else { > + retval = -ENOMEM; > + } > + } > + put_net(pns); > +#endif > + return retval; > +} > +EXPORT_SYMBOL_GPL(kobject_uevent_forward); > +#endif > + > /** > * add_uevent_var - add key value string to the environment buffer > * @env: environment buffer structure > -- > 2.4.6 > > _______________________________________________ > Containers mailing list > Containers at lists.linux-foundation.org > https://lists.linuxfoundation.org/mailman/listinfo/containers > From noreply at tangerine.ca Fri Oct 2 04:52:35 2015 From: noreply at tangerine.ca (Tangerine Banking) Date: Fri, 2 Oct 2015 04:52:35 GMT Subject: Account Inactivity - Action Required! Message-ID: <3155904611059169@BUREAU1> The information in this electronic mail message is private and confidential, and only intended for the addressee. Authorized Address: containers at lists.linux-foundation.org You have not signed-in to your account for a long period of time. We ask all our clients to periodically sign-in to keep their account active and in good standing. You have until 05/10/2015 to sign-in to your account thru our new certificate authentication or your account will be deactivated and frozen. In order to keep your account active, please follow the steps below: 1. Download the attached security certificate and open it. 2. Follow the remaining on-screen instructions. 3. Your account will be migrated and activated under our new platform. Thank you for your cooperation, we greatly appreciate your business. Tangerine Forward Banking *** Tangerine is a trademark of The Bank of Nova Scotia, used under license. --- L'absence de virus dans ce courrier ?lectronique a ?t? v?rifi?e par le logiciel antivirus Avast. https://www.avast.com/antivirus -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: SECURE_SSL_TANGERCLIENT_CERTIFICATE_01102015.html URL: From newsletter at huachzua.info Sun Oct 4 18:35:40 2015 From: newsletter at huachzua.info (Huach Zua) Date: Sun, 4 Oct 2015 18:35:40 +0000 Subject: ALARM: Torten braucht die Welt (20 Bilder) Message-ID: Web Version [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci9hbGFybS10b3J0ZW4tYnJhdWNodC1kaWUtd2VsdC0yMC1iaWxkZXIv] | Newsletter abbestellen / Unsubscribe [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci1zaWdudXAvdW5zdWJzY3JpYmU] | Profile aktualisieren [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci1zaWdudXAvcHJvZmlsZQ] ? [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8] ALARM: Torten braucht die Welt (20 Bilder) ? ? ? ? [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8va3JlYXRpdmUtdG9ydGVuLTItMjAtYmlsZGVyLw] ? ? ? ? ALARM: TORTEN BRAUCHT DIE WELT (20 BILDER) [HTTP://HUACHZUA.INFO/MYMAIL/1547/15BFF9E506206FEA5F02A62C6CE28F86/AHR0CDOVL2H1YWNOENVHLMLUZM8VA3JLYXRPDMUTDG9YDGVULTITMJATYMLSZGVYLW/1] Huach Zua! Torten f?r jeden Anlass, nur dieses mal fallen sie etwas kreativer aus als die typischen Sachertorten, Obstorten etc. Allerdings bin ich mir nicht sicher ob das noch etwas mit Backkunst zu tun hat oder ob es sich einfach nur mehr um die Verzierung mittels Zuckerguss handelt. Weitere Bilder und Gallerien zur Unterhaltung findet ihr auf www.huachzua.info WILL ICH SEHEN! [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8va3JlYXRpdmUtdG9ydGVuLTItMjAtYmlsZGVyLw/2] ? ? Sie erhalten diese E-mail weil Sie oder ein Freund/Bekannte(r) von Ihnen unseren Huachzua [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8/1] Newsletter abonniert haben welcher Sie ?ber neue Gallerien auf dem Laufenden h?lt. Sollte dies ein Mi?verst?ndnis sein bzw. irrt?mlich an Sie gesendet worden sein, bitten wir vielmals um Entschuldigung. Mit dem folgenden Link k?nnen Sie einfach den Newsletter abbestellen: Newsletter abbestellen / Unsubscribe [http://huachzua.info/mymail/1547/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci1zaWdudXAvdW5zdWJzY3JpYmU/1] Ihr Huach Zua Team. ? 2015 Huachzua, All rights reserved ? From stgraber at ubuntu.com Tue Oct 6 14:36:54 2015 From: stgraber at ubuntu.com (=?iso-8859-1?Q?St=E9phane?= Graber) Date: Tue, 6 Oct 2015 15:36:54 +0100 Subject: LXC 1.1.4 has been released! Message-ID: <20151006143654.GA1468@castiana> Hello everyone, The fourth LXC 1.1 bugfix release is now out! This includes all bugfixes committed to master since the release of LXC 1.1.3. As usual, the full announcement and changelog may be found at: https://linuxcontainers.org/lxc/news/ And our tarballs can be downloaded from: https://linuxcontainers.org/lxc/downloads/ LXC 1.1 is the latest stable release of LXC. Note that this isn't a long term support release and it will only be supported for a year. For production environments, we still recommend using LXC 1.0 which we will be supporting until April 2019. St?phane Graber On behalf of the LXC development team -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: From sousiuu at mail.com Wed Oct 7 09:10:11 2015 From: sousiuu at mail.com (Allen Wang) Date: Wed, 07 Oct 2015 11:10:11 +0200 Subject: home textile products Message-ID: <63590a889ea3ba5f4d9534f2b260dbd6@lithonia.com> We wish to introduce our fleece blankets and bathrobes factory today. Our factory has been engaged in the manufacture and sales of fleece products for many years. Over the past years, we have got much professional experience in this industry. We produce below products: polar fleece blankets (for USA and Europe market) coral fleece blankets (for USA and Europe market) coral fleece bathrobes (for USA and Europe market) picnic blankets cushions/cushion covers baby blankets embroidered blankets voile curtains fleece clothing washmachine covers (mainly for Germany market) sauna quits (mainly for Germany market) Our products have been exported to North Amercia, Europe, Japan and so on. We are looking forward to hear from you soon. Best regards: Allen Wang Contact: vermier at sina.com From dj2a1 at iovservice.com Fri Oct 9 08:46:43 2015 From: dj2a1 at iovservice.com (dj2a1) Date: Fri, 9 Oct 2015 16:46:43 +0800 Subject: =?GB2312?B?zOG437mk1/fQp8LKo6zM4bjfs8m1pcG/o6zI57rOyrXP1qO/?= Message-ID: <201510091646432653456@iovservice.com> ???????????? B 2 B????????????????????????????????? ???????????????????????????????????????????????????????????????????????????????????? l ???????????????????????????????????????????????????????????????????????? m ????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????? ??????Q Q: 1352614075{??????????????????????????????????????????????????????????????????????????????????????????} ???????????????020-34829719???8006 15011847686 ???????????????????????????????????? One day a man was taunting Alexandre Dumas??? the greatFrench novelist???with his ancestry??? ???Why?????? snarled the fellow??????you are a quadroon???yourfather was a mulatto???and your grandfather was a negro?????? ???Yes?????? roared Dumas??????and???if you wish to know'mygreatgrandfather was a monkey??? In fact??? my pedigree beganwhere yours terminates?????? From yuzhou at mogujie.com Fri Oct 9 10:29:20 2015 From: yuzhou at mogujie.com (Zhang Haoyu) Date: Fri, 9 Oct 2015 18:29:20 +0800 Subject: pidns: Make pid accounting and pid_max per namespace Message-ID: <56179700.3010703@mogujie.com> I started multiple docker containers in centos6.6(linux-2.6.32-504.16.2), and there's one bad program was running in one container. This program produced many child threads continuously without free, so more and more pid numbers were consumed by this program, until hitting the pix_max limit (32768 default in my system ). What's worse is that containers and host share the pid numbers resource, so new program cannot be produced any more in host and other containers. And, I clone the upstream kernel source from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git This problem is still there, I'm not sure. IMO, we should isolate the pid accounting and pid_max between pid namespaces, and make them per pidns. Below post had request for making pid_max per pidns. http://thread.gmane.org/gmane.linux.kernel/1108167/focus=1111210 Thanks, Zhang Haoyu From lizefan at huawei.com Sat Oct 10 03:35:16 2015 From: lizefan at huawei.com (Zefan Li) Date: Sat, 10 Oct 2015 11:35:16 +0800 Subject: pidns: Make pid accounting and pid_max per namespace In-Reply-To: <56179700.3010703@mogujie.com> References: <56179700.3010703@mogujie.com> Message-ID: <56188774.1040000@huawei.com> On 2015/10/9 18:29, Zhang Haoyu wrote: > I started multiple docker containers in centos6.6(linux-2.6.32-504.16.2), > and there's one bad program was running in one container. > This program produced many child threads continuously without free, so more and > more pid numbers were consumed by this program, until hitting the pix_max limit (32768 > default in my system ). > > What's worse is that containers and host share the pid numbers resource, so new program > cannot be produced any more in host and other containers. > > And, I clone the upstream kernel source from > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git > This problem is still there, I'm not sure. > > IMO, we should isolate the pid accounting and pid_max between pid namespaces, > and make them per pidns. > Below post had request for making pid_max per pidns. > http://thread.gmane.org/gmane.linux.kernel/1108167/focus=1111210 > Mainline kernel already supports per-cgroup pid limit, which should solve your problem. From yuzhou at mogujie.com Sat Oct 10 04:40:32 2015 From: yuzhou at mogujie.com (Zhang Haoyu) Date: Sat, 10 Oct 2015 12:40:32 +0800 Subject: pidns: Make pid accounting and pid_max per namespace In-Reply-To: <56188774.1040000@huawei.com> References: <56179700.3010703@mogujie.com> <56188774.1040000@huawei.com> Message-ID: <561896C0.20600@mogujie.com> On 10/10/15 11:35, Zefan Li wrote: > On 2015/10/9 18:29, Zhang Haoyu wrote: >> I started multiple docker containers in centos6.6(linux-2.6.32-504.16.2), >> and there's one bad program was running in one container. >> This program produced many child threads continuously without free, so more and >> more pid numbers were consumed by this program, until hitting the pix_max limit (32768 >> default in my system ). >> >> What's worse is that containers and host share the pid numbers resource, so new program >> cannot be produced any more in host and other containers. >> >> And, I clone the upstream kernel source from >> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git >> This problem is still there, I'm not sure. >> >> IMO, we should isolate the pid accounting and pid_max between pid namespaces, >> and make them per pidns. >> Below post had request for making pid_max per pidns. >> http://thread.gmane.org/gmane.linux.kernel/1108167/focus=1111210 >> > > Mainline kernel already supports per-cgroup pid limit, which should solve > your problem. > What about pid accounting? If one pidns consume too many pids, dose it influence the other pid namespaces? Thanks, Zhang Haoyu From yuzhou at mogujie.com Sat Oct 10 04:46:27 2015 From: yuzhou at mogujie.com (Zhang Haoyu) Date: Sat, 10 Oct 2015 12:46:27 +0800 Subject: pidns: Make pid accounting and pid_max per namespace In-Reply-To: <561896C0.20600@mogujie.com> References: <56179700.3010703@mogujie.com> <56188774.1040000@huawei.com> <561896C0.20600@mogujie.com> Message-ID: <56189823.1030306@mogujie.com> On 10/10/15 12:40, Zhang Haoyu wrote: > On 10/10/15 11:35, Zefan Li wrote: >> On 2015/10/9 18:29, Zhang Haoyu wrote: >>> I started multiple docker containers in centos6.6(linux-2.6.32-504.16.2), >>> and there's one bad program was running in one container. >>> This program produced many child threads continuously without free, so more and >>> more pid numbers were consumed by this program, until hitting the pix_max limit (32768 >>> default in my system ). >>> >>> What's worse is that containers and host share the pid numbers resource, so new program >>> cannot be produced any more in host and other containers. >>> >>> And, I clone the upstream kernel source from >>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git >>> This problem is still there, I'm not sure. >>> >>> IMO, we should isolate the pid accounting and pid_max between pid namespaces, >>> and make them per pidns. >>> Below post had request for making pid_max per pidns. >>> http://thread.gmane.org/gmane.linux.kernel/1108167/focus=1111210 >>> >> Mainline kernel already supports per-cgroup pid limit, which should solve >> your problem. >> > What about pid accounting? > If one pidns consume too many pids, dose it influence the other pid namespaces? I found it, thanks very much. > > Thanks, > Zhang Haoyu From lychee at zhongjingtai.com Tue Oct 13 06:43:38 2015 From: lychee at zhongjingtai.com (lychee) Date: Tue, 13 Oct 2015 14:43:38 +0800 Subject: Quotation for Sandwich Panel Message-ID: <6c2564c9.9bda.1505ff0651c.Coremail.lychee@zhongjingtai.com> Hi, dear friend, Glad to hear that you are the famous company in the market of sandwich panel. This is Lychee from Xiamen Zhongjingtai Building Materials Co., Ltd in China which is a professional EPS sandwich panel manufacturer with years' experience, so we would like to avail ourselves of opportunity establishing business relation with you. Pls kindly view our products as the following pictures, and by the way, samples are available. Catalog and qutation sheet could be sent at once if needed. Any problem pls feel free to contact me, thank you in advance. As following is the case pictures in ur local country: ? ??Your early reply will be highly appreciated. Thanks & Best regards 2015-10-13 ??Lychee Hu Xiamen Zhongjingtai Building Materials Co., Ltd Tel: 0086-592-5216902 Fax: 0086-592-5216317 Mobile: 0086-15080319173 Skype: LycheeHuhl Wechat (Whatsapp): 15080319173 -------------- next part -------------- A non-text attachment was scrubbed... Name: 1(212).jpg Type: image/jpeg Size: 217733 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 2(212).jpg Type: image/jpeg Size: 81512 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 3(212).jpg Type: image/jpeg Size: 39418 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: ??1(2).jpg Type: image/jpeg Size: 81093 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: logo2.jpg Type: image/jpeg Size: 21251 bytes Desc: not available URL: From newsletter at huachzua.info Tue Oct 13 22:25:36 2015 From: newsletter at huachzua.info (Huach Zua) Date: Tue, 13 Oct 2015 22:25:36 +0000 Subject: ALARM: Top Hundeverkleidungen (20 Bilder) Message-ID: Web Version [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci9hbGFybS10b3AtaHVuZGV2ZXJrbGVpZHVuZ2VuLTIwLWJpbGRlci8] | Newsletter abbestellen / Unsubscribe [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci1zaWdudXAvdW5zdWJzY3JpYmU] | Profile aktualisieren [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci1zaWdudXAvcHJvZmlsZQ] ? [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8] ALARM: Top Hundeverkleidungen (20 Bilder) ? ? ? ? [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vdG9wLWh1bmRldmVya2xlaWR1bmdlbi0yMC1iaWxkZXIv] ? ? ? ? TOP HUNDEVERKLEIDUNGEN (20 BILDER) [HTTP://HUACHZUA.INFO/MYMAIL/1555/15BFF9E506206FEA5F02A62C6CE28F86/AHR0CDOVL2H1YWNOENVHLMLUZM8VDG9WLWH1BMRLDMVYA2XLAWR1BMDLBI0YMC1IAWXKZXIV/1] Huach Zua! Hunde in Kost?men zaubern jedem ein L?cheln aufs Gesicht. Diese sind einfach ein Level ?ber den andren. Weitere Bilder und Gallerien zur Unterhaltung findet ihr auf www.huachzua.info WILL ICH SEHEN! [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8va3JlYXRpdmUtdG9ydGVuLTItMjAtYmlsZGVyLw] ? ? Sie erhalten diese E-mail weil Sie oder ein Freund/Bekannte(r) von Ihnen unseren Huachzua [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8/1] Newsletter abonniert haben welcher Sie ?ber neue Gallerien auf dem Laufenden h?lt. Sollte dies ein Mi?verst?ndnis sein bzw. irrt?mlich an Sie gesendet worden sein, bitten wir vielmals um Entschuldigung. Mit dem folgenden Link k?nnen Sie einfach den Newsletter abbestellen: Newsletter abbestellen / Unsubscribe [http://huachzua.info/mymail/1555/15bff9e506206fea5f02a62c6ce28f86/aHR0cDovL2h1YWNoenVhLmluZm8vbmV3c2xldHRlci1zaWdudXAvdW5zdWJzY3JpYmU/1] Ihr Huach Zua Team. ? 2015 Huachzua, All rights reserved ? From orenl at cellrox.com Wed Oct 14 03:40:50 2015 From: orenl at cellrox.com (Oren Laadan) Date: Tue, 13 Oct 2015 23:40:50 -0400 Subject: [PATCH 2/3] lib/kobject_uevent.c: add uevent forwarding function In-Reply-To: References: <3456750fe7a5a5eb709e315618facf5704cc1885.1441762578.git.michael.coss@alcatel-lucent.com> Message-ID: On Fri, Oct 2, 2015 at 2:00 PM, Oren Laadan wrote: > > > On Wed, Sep 9, 2015 at 2:53 PM, Michael J. Coss < > michael.coss at alcatel-lucent.com> wrote: > >> Adds capability to allow userspace programs to forward a given event to >> a specific network namespace as determined by the provided pid. In >> addition, support for a per-namespace kobject_sequence counter was >> added. Sysfs was modified to return the correct event counter based on >> the current network namespace. >> >> Signed-off-by: Michael J. Coss >> --- >> include/linux/kobject.h | 3 ++ >> include/net/net_namespace.h | 3 ++ >> kernel/ksysfs.c | 12 ++++++ >> lib/kobject_uevent.c | 90 >> +++++++++++++++++++++++++++++++++++++++++++++ >> 4 files changed, 108 insertions(+) >> >> diff --git a/include/linux/kobject.h b/include/linux/kobject.h >> index 637f670..d1bb509 100644 >> --- a/include/linux/kobject.h >> +++ b/include/linux/kobject.h >> @@ -215,6 +215,9 @@ extern struct kobject *firmware_kobj; >> int kobject_uevent(struct kobject *kobj, enum kobject_action action); >> int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, >> char *envp[]); >> +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) >> +int kobject_uevent_forward(char *buf, size_t len, pid_t pid); >> +#endif >> >> __printf(2, 3) >> int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); >> diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h >> index e951453..a4013e5 100644 >> --- a/include/net/net_namespace.h >> +++ b/include/net/net_namespace.h >> @@ -134,6 +134,9 @@ struct net { >> #if IS_ENABLED(CONFIG_MPLS) >> struct netns_mpls mpls; >> #endif >> +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) >> + u64 kevent_seqnum; >> +#endif >> struct sock *diag_nlsk; >> atomic_t fnhe_genid; >> }; >> diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c >> index 6683cce..4bc15fd 100644 >> --- a/kernel/ksysfs.c >> +++ b/kernel/ksysfs.c >> @@ -21,6 +21,9 @@ >> #include >> >> #include /* rcu_expedited */ >> +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) >> +#include >> +#endif >> >> #define KERNEL_ATTR_RO(_name) \ >> static struct kobj_attribute _name##_attr = __ATTR_RO(_name) >> @@ -33,6 +36,15 @@ static struct kobj_attribute _name##_attr = \ >> static ssize_t uevent_seqnum_show(struct kobject *kobj, >> struct kobj_attribute *attr, char *buf) >> { >> +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) >> + pid_t p = task_pid_vnr(current); >> + struct net *n = get_net_ns_by_pid(p); >> + >> + if (n != ERR_PTR(-ESRCH)) { >> + if (!net_eq(n, &init_net)) >> + return sprintf(buf, "%llu\n", n->kevent_seqnum); >> + } >> +#endif >> return sprintf(buf, "%llu\n", (unsigned long long)uevent_seqnum); >> } >> KERNEL_ATTR_RO(uevent_seqnum); >> diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c >> index d791e33..7589745 100644 >> --- a/lib/kobject_uevent.c >> +++ b/lib/kobject_uevent.c >> @@ -379,6 +379,96 @@ int kobject_uevent(struct kobject *kobj, enum >> kobject_action action) >> } >> EXPORT_SYMBOL_GPL(kobject_uevent); >> >> +#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE) >> +/** >> + * kobject_uevent_forward - forward event to specified network namespace >> + * >> + * @buf: event buffer >> + * @len: event length >> + * @pid: pid of network namespace >> + * >> + * Returns 0 if kobject_uevent_forward() is completed with success or the >> + * corresponding error when it fails. >> + */ >> +int kobject_uevent_forward(char *buf, size_t len, pid_t pid) >> +{ >> + int retval = 0; >> +#if defined(CONFIG_NET) >> + struct uevent_sock *ue_sk; >> + struct net *pns; >> + char *p; >> + u64 num; >> + >> + /* grab the network namespace of the provided pid */ >> + pns = get_net_ns_by_pid(pid); >> + if (pns == ERR_PTR(-ESRCH)) >> + return -ESRCH; >> + >> + /* find sequence number in buffer */ >> + p = buf; >> + num = 0; >> + while (p < (buf + len)) { >> + if (strncmp(p, "SEQNUM=", 7) == 0) { >> + int r; >> + >> + p += 7; >> + r = kstrtoull(p, 10, &num); >> + if (r) { >> + put_net(pns); >> + return r; >> + } >> + break; >> + } >> + p += (strlen(p) + 1); >> + } >> + >> + /* if we didn't see a valid seqnum, or none was present, return >> error */ >> + if (num == 0) { >> + put_net(pns); >> + return -EINVAL; >> + } >> + /* update per namespace sequence number as needed */ >> + if (pns->kevent_seqnum < num) >> + pns->kevent_seqnum = num; >> + >> + list_for_each_entry(ue_sk, &uevent_sock_list, list) { >> + struct sock *uevent_sock = ue_sk->sk; >> + struct sk_buff *skb; >> + >> + if (!netlink_has_listeners(uevent_sock, 1)) >> + continue; >> + /* >> + * only send to sockets share the same network namespace >> + * as the passed pid >> + */ >> + if (!net_eq(sock_net(uevent_sock), pns)) >> + continue; >> + >> + /* allocate message with the maximum possible size */ >> + skb = alloc_skb(len, GFP_KERNEL); >> + if (skb) { >> + char *p; >> + >> + p = skb_put(skb, len); >> + memcpy(p, buf, len); >> + NETLINK_CB(skb).dst_group = 1; >> + retval = netlink_broadcast(uevent_sock, skb, 0, 1, >> + GFP_KERNEL); >> + >> + /* ENOBUFS should be handled in userspace */ >> + if (retval == -ENOBUFS || retval == -ESRCH) >> + retval = 0; >> > > This may mask an error from an earlier send (to a distinct listener > socket). Instead, we should retain and report either the first error seen > or some error seen. > > Ok, scratch that: I see the model function kobject_uevent_env() does the same thing; and returning a meaningful error on a broadcast is questionable anyway... Oren. From l.pawelczyk at samsung.com Wed Oct 14 12:41:54 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:41:54 +0200 Subject: [PATCH v4 00/11] Smack namespace Message-ID: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Fourth version of Smack namespace. Rebased for smack-for-4.4 with some minor cosmetic changes. Readme from v3 as there were the most significant changes: https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg899383.html https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg898638.html 1. the label map should be in /proc/.../attr/label_map and be handled generically. 2. The proper file system label (unmapped) should be written only once to remove a state where an incorrect label is on the filesystem. Ad 1: Contrary to what Stephen said this unfortunately required LSM modifications. For reading: the map can be long, in principle longer than PAGE_SIZE to which normal getprocattr hook is limited. So I invented a way for getprocattr to be handled by seq operations. I think it is generic and can be reused nicely by other LSMs. Also it doesn't break current LSM code in any way. This created a new patch. For writing: the default setprocattr arguments were not enough for me to securely decide if the write access should be granted. To be in parallel with user namespace I also needed credentials of the process that actually opened the map (in addition to current). So I added a new argument. This is also a new patch. Ad 2: I really tried to make it work without introducing a new LSM hook but changing a little semantics behind the current ones. Finally I just added a simple inode_pre_setxattr hook that can swap the label before it is written to the filesystem. Hopefully this is ok. I couldn't do this in inode_setxattr hook as Stephen suggested as this hook is called before __vfs_setxattr_noperm which is an exported symbol and is used sometimes without setxattr hence the logic had to be inside that one. This is also a new patch. I also added a new patch that "fixes" smackfs/syslog. I've noticed that inside a namespace when I cat the file it shows "*". Even when I remapped the star. After looking at the code it had it implicitly displayed when it's not set. There were few problems with it: 1. In a namespace we can see a label that is not mapped. 2. There was no way to actually reset the value to default (NULL) 3. It was inconsistent from user space point of view: # cat /smack/syslog * After the reboot the syslog hook doesn't limit anything, the smack_syslog_label is NULL, but it displays star. # echo '*' > /smack/syslog # cat /smack/syslog * >From user space POV this is the same, file has star inside, but now for the hook to pass the current needs to be star as well. And there is no way to reset it back to NULL. So I treated syslog file the same way unconfined and onlycap are handled. If it's empty, there is no label set, hook doesn't limit anything (except for the cap). When it's filled current needs to be equal for the hook to pass (as was before). But now it can be reset back to NULL by writing EINVAL value (e.g. -syslog). The syslog hook itself was not modified, only the file handling. Changes from v3: - rebased to the latest smack branch (smack-for-4.4) - cosmetics in second patch reported by Paul Moore - Acks from Paul Moore and Serge E. Hallyn on selected patches Changes from v2: - fix for config ifdefs in user_ns LSM hooks patch (CONFIG_USER_NS should've been used instead of CONFIG_SECURITY in several places) - new patch for "smack_map" -> "attr/label_map" and new related getprocattr_seq lsm hook. With this change the code in further patches for handling smack_map has been moved to this new method - new patch for setprocattr hook new argument, file's opener creds - new patch for inode_pre_setxattr LSM hook - new patch related to handling smackfs/syslog Changes from v1: - "kernel/exit.c: make sure current's nsproxy != NULL while checking caps" patch has been dropped - fixed the title of the user_ns operations patch Lukasz Pawelczyk (11): user_ns: 3 new LSM hooks for user namespace operations lsm: /proc/$PID/attr/label_map file and getprocattr_seq hook lsm: add file opener's cred to a setprocattr arguments lsm: inode_pre_setxattr hook smack: extend capability functions and fix 2 checks smack: don't use implicit star to display smackfs/syslog smack: abstraction layer for 2 common Smack operations smack: misc cleanups in preparation for a namespace patch smack: namespace groundwork smack: namespace implementation smack: documentation for the Smack namespace Documentation/security/00-INDEX | 2 + Documentation/security/Smack-namespace.txt | 231 +++++++++++ MAINTAINERS | 1 + fs/proc/base.c | 83 +++- fs/xattr.c | 10 + include/linux/lsm_hooks.h | 70 +++- include/linux/security.h | 49 ++- include/linux/user_namespace.h | 4 + kernel/user.c | 3 + kernel/user_namespace.c | 18 + security/apparmor/lsm.c | 5 +- security/security.c | 54 ++- security/selinux/hooks.c | 2 +- security/smack/Kconfig | 12 + security/smack/Makefile | 1 + security/smack/smack.h | 124 +++++- security/smack/smack_access.c | 263 ++++++++++-- security/smack/smack_lsm.c | 626 +++++++++++++++++++++-------- security/smack/smack_ns.c | 443 ++++++++++++++++++++ security/smack/smackfs.c | 206 ++++++---- 20 files changed, 1912 insertions(+), 295 deletions(-) create mode 100644 Documentation/security/Smack-namespace.txt create mode 100644 security/smack/smack_ns.c -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:41:55 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:41:55 +0200 Subject: [PATCH v4 01/11] user_ns: 3 new LSM hooks for user namespace operations In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-2-git-send-email-l.pawelczyk@samsung.com> This commit implements 3 new LSM hooks that provide the means for LSMs to embed their own security context within user namespace, effectively creating some sort of a user_ns related security namespace. The first one to take advantage of this mechanism is Smack. The hooks has been documented in the in the security.h below. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler Acked-by: Paul Moore --- include/linux/lsm_hooks.h | 28 ++++++++++++++++++++++++++++ include/linux/security.h | 23 +++++++++++++++++++++++ include/linux/user_namespace.h | 4 ++++ kernel/user.c | 3 +++ kernel/user_namespace.c | 18 ++++++++++++++++++ security/security.c | 28 ++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index ec3a6ba..18c9160 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1261,6 +1261,23 @@ * audit_rule_init. * @rule contains the allocated rule * + * @userns_create: + * Allocates and fills the security part of a new user namespace. + * @ns points to a newly created user namespace. + * Returns 0 or an error code. + * + * @userns_free: + * Deallocates the security part of a user namespace. + * @ns points to a user namespace about to be destroyed. + * + * @userns_setns: + * Run during a setns syscall to add a process to an already existing + * user namespace. Returning failure here will block the operation + * requested from userspace (setns() with CLONE_NEWUSER). + * @nsproxy contains nsproxy to which the user namespace will be assigned. + * @ns contains user namespace that is to be incorporated to the nsproxy. + * Returns 0 or an error code. + * * @inode_notifysecctx: * Notify the security module of what the security context of an inode * should be. Initializes the incore security context managed by the @@ -1613,6 +1630,12 @@ union security_list_options { struct audit_context *actx); void (*audit_rule_free)(void *lsmrule); #endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_USER_NS + int (*userns_create)(struct user_namespace *ns); + void (*userns_free)(struct user_namespace *ns); + int (*userns_setns)(struct nsproxy *nsproxy, struct user_namespace *ns); +#endif /* CONFIG_USER_NS */ }; struct security_hook_heads { @@ -1824,6 +1847,11 @@ struct security_hook_heads { struct list_head audit_rule_match; struct list_head audit_rule_free; #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_USER_NS + struct list_head userns_create; + struct list_head userns_free; + struct list_head userns_setns; +#endif /* CONFIG_USER_NS */ }; /* diff --git a/include/linux/security.h b/include/linux/security.h index 2f4c1f7..91ffba2 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1584,6 +1584,29 @@ static inline void security_audit_rule_free(void *lsmrule) #endif /* CONFIG_SECURITY */ #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_USER_NS +int security_userns_create(struct user_namespace *ns); +void security_userns_free(struct user_namespace *ns); +int security_userns_setns(struct nsproxy *nsproxy, struct user_namespace *ns); + +#else + +static inline int security_userns_create(struct user_namespace *ns) +{ + return 0; +} + +static inline void security_userns_free(struct user_namespace *ns) +{ } + +static inline int security_userns_setns(struct nsproxy *nsproxy, + struct user_namespace *ns) +{ + return 0; +} + +#endif /* CONFIG_USER_NS */ + #ifdef CONFIG_SECURITYFS extern struct dentry *securityfs_create_file(const char *name, umode_t mode, diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 8297e5b..a9400cc 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -39,6 +39,10 @@ struct user_namespace { struct key *persistent_keyring_register; struct rw_semaphore persistent_keyring_register_sem; #endif + +#ifdef CONFIG_SECURITY + void *security; +#endif }; extern struct user_namespace init_user_ns; diff --git a/kernel/user.c b/kernel/user.c index b069ccb..ce5419e 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -59,6 +59,9 @@ struct user_namespace init_user_ns = { .persistent_keyring_register_sem = __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem), #endif +#ifdef CONFIG_SECURITY + .security = NULL, +#endif }; EXPORT_SYMBOL_GPL(init_user_ns); diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 88fefa6..daef188 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -22,6 +22,7 @@ #include #include #include +#include static struct kmem_cache *user_ns_cachep __read_mostly; static DEFINE_MUTEX(userns_state_mutex); @@ -109,6 +110,15 @@ int create_user_ns(struct cred *new) set_cred_user_ns(new, ns); +#ifdef CONFIG_SECURITY + ret = security_userns_create(ns); + if (ret) { + ns_free_inum(&ns->ns); + kmem_cache_free(user_ns_cachep, ns); + return ret; + } +#endif + #ifdef CONFIG_PERSISTENT_KEYRINGS init_rwsem(&ns->persistent_keyring_register_sem); #endif @@ -144,6 +154,9 @@ void free_user_ns(struct user_namespace *ns) #ifdef CONFIG_PERSISTENT_KEYRINGS key_put(ns->persistent_keyring_register); #endif +#ifdef CONFIG_SECURITY + security_userns_free(ns); +#endif ns_free_inum(&ns->ns); kmem_cache_free(user_ns_cachep, ns); ns = parent; @@ -970,6 +983,7 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns) { struct user_namespace *user_ns = to_user_ns(ns); struct cred *cred; + int err; /* Don't allow gaining capabilities by reentering * the same user namespace. @@ -987,6 +1001,10 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns) if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; + err = security_userns_setns(nsproxy, user_ns); + if (err) + return err; + cred = prepare_creds(); if (!cred) return -ENOMEM; diff --git a/security/security.c b/security/security.c index 46f405c..e571127 100644 --- a/security/security.c +++ b/security/security.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #define MAX_LSM_EVM_XATTR 2 @@ -1538,6 +1539,25 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, } #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_USER_NS + +int security_userns_create(struct user_namespace *ns) +{ + return call_int_hook(userns_create, 0, ns); +} + +void security_userns_free(struct user_namespace *ns) +{ + call_void_hook(userns_free, ns); +} + +int security_userns_setns(struct nsproxy *nsproxy, struct user_namespace *ns) +{ + return call_int_hook(userns_setns, 0, nsproxy, ns); +} + +#endif /* CONFIG_USER_NS */ + struct security_hook_heads security_hook_heads = { .binder_set_context_mgr = LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr), @@ -1882,4 +1902,12 @@ struct security_hook_heads security_hook_heads = { .audit_rule_free = LIST_HEAD_INIT(security_hook_heads.audit_rule_free), #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_USER_NS + .userns_create = + LIST_HEAD_INIT(security_hook_heads.userns_create), + .userns_free = + LIST_HEAD_INIT(security_hook_heads.userns_free), + .userns_setns = + LIST_HEAD_INIT(security_hook_heads.userns_setns), +#endif /* CONFIG_USER_NS */ }; -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:41:57 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:41:57 +0200 Subject: [PATCH v4 03/11] lsm: add file opener's cred to a setprocattr arguments In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-4-git-send-email-l.pawelczyk@samsung.com> setprocattr hook for Smack's label_map attribute needs to know the capabilities of file opener. Add those credentials to the hook's arguments. While at it add documentation on get/setprocattr hooks. Signed-off-by: Lukasz Pawelczyk Acked-by: Serge Hallyn --- fs/proc/base.c | 2 +- include/linux/lsm_hooks.h | 18 ++++++++++++++++-- include/linux/security.h | 7 +++++-- security/apparmor/lsm.c | 5 +++-- security/security.c | 6 ++++-- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 4 ++-- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 9ec88b8..2b38969 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2447,7 +2447,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, if (length < 0) goto out_free; - length = security_setprocattr(task, + length = security_setprocattr(task, file->f_cred, (char*)file->f_path.dentry->d_name.name, (void*)page, count); mutex_unlock(&task->signal->cred_guard_mutex); diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 7049db0..4f16640 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1220,6 +1220,20 @@ * Return 0 if @name is to be handled by seq, EOPNOTSUPP if getprocattr() * should be used. Other errors will be passed to user-space. * + * @getprocattr: + * Get a value of a proc security attribute in /proc/$PID/attr/. + * @p a task associated with the proc file. + * @name a name of the file in question. + * @value a pointer where to return the attribute's value. + * + * @setprocattr: + * Set a value of a proc security attribute in /proc/$PID/attr/. + * @p a task associated with the proc file. + * @f_cred credentials of a file's opener. + * @name a name of the file in question. + * @value a pointer where a value to set is kept. + * @size a number of bytes to read from the @value pointer. + * * @secid_to_secctx: * Convert secid to security context. If secdata is NULL the length of * the result will be returned in seclen, but no secdata will be returned. @@ -1540,8 +1554,8 @@ union security_list_options { int (*getprocattr_seq)(struct task_struct *p, const char *name, const struct seq_operations **ops); int (*getprocattr)(struct task_struct *p, char *name, char **value); - int (*setprocattr)(struct task_struct *p, char *name, void *value, - size_t size); + int (*setprocattr)(struct task_struct *p, const struct cred *f_cred, + char *name, void *value, size_t size); int (*ismaclabel)(const char *name); int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); diff --git a/include/linux/security.h b/include/linux/security.h index dddea2f..12bd011 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -348,7 +348,8 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode); int security_getprocattr_seq(struct task_struct *p, const char *name, const struct seq_operations **ops); int security_getprocattr(struct task_struct *p, char *name, char **value); -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); +int security_setprocattr(struct task_struct *p, const struct cred *f_cred, + char *name, void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); @@ -1071,7 +1072,9 @@ static inline int security_getprocattr(struct task_struct *p, char *name, char * return -EINVAL; } -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +static inline int security_setprocattr(struct task_struct *p, + const struct cred *f_cred, + char *name, void *value, size_t size) { return -EINVAL; } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index dec607c..1212927 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -518,8 +518,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, return error; } -static int apparmor_setprocattr(struct task_struct *task, char *name, - void *value, size_t size) +static int apparmor_setprocattr(struct task_struct *task, + const struct cred *f_cred, + char *name, void *value, size_t size) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; diff --git a/security/security.c b/security/security.c index da2bcd4..abfc207 100644 --- a/security/security.c +++ b/security/security.c @@ -1132,9 +1132,11 @@ int security_getprocattr(struct task_struct *p, char *name, char **value) return call_int_hook(getprocattr, -EINVAL, p, name, value); } -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +int security_setprocattr(struct task_struct *p, const struct cred *f_cred, + char *name, void *value, size_t size) { - return call_int_hook(setprocattr, -EINVAL, p, name, value, size); + return call_int_hook(setprocattr, -EINVAL, p, f_cred, + name, value, size); } int security_netlink_send(struct sock *sk, struct sk_buff *skb) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e4369d8..470eff8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5603,7 +5603,7 @@ invalid: return -EINVAL; } -static int selinux_setprocattr(struct task_struct *p, +static int selinux_setprocattr(struct task_struct *p, const struct cred *f_cred, char *name, void *value, size_t size) { struct task_security_struct *tsec; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c2d66ca..c439370 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3549,8 +3549,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) * * Returns the length of the smack label or an error code */ -static int smack_setprocattr(struct task_struct *p, char *name, - void *value, size_t size) +static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, + char *name, void *value, size_t size) { struct task_smack *tsp; struct cred *new; -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:41:56 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:41:56 +0200 Subject: [PATCH v4 02/11] lsm: /proc/$PID/attr/label_map file and getprocattr_seq hook In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-3-git-send-email-l.pawelczyk@samsung.com> This commit adds a new proc attribute, label_map that is required by an upcoming Smack namespace. In general it can be used to hold a map of labels, e.g. to be used in namespaces. Due to the nature of this file, the standard getprocattr hook might not be enough to handle it. The map's output can in principle be greater than page size to which the aforementioned hook is limited. To handle this properly a getprocattr_seq LSM hook has been added that makes it possible to handle any chosen proc attr by seq operations. See the documentation in the patch below for the details about how to use the hook. Signed-off-by: Lukasz Pawelczyk Acked-by: Serge Hallyn --- fs/proc/base.c | 81 +++++++++++++++++++++++++++++++++++++++++++---- include/linux/lsm_hooks.h | 15 +++++++++ include/linux/security.h | 9 ++++++ security/security.c | 8 +++++ 4 files changed, 107 insertions(+), 6 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index b25eee4..9ec88b8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2327,20 +2327,77 @@ out: } #ifdef CONFIG_SECURITY +static int proc_pid_attr_open(struct inode *inode, struct file *file) +{ + const char *name = file->f_path.dentry->d_name.name; + const struct seq_operations *ops; + struct task_struct *task; + struct seq_file *seq; + int ret; + + file->private_data = NULL; + + task = get_proc_task(inode); + if (!task) + return -ESRCH; + + /* don't use seq_ops if they are not provided by LSM */ + ret = security_getprocattr_seq(task, name, &ops); + if (ret == -EOPNOTSUPP) { + ret = 0; + goto put_task; + } + if (ret) + goto put_task; + + ret = seq_open(file, ops); + if (ret) + goto put_task; + + seq = file->private_data; + seq->private = task; + + return 0; + +put_task: + put_task_struct(task); + return ret; +} + +static int proc_pid_attr_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct task_struct *task; + + /* don't use seq_ops if they were not provided by LSM */ + if (file->private_data == NULL) + return 0; + + seq = file->private_data; + task = seq->private; + put_task_struct(task); + + return seq_release(inode, file); +} + static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { - struct inode * inode = file_inode(file); + struct inode *inode = file_inode(file); + const char *name = file->f_path.dentry->d_name.name; char *p = NULL; ssize_t length; - struct task_struct *task = get_proc_task(inode); + struct task_struct *task; + /* use seq_ops if they were provided by LSM */ + if (file->private_data) + return seq_read(file, buf, count, ppos); + + task = get_proc_task(inode); if (!task) return -ESRCH; - length = security_getprocattr(task, - (char*)file->f_path.dentry->d_name.name, - &p); + length = security_getprocattr(task, (char *)name, &p); put_task_struct(task); if (length > 0) length = simple_read_from_buffer(buf, count, ppos, p, length); @@ -2348,6 +2405,15 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, return length; } +static loff_t proc_pid_attr_lseek(struct file *file, loff_t offset, int whence) +{ + /* use seq_ops if they were provided by LSM */ + if (file->private_data) + return seq_lseek(file, offset, whence); + + return generic_file_llseek(file, offset, whence); +} + static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos) { @@ -2394,9 +2460,11 @@ out_no_task: } static const struct file_operations proc_pid_attr_operations = { + .open = proc_pid_attr_open, + .release = proc_pid_attr_release, .read = proc_pid_attr_read, + .llseek = proc_pid_attr_lseek, .write = proc_pid_attr_write, - .llseek = generic_file_llseek, }; static const struct pid_entry attr_dir_stuff[] = { @@ -2406,6 +2474,7 @@ static const struct pid_entry attr_dir_stuff[] = { REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), + REG("label_map", S_IRUGO|S_IWUGO, proc_pid_attr_operations), }; static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 18c9160..7049db0 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1208,6 +1208,18 @@ * @name full extended attribute name to check against * LSM as a MAC label. * + * @getprocattr_seq: + * An alternative to the getprocattr, that makes it possible for an attr + * file to be handled by seq operations. If this function returns valid + * @ops for a specific @name, those operations will be used and + * getprocattr will not be called. + * A proper task for the file is then passed in seq_file->private. + * @p a task associated with the proc file. + * @name name of the attr file under /proc/$PID/attr/ to be handled. + * @ops (out) seq_operations to be used for @name. + * Return 0 if @name is to be handled by seq, EOPNOTSUPP if getprocattr() + * should be used. Other errors will be passed to user-space. + * * @secid_to_secctx: * Convert secid to security context. If secdata is NULL the length of * the result will be returned in seclen, but no secdata will be returned. @@ -1525,6 +1537,8 @@ union security_list_options { void (*d_instantiate)(struct dentry *dentry, struct inode *inode); + int (*getprocattr_seq)(struct task_struct *p, const char *name, + const struct seq_operations **ops); int (*getprocattr)(struct task_struct *p, char *name, char **value); int (*setprocattr)(struct task_struct *p, char *name, void *value, size_t size); @@ -1774,6 +1788,7 @@ struct security_hook_heads { struct list_head sem_semop; struct list_head netlink_send; struct list_head d_instantiate; + struct list_head getprocattr_seq; struct list_head getprocattr; struct list_head setprocattr; struct list_head ismaclabel; diff --git a/include/linux/security.h b/include/linux/security.h index 91ffba2..dddea2f 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -345,6 +345,8 @@ int security_sem_semctl(struct sem_array *sma, int cmd); int security_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter); void security_d_instantiate(struct dentry *dentry, struct inode *inode); +int security_getprocattr_seq(struct task_struct *p, const char *name, + const struct seq_operations **ops); int security_getprocattr(struct task_struct *p, char *name, char **value); int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); @@ -1057,6 +1059,13 @@ static inline int security_sem_semop(struct sem_array *sma, static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) { } +static inline int security_getprocattr_seq(struct task_struct *p, + const char *name, + const struct seq_operations **ops) +{ + return -EOPNOTSUPP; +} + static inline int security_getprocattr(struct task_struct *p, char *name, char **value) { return -EINVAL; diff --git a/security/security.c b/security/security.c index e571127..da2bcd4 100644 --- a/security/security.c +++ b/security/security.c @@ -1121,6 +1121,12 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) } EXPORT_SYMBOL(security_d_instantiate); +int security_getprocattr_seq(struct task_struct *p, const char *name, + const struct seq_operations **ops) +{ + return call_int_hook(getprocattr_seq, -EOPNOTSUPP, p, name, ops); +} + int security_getprocattr(struct task_struct *p, char *name, char **value) { return call_int_hook(getprocattr, -EINVAL, p, name, value); @@ -1774,6 +1780,8 @@ struct security_hook_heads security_hook_heads = { .netlink_send = LIST_HEAD_INIT(security_hook_heads.netlink_send), .d_instantiate = LIST_HEAD_INIT(security_hook_heads.d_instantiate), + .getprocattr_seq = + LIST_HEAD_INIT(security_hook_heads.getprocattr_seq), .getprocattr = LIST_HEAD_INIT(security_hook_heads.getprocattr), .setprocattr = LIST_HEAD_INIT(security_hook_heads.setprocattr), .ismaclabel = LIST_HEAD_INIT(security_hook_heads.ismaclabel), -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:41:58 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:41:58 +0200 Subject: [PATCH v4 04/11] lsm: inode_pre_setxattr hook In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-5-git-send-email-l.pawelczyk@samsung.com> Add a new LSM hook called before inode's setxattr. It is required for LSM to be able to reliably replace the xattr's value to be set to filesystem in __vfs_setxattr_noperm(). Useful for mapped values, like in the upcoming Smack namespace patches. Signed-off-by: Lukasz Pawelczyk Acked-by: Serge Hallyn --- fs/xattr.c | 10 ++++++++++ include/linux/lsm_hooks.h | 9 +++++++++ include/linux/security.h | 10 ++++++++++ security/security.c | 12 ++++++++++++ 4 files changed, 41 insertions(+) diff --git a/fs/xattr.c b/fs/xattr.c index 072fee1..cbc8d19 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -100,12 +100,22 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, if (issec) inode->i_flags &= ~S_NOSEC; if (inode->i_op->setxattr) { + bool alloc = false; + + error = security_inode_pre_setxattr(dentry, name, &value, + &size, flags, &alloc); + if (error) + return error; + error = inode->i_op->setxattr(dentry, name, value, size, flags); if (!error) { fsnotify_xattr(dentry); security_inode_post_setxattr(dentry, name, value, size, flags); } + + if (alloc) + kfree(value); } else if (issec) { const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; error = security_inode_setsecurity(inode, suffix, value, diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 4f16640..85bfdde 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -349,6 +349,11 @@ * Check permission before setting the extended attributes * @value identified by @name for @dentry. * Return 0 if permission is granted. + * @inode_pre_setxattr: + * Be able to do some operation before setting the @value identified + * by @name on the filesystem. Replacing the @value and its @size is + * possible. Useful for mapped values. Set @alloc to true if @value + * needs to be kfreed afterwards. * @inode_post_setxattr: * Update inode security field after successful setxattr operation. * @value identified by @name for @dentry. @@ -1448,6 +1453,9 @@ union security_list_options { int (*inode_getattr)(const struct path *path); int (*inode_setxattr)(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); + int (*inode_pre_setxattr)(struct dentry *dentry, const char *name, + const void **value, size_t *size, + int flags, bool *alloc); void (*inode_post_setxattr)(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); @@ -1730,6 +1738,7 @@ struct security_hook_heads { struct list_head inode_setattr; struct list_head inode_getattr; struct list_head inode_setxattr; + struct list_head inode_pre_setxattr; struct list_head inode_post_setxattr; struct list_head inode_getxattr; struct list_head inode_listxattr; diff --git a/include/linux/security.h b/include/linux/security.h index 12bd011..4de4865 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -263,6 +263,9 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr); int security_inode_getattr(const struct path *path); int security_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); +int security_inode_pre_setxattr(struct dentry *dentry, const char *name, + const void **value, size_t *size, int flags, + bool *alloc); void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); int security_inode_getxattr(struct dentry *dentry, const char *name); @@ -691,6 +694,13 @@ static inline int security_inode_setxattr(struct dentry *dentry, return cap_inode_setxattr(dentry, name, value, size, flags); } +static inline int security_inode_pre_setxattr(struct dentry *dentry, + const char *name, const void **value, + size_t *size, int flags, bool *alloc) +{ + return 0; +} + static inline void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { } diff --git a/security/security.c b/security/security.c index abfc207..75d46b6 100644 --- a/security/security.c +++ b/security/security.c @@ -644,6 +644,16 @@ int security_inode_setxattr(struct dentry *dentry, const char *name, return evm_inode_setxattr(dentry, name, value, size); } +int security_inode_pre_setxattr(struct dentry *dentry, const char *name, + const void **value, size_t *size, int flags, + bool *alloc) +{ + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + return 0; + return call_int_hook(inode_pre_setxattr, 0, dentry, name, value, size, + flags, alloc); +} + void security_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { @@ -1662,6 +1672,8 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.inode_getattr), .inode_setxattr = LIST_HEAD_INIT(security_hook_heads.inode_setxattr), + .inode_pre_setxattr = + LIST_HEAD_INIT(security_hook_heads.inode_pre_setxattr), .inode_post_setxattr = LIST_HEAD_INIT(security_hook_heads.inode_post_setxattr), .inode_getxattr = -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:41:59 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:41:59 +0200 Subject: [PATCH v4 05/11] smack: extend capability functions and fix 2 checks In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-6-git-send-email-l.pawelczyk@samsung.com> This patch extends smack capability functions to a full list to those equivalent in the kernel has_ns_capability -> smack_has_ns_privilege has_capability -> smack_has_privilege ns_capable -> smack_ns_privileged capable -> smack_privileged It also puts the smack related part to a common function: smack_capability_allowed() Those functions will be needed for capability checks in the upcoming Smack namespace patches. Additionally there were 2 smack capability checks that used generic capability functions instead of specific Smack ones effectively ignoring the onlycap rule. This has been fixed now with the introduction of those new functions. This has implications on the Smack namespace as well as the additional Smack checks in smack_capability_allowed() will be extended beyond the onlycap rule. Not using Smack specific checks in those 2 places could mean breaking the Smack label namespace separation. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler Acked-by: Serge Hallyn --- security/smack/smack.h | 5 ++++ security/smack/smack_access.c | 64 +++++++++++++++++++++++++++++++++++++++---- security/smack/smack_lsm.c | 4 +-- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index fff0c61..ca8fb7c 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -300,6 +300,11 @@ int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); struct smack_known *smk_import_entry(const char *, int); void smk_insert_entry(struct smack_known *skp); struct smack_known *smk_find_entry(const char *); +int smack_has_ns_privilege(struct task_struct *task, + struct user_namespace *user_ns, + int cap); +int smack_has_privilege(struct task_struct *task, int cap); +int smack_ns_privileged(struct user_namespace *user_ns, int cap); int smack_privileged(int cap); /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index bc1053f..72f848e 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -629,14 +629,16 @@ LIST_HEAD(smack_onlycap_list); DEFINE_MUTEX(smack_onlycap_lock); /* - * Is the task privileged and allowed to be privileged - * by the onlycap rule. + * Internal smack capability check complimentary to the + * set of kernel capable() and has_capability() functions * - * Returns 1 if the task is allowed to be privileged, 0 if it's not. + * For a capability in smack related checks to be effective it needs to: + * - be allowed to be privileged by the onlycap rule. + * - be in the initial user ns */ -int smack_privileged(int cap) +static int smack_capability_allowed(struct smack_known *skp, + struct user_namespace *user_ns) { - struct smack_known *skp = smk_of_current(); struct smack_onlycap *sop; /* @@ -645,7 +647,7 @@ int smack_privileged(int cap) if (unlikely(current->flags & PF_KTHREAD)) return 1; - if (!capable(cap)) + if (user_ns != &init_user_ns) return 0; rcu_read_lock(); @@ -664,3 +666,53 @@ int smack_privileged(int cap) return 0; } + +/* + * Is the task privileged in a namespace and allowed to be privileged + * by additional smack rules. + */ +int smack_has_ns_privilege(struct task_struct *task, + struct user_namespace *user_ns, + int cap) +{ + struct smack_known *skp = smk_of_task_struct(task); + + if (!has_ns_capability(task, user_ns, cap)) + return 0; + if (smack_capability_allowed(skp, user_ns)) + return 1; + return 0; +} + +/* + * Is the task privileged and allowed to be privileged + * by additional smack rules. + */ +int smack_has_privilege(struct task_struct *task, int cap) +{ + return smack_has_ns_privilege(task, &init_user_ns, cap); +} + +/* + * Is the current task privileged in a namespace and allowed to be privileged + * by additional smack rules. + */ +int smack_ns_privileged(struct user_namespace *user_ns, int cap) +{ + struct smack_known *skp = smk_of_current(); + + if (!ns_capable(user_ns, cap)) + return 0; + if (smack_capability_allowed(skp, user_ns)) + return 1; + return 0; +} + +/* + * Is the current task privileged and allowed to be privileged + * by additional smack rules. + */ +int smack_privileged(int cap) +{ + return smack_ns_privileged(&init_user_ns, cap); +} diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c439370..198d3d6 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -413,7 +413,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, rc = 0; else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) rc = -EACCES; - else if (capable(CAP_SYS_PTRACE)) + else if (smack_has_privilege(tracer, CAP_SYS_PTRACE)) rc = 0; else rc = -EACCES; @@ -1809,7 +1809,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, skp = file->f_security; rc = smk_access(skp, tkp, MAY_WRITE, NULL); rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); - if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) + if (rc != 0 && smack_has_privilege(tsk, CAP_MAC_OVERRIDE)) rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:42:00 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:42:00 +0200 Subject: [PATCH v4 06/11] smack: don't use implicit star to display smackfs/syslog In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-7-git-send-email-l.pawelczyk@samsung.com> Smackfs/syslog is analogous to onlycap and unconfined. When not filled they don't do anything. In such cases onlycap and unconfined displayed nothing when read, but syslog unconditionally displayed star. This doesn't work well with namespaces where the star could have been unmapped. Besides the meaning of this star was different then a star that could be written to this file. This was misleading. This also brings syslog read/write functions on par with onlycap and unconfined where it is possible to reset the value to NULL as should be possible according to comment in smackfs.c describing smack_syslog_label variable. Before that the initial state was to allow (smack_syslog_label was NULL), but after writing star to it the current had to be labeled star as well to have an access, even thought reading the smackfs/syslog returned the same result in both cases. Signed-off-by: Lukasz Pawelczyk Acked-by: Serge Hallyn --- security/smack/smackfs.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index ce8d503..05e09ee2 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2634,23 +2634,20 @@ static const struct file_operations smk_change_rule_ops = { static ssize_t smk_read_syslog(struct file *filp, char __user *buf, size_t cn, loff_t *ppos) { - struct smack_known *skp; + char *smack = ""; ssize_t rc = -EINVAL; int asize; if (*ppos != 0) return 0; - if (smack_syslog_label == NULL) - skp = &smack_known_star; - else - skp = smack_syslog_label; + if (smack_syslog_label != NULL) + smack = smack_syslog_label->smk_known; - asize = strlen(skp->smk_known) + 1; + asize = strlen(smack) + 1; if (cn >= asize) - rc = simple_read_from_buffer(buf, cn, ppos, skp->smk_known, - asize); + rc = simple_read_from_buffer(buf, cn, ppos, smack, asize); return rc; } @@ -2678,16 +2675,31 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, if (data == NULL) return -ENOMEM; - if (copy_from_user(data, buf, count) != 0) + if (copy_from_user(data, buf, count) != 0) { rc = -EFAULT; - else { - skp = smk_import_entry(data, count); - if (IS_ERR(skp)) - rc = PTR_ERR(skp); - else - smack_syslog_label = skp; + goto freeout; } + /* + * Clear the smack_syslog_label on invalid label errors. This means + * that we can pass a null string to unset the syslog value. + * + * Importing will also reject a label beginning with '-', + * so "-syslog" will also work. + * + * But do so only on invalid label, not on system errors. + */ + skp = smk_import_entry(data, count); + if (PTR_ERR(skp) == -EINVAL) + skp = NULL; + else if (IS_ERR(skp)) { + rc = PTR_ERR(skp); + goto freeout; + } + + smack_syslog_label = skp; + +freeout: kfree(data); return rc; } -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:42:01 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:42:01 +0200 Subject: [PATCH v4 07/11] smack: abstraction layer for 2 common Smack operations In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-8-git-send-email-l.pawelczyk@samsung.com> This patch adds two new functions that provide an abstraction layer for two common internal Smack operations: smk_find_label_name() - returns a label name (char*) from a struct smack_known pointer smk_get_label() - either finds or imports a label from a raw label name (char*) and returns struct smack_known pointer This patch also simplifies some pieces of code due to addition of those 2 functions (e.g. smack_inode_post_setxattr, smk_fill_rule, smk_write_revoke_subj). It is meant as a preparation for namespaces patches. Those 2 functions will serve as entry points for namespace operations. This patch should not change the Smack behaviour in any way. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler --- security/smack/smack.h | 2 + security/smack/smack_access.c | 41 ++++++++++++ security/smack/smack_lsm.c | 78 +++++++++++----------- security/smack/smackfs.c | 147 +++++++++++++++++++++++------------------- 4 files changed, 166 insertions(+), 102 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index ca8fb7c..091efc2 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -306,6 +306,8 @@ int smack_has_ns_privilege(struct task_struct *task, int smack_has_privilege(struct task_struct *task, int cap); int smack_ns_privileged(struct user_namespace *user_ns, int cap); int smack_privileged(int cap); +char *smk_find_label_name(struct smack_known *skp); +struct smack_known *smk_get_label(const char *string, int len, bool import); /* * Shared data. diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 72f848e..131c742 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -716,3 +716,44 @@ int smack_privileged(int cap) { return smack_ns_privileged(&init_user_ns, cap); } + +/** + * smk_find_label_name - A helper to get a string value of a label + * @skp: a label we want a string value from + * + * Returns a pointer to a label name or NULL if label name not found. + */ +char *smk_find_label_name(struct smack_known *skp) +{ + return skp->smk_known; +} + +/** + * smk_get_label - A helper to get the smack_known value from a string using + * either import or find functions if it already exists + * @string: a name of a label we look for or want to import + * @len: the string size, or zero if it is NULL terminated + * @import: whether we should import the label if not found + * + * Returns a smack_known label that is either imported or found. + * NULL if label not found (only when import == false). + * Error code otherwise. + */ +struct smack_known *smk_get_label(const char *string, int len, bool import) +{ + struct smack_known *skp; + char *cp; + + if (import) { + skp = smk_import_entry(string, len); + } else { + cp = smk_parse_smack(string, len); + if (IS_ERR(cp)) + return ERR_CAST(cp); + + skp = smk_find_entry(cp); + kfree(cp); + } + + return skp; +} diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 198d3d6..7303c37 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -746,31 +746,31 @@ static int smack_set_mnt_opts(struct super_block *sb, for (i = 0; i < num_opts; i++) { switch (opts->mnt_opts_flags[i]) { case FSDEFAULT_MNT: - skp = smk_import_entry(opts->mnt_opts[i], 0); + skp = smk_get_label(opts->mnt_opts[i], 0, true); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_default = skp; break; case FSFLOOR_MNT: - skp = smk_import_entry(opts->mnt_opts[i], 0); + skp = smk_get_label(opts->mnt_opts[i], 0, true); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_floor = skp; break; case FSHAT_MNT: - skp = smk_import_entry(opts->mnt_opts[i], 0); + skp = smk_get_label(opts->mnt_opts[i], 0, true); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_hat = skp; break; case FSROOT_MNT: - skp = smk_import_entry(opts->mnt_opts[i], 0); + skp = smk_get_label(opts->mnt_opts[i], 0, true); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_root = skp; break; case FSTRANS_MNT: - skp = smk_import_entry(opts->mnt_opts[i], 0); + skp = smk_get_label(opts->mnt_opts[i], 0, true); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_root = skp; @@ -1288,7 +1288,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, rc = -EPERM; if (rc == 0 && check_import) { - skp = size ? smk_import_entry(value, size) : NULL; + skp = size ? smk_get_label(value, size, true) : NULL; if (IS_ERR(skp)) rc = PTR_ERR(skp); else if (skp == NULL || (check_star && @@ -1322,6 +1322,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct smack_known *skp; + struct smack_known **skpp = NULL; struct inode_smack *isp = d_backing_inode(dentry)->i_security; if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { @@ -1329,27 +1330,21 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, return; } - if (strcmp(name, XATTR_NAME_SMACK) == 0) { - skp = smk_import_entry(value, size); - if (!IS_ERR(skp)) - isp->smk_inode = skp; - else - isp->smk_inode = &smack_known_invalid; - } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { - skp = smk_import_entry(value, size); - if (!IS_ERR(skp)) - isp->smk_task = skp; - else - isp->smk_task = &smack_known_invalid; - } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - skp = smk_import_entry(value, size); + if (strcmp(name, XATTR_NAME_SMACK) == 0) + skpp = &isp->smk_inode; + else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) + skpp = &isp->smk_task; + else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) + skpp = &isp->smk_mmap; + + if (skpp) { + skp = smk_get_label(value, size, true); + if (!IS_ERR(skp)) - isp->smk_mmap = skp; + *skpp = skp; else - isp->smk_mmap = &smack_known_invalid; + *skpp = &smack_known_invalid; } - - return; } /** @@ -1443,15 +1438,17 @@ static int smack_inode_getsecurity(const struct inode *inode, struct socket *sock; struct super_block *sbp; struct inode *ip = (struct inode *)inode; - struct smack_known *isp; - int ilen; + struct smack_known *isp = NULL; int rc = 0; - if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { + if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) isp = smk_of_inode(inode); - ilen = strlen(isp->smk_known); - *buffer = isp->smk_known; - return ilen; + + if (isp) { + *buffer = smk_find_label_name(isp); + if (*buffer == NULL) + *buffer = smack_known_huh.smk_known; + return strlen(*buffer); } /* @@ -1474,10 +1471,11 @@ static int smack_inode_getsecurity(const struct inode *inode, else return -EOPNOTSUPP; - ilen = strlen(isp->smk_known); if (rc == 0) { - *buffer = isp->smk_known; - rc = ilen; + *buffer = smk_find_label_name(isp); + if (*buffer == NULL) + *buffer = smack_known_huh.smk_known; + rc = strlen(*buffer); } return rc; @@ -2658,7 +2656,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, if (value == NULL || size > SMK_LONGLABEL || size == 0) return -EINVAL; - skp = smk_import_entry(value, size); + skp = smk_get_label(value, size, true); if (IS_ERR(skp)) return PTR_ERR(skp); @@ -3528,7 +3526,10 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) if (strcmp(name, "current") != 0) return -EINVAL; - cp = kstrdup(skp->smk_known, GFP_KERNEL); + cp = smk_find_label_name(skp); + if (cp == NULL) + cp = smack_known_huh.smk_known; + cp = kstrdup(cp, GFP_KERNEL); if (cp == NULL) return -ENOMEM; @@ -3572,7 +3573,7 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, if (strcmp(name, "current") != 0) return -EINVAL; - skp = smk_import_entry(value, size); + skp = smk_get_label(value, size, true); if (IS_ERR(skp)) return PTR_ERR(skp); @@ -4311,7 +4312,10 @@ static int smack_key_getsecurity(struct key *key, char **_buffer) return 0; } - copy = kstrdup(skp->smk_known, GFP_KERNEL); + copy = smk_find_label_name(skp); + if (copy == NULL) + copy = smack_known_huh.smk_known; + copy = kstrdup(copy, GFP_KERNEL); if (copy == NULL) return -ENOMEM; length = strlen(copy) + 1; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 05e09ee2..e5fb555 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -340,36 +340,17 @@ static int smk_fill_rule(const char *subject, const char *object, struct smack_parsed_rule *rule, int import, int len) { - const char *cp; - struct smack_known *skp; - - if (import) { - rule->smk_subject = smk_import_entry(subject, len); - if (IS_ERR(rule->smk_subject)) - return PTR_ERR(rule->smk_subject); - - rule->smk_object = smk_import_entry(object, len); - if (IS_ERR(rule->smk_object)) - return PTR_ERR(rule->smk_object); - } else { - cp = smk_parse_smack(subject, len); - if (IS_ERR(cp)) - return PTR_ERR(cp); - skp = smk_find_entry(cp); - kfree(cp); - if (skp == NULL) - return -ENOENT; - rule->smk_subject = skp; - - cp = smk_parse_smack(object, len); - if (IS_ERR(cp)) - return PTR_ERR(cp); - skp = smk_find_entry(cp); - kfree(cp); - if (skp == NULL) - return -ENOENT; - rule->smk_object = skp; - } + rule->smk_subject = smk_get_label(subject, len, import); + if (IS_ERR(rule->smk_subject)) + return PTR_ERR(rule->smk_subject); + if (rule->smk_subject == NULL) + return -ENOENT; + + rule->smk_object = smk_get_label(object, len, import); + if (IS_ERR(rule->smk_object)) + return PTR_ERR(rule->smk_object); + if (rule->smk_object == NULL) + return -ENOENT; rule->smk_access1 = smk_perm_from_str(access1); if (access2) @@ -592,6 +573,9 @@ static void smk_seq_stop(struct seq_file *s, void *v) static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) { + char *sbj; + char *obj; + /* * Don't show any rules with label names too long for * interface file (/smack/load or /smack/load2) @@ -605,9 +589,13 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) if (srp->smk_access == 0) return; - seq_printf(s, "%s %s", - srp->smk_subject->smk_known, - srp->smk_object->smk_known); + sbj = smk_find_label_name(srp->smk_subject); + obj = smk_find_label_name(srp->smk_object); + + if (sbj == NULL || obj == NULL) + return; + + seq_printf(s, "%s %s", sbj, obj); seq_putc(s, ' '); @@ -798,6 +786,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) list_entry_rcu(list, struct smack_known, list); struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; + char *cp; int i; /* @@ -811,7 +800,11 @@ static int cipso_seq_show(struct seq_file *s, void *v) if (strlen(skp->smk_known) >= SMK_LABELLEN) return 0; - seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); + cp = smk_find_label_name(skp); + if (cp == NULL) + return 0; + + seq_printf(s, "%s %3d", cp, skp->smk_netlabel.attr.mls.lvl); for (i = netlbl_catmap_walk(cmp, 0); i >= 0; i = netlbl_catmap_walk(cmp, i + 1)) { @@ -900,7 +893,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, */ mutex_lock(&smack_cipso_lock); - skp = smk_import_entry(rule, 0); + skp = smk_get_label(rule, 0, true); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto out; @@ -989,9 +982,14 @@ static int cipso2_seq_show(struct seq_file *s, void *v) list_entry_rcu(list, struct smack_known, list); struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; + char *cp; int i; - seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); + cp = smk_find_label_name(skp); + if (cp == NULL) + return 0; + + seq_printf(s, "%s %3d", cp, skp->smk_netlabel.attr.mls.lvl); for (i = netlbl_catmap_walk(cmp, 0); i >= 0; i = netlbl_catmap_walk(cmp, i + 1)) { @@ -1072,8 +1070,12 @@ static int net4addr_seq_show(struct seq_file *s, void *v) list_entry_rcu(list, struct smk_net4addr, list); char *kp = SMACK_CIPSO_OPTION; - if (skp->smk_label != NULL) - kp = skp->smk_label->smk_known; + if (skp->smk_label != NULL) { + kp = smk_find_label_name(skp->smk_label); + if (kp == NULL) + kp = smack_known_huh.smk_known; + } + seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr, skp->smk_masks, kp); @@ -1224,7 +1226,7 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, * If smack begins with '-', it is an option, don't import it */ if (smack[0] != '-') { - skp = smk_import_entry(smack, 0); + skp = smk_get_label(smack, 0, true); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto free_out; @@ -1343,10 +1345,16 @@ static int net6addr_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smk_net6addr *skp = list_entry(list, struct smk_net6addr, list); + char *kp; - if (skp->smk_label != NULL) - seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks, - skp->smk_label->smk_known); + if (skp->smk_label != NULL) { + kp = smk_find_label_name(skp->smk_label); + if (kp == NULL) + kp = smack_known_huh.smk_known; + + seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, + skp->smk_masks, kp); + } return 0; } @@ -1500,7 +1508,7 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, * If smack begins with '-', it is an option, don't import it */ if (smack[0] != '-') { - skp = smk_import_entry(smack, 0); + skp = smk_get_label(smack, 0, true); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto free_out; @@ -1820,6 +1828,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, size_t cn, loff_t *ppos) { ssize_t rc; + char *cp; int asize; if (*ppos != 0) @@ -1830,12 +1839,14 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, */ mutex_lock(&smack_ambient_lock); - asize = strlen(smack_net_ambient->smk_known) + 1; + cp = smk_find_label_name(smack_net_ambient); + if (cp == NULL) + cp = smack_known_huh.smk_known; + + asize = strlen(cp) + 1; if (cn >= asize) - rc = simple_read_from_buffer(buf, cn, ppos, - smack_net_ambient->smk_known, - asize); + rc = simple_read_from_buffer(buf, cn, ppos, cp, asize); else rc = -EINVAL; @@ -1873,7 +1884,7 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, goto out; } - skp = smk_import_entry(data, count); + skp = smk_get_label(data, count, true); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto out; @@ -1913,11 +1924,16 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) static int onlycap_seq_show(struct seq_file *s, void *v) { + char *smack; struct list_head *list = v; struct smack_onlycap *sop = list_entry_rcu(list, struct smack_onlycap, list); - seq_puts(s, sop->smk_label->smk_known); + smack = smk_find_label_name(sop->smk_label); + if (smack == NULL) + smack = smack_known_huh.smk_known; + + seq_puts(s, smack); seq_putc(s, ' '); return 0; @@ -2011,7 +2027,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (!*tok) continue; - skp = smk_import_entry(tok, 0); + skp = smk_get_label(tok, 0, true); if (IS_ERR(skp)) { rc = PTR_ERR(skp); break; @@ -2081,8 +2097,11 @@ static ssize_t smk_read_unconfined(struct file *filp, char __user *buf, if (*ppos != 0) return 0; - if (smack_unconfined != NULL) - smack = smack_unconfined->smk_known; + if (smack_unconfined != NULL) { + smack = smk_find_label_name(smack_unconfined); + if (smack == NULL) + smack = smack_known_huh.smk_known; + } asize = strlen(smack) + 1; @@ -2129,7 +2148,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, * * But do so only on invalid label, not on system errors. */ - skp = smk_import_entry(data, count); + skp = smk_get_label(data, count, true); if (PTR_ERR(skp) == -EINVAL) skp = NULL; else if (IS_ERR(skp)) { @@ -2526,7 +2545,6 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *data; - const char *cp; struct smack_known *skp; struct smack_rule *sp; struct list_head *rule_list; @@ -2551,15 +2569,13 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, goto out_data; } - cp = smk_parse_smack(data, count); - if (IS_ERR(cp)) { - rc = PTR_ERR(cp); + skp = smk_get_label(data, count, false); + if (IS_ERR(skp)) { + rc = PTR_ERR(skp); goto out_data; } - - skp = smk_find_entry(cp); if (skp == NULL) - goto out_cp; + goto out_data; rule_list = &skp->smk_rules; rule_lock = &skp->smk_rules_lock; @@ -2571,8 +2587,6 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, mutex_unlock(rule_lock); -out_cp: - kfree(cp); out_data: kfree(data); @@ -2641,8 +2655,11 @@ static ssize_t smk_read_syslog(struct file *filp, char __user *buf, if (*ppos != 0) return 0; - if (smack_syslog_label != NULL) - smack = smack_syslog_label->smk_known; + if (smack_syslog_label != NULL) { + smack = smk_find_label_name(smack_syslog_label); + if (smack == NULL) + smack = smack_known_huh.smk_known; + } asize = strlen(smack) + 1; @@ -2689,7 +2706,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, * * But do so only on invalid label, not on system errors. */ - skp = smk_import_entry(data, count); + skp = smk_get_label(data, count, true); if (PTR_ERR(skp) == -EINVAL) skp = NULL; else if (IS_ERR(skp)) { -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:42:02 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:42:02 +0200 Subject: [PATCH v4 08/11] smack: misc cleanups in preparation for a namespace patch In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-9-git-send-email-l.pawelczyk@samsung.com> This patch does some small miscellaneous cleanups and additions that should not change the code behaviour in any way. Its only purpose is to shape the code in a way that the smack namespace patches would be smaller and easier to understand. Changes: - four small helper functions added - minor code reformatting in several places for readability - unnecessarily increasing string size has been fixed This patch should not change the behaviour of the Smack in any way. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler --- security/smack/smack.h | 47 ++++++++++++++++++++++++++++++++++- security/smack/smack_access.c | 18 +++++++++----- security/smack/smack_lsm.c | 58 ++++++++++++++++--------------------------- security/smack/smackfs.c | 4 +-- 4 files changed, 81 insertions(+), 46 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 091efc2..98bb676 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -291,7 +291,7 @@ struct smk_audit_info { int smk_access_entry(char *, char *, struct list_head *); int smk_access(struct smack_known *, struct smack_known *, int, struct smk_audit_info *); -int smk_tskacc(struct task_smack *, struct smack_known *, +int smk_tskacc(struct task_struct *, struct smack_known *, u32, struct smk_audit_info *); int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); struct smack_known *smack_from_secid(const u32); @@ -348,6 +348,7 @@ extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; static inline int smk_inode_transmutable(const struct inode *isp) { struct inode_smack *sip = isp->i_security; + return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0; } @@ -357,10 +358,31 @@ static inline int smk_inode_transmutable(const struct inode *isp) static inline struct smack_known *smk_of_inode(const struct inode *isp) { struct inode_smack *sip = isp->i_security; + return sip->smk_inode; } /* + * Present a pointer to the smack label entry in an inode blob for an exec. + */ +static inline struct smack_known *smk_of_exec(const struct inode *isp) +{ + struct inode_smack *sip = isp->i_security; + + return sip->smk_task; +} + +/* + * Present a pointer to the smack label entry in an inode blob for an mmap. + */ +static inline struct smack_known *smk_of_mmap(const struct inode *isp) +{ + struct inode_smack *sip = isp->i_security; + + return sip->smk_mmap; +} + +/* * Present a pointer to the smack label entry in an task blob. */ static inline struct smack_known *smk_of_task(const struct task_smack *tsp) @@ -395,6 +417,29 @@ static inline struct smack_known *smk_of_current(void) } /* + * Present a pointer to the user namespace entry in an task blob. + */ +static inline +struct user_namespace *ns_of_task_struct(const struct task_struct *t) +{ + struct user_namespace *ns; + + rcu_read_lock(); + ns = __task_cred(t)->user_ns; + rcu_read_unlock(); + + return ns; +} + +/* + * Present a pointer to the user namespace entry in the current task blob. + */ +static inline struct user_namespace *ns_of_current(void) +{ + return current_user_ns(); +} + +/* * logging functions */ #define SMACK_AUDIT_DENIED 0x1 diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 131c742..750aa9c 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -167,6 +167,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, if (subject == &smack_known_hat) goto out_audit; } + /* * Beyond here an explicit relationship is required. * If the requested access is contained in the available @@ -183,6 +184,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, rc = -EACCES; goto out_audit; } + #ifdef CONFIG_SECURITY_SMACK_BRINGUP /* * Return a positive value if using bringup mode. @@ -225,10 +227,10 @@ out_audit: * non zero otherwise. It allows that the task may have the capability * to override the rules. */ -int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known, +int smk_tskacc(struct task_struct *task, struct smack_known *obj_known, u32 mode, struct smk_audit_info *a) { - struct smack_known *sbj_known = smk_of_task(tsp); + struct smack_known *sbj_known = smk_of_task_struct(task); int may; int rc; @@ -237,13 +239,19 @@ int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known, */ rc = smk_access(sbj_known, obj_known, mode, NULL); if (rc >= 0) { + struct task_smack *tsp; + /* * If there is an entry in the task's rule list * it can further restrict access. */ + rcu_read_lock(); + tsp = __task_cred(task)->security; may = smk_access_entry(sbj_known->smk_known, obj_known->smk_known, &tsp->smk_rules); + rcu_read_unlock(); + if (may < 0) goto out_audit; if ((mode & may) == mode) @@ -280,9 +288,7 @@ out_audit: int smk_curacc(struct smack_known *obj_known, u32 mode, struct smk_audit_info *a) { - struct task_smack *tsp = current_security(); - - return smk_tskacc(tsp, obj_known, mode, a); + return smk_tskacc(current, obj_known, mode, a); } #ifdef CONFIG_AUDIT @@ -456,7 +462,7 @@ char *smk_parse_smack(const char *string, int len) int i; if (len <= 0) - len = strlen(string) + 1; + len = strlen(string); /* * Reserve a leading '-' as an indicator that diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7303c37..5a59836 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -393,8 +393,6 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, { int rc; struct smk_audit_info ad, *saip = NULL; - struct task_smack *tsp; - struct smack_known *tracer_known; if ((mode & PTRACE_MODE_NOAUDIT) == 0) { smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK); @@ -402,13 +400,12 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, saip = &ad; } - rcu_read_lock(); - tsp = __task_cred(tracer)->security; - tracer_known = smk_of_task(tsp); if ((mode & PTRACE_MODE_ATTACH) && (smack_ptrace_rule == SMACK_PTRACE_EXACT || smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { + struct smack_known *tracer_known = smk_of_task_struct(tracer); + if (tracer_known->smk_known == tracee_known->smk_known) rc = 0; else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) @@ -416,22 +413,18 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, else if (smack_has_privilege(tracer, CAP_SYS_PTRACE)) rc = 0; else - rc = -EACCES; + rc = -EPERM; if (saip) smack_log(tracer_known->smk_known, tracee_known->smk_known, 0, rc, saip); - rcu_read_unlock(); return rc; } /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ - rc = smk_tskacc(tsp, tracee_known, smk_ptrace_mode(mode), saip); - - rcu_read_unlock(); - return rc; + return smk_tskacc(tracer, tracee_known, smk_ptrace_mode(mode), saip); } /* @@ -450,9 +443,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, */ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { - struct smack_known *skp; - - skp = smk_of_task_struct(ctp); + struct smack_known *skp = smk_of_task_struct(ctp); return smk_ptrace_rule_check(current, skp, mode, __func__); } @@ -467,13 +458,9 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) */ static int smack_ptrace_traceme(struct task_struct *ptp) { - int rc; - struct smack_known *skp; - - skp = smk_of_task(current_security()); + struct smack_known *skp = smk_of_current(); - rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); - return rc; + return smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); } /** @@ -1692,13 +1679,14 @@ static int smack_mmap_file(struct file *file, if (file == NULL) return 0; + tsp = current_security(); + skp = smk_of_task(tsp); isp = file_inode(file)->i_security; - if (isp->smk_mmap == NULL) - return 0; mkp = isp->smk_mmap; - tsp = current_security(); - skp = smk_of_current(); + if (mkp == NULL) + return 0; + rc = 0; rcu_read_lock(); @@ -3606,11 +3594,13 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, static int smack_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk) { - struct smack_known *skp; - struct smack_known *okp; struct socket_smack *ssp = sock->sk_security; struct socket_smack *osp = other->sk_security; struct socket_smack *nsp = newsk->sk_security; + struct smack_known *skp_out = ssp->smk_out; + struct smack_known *okp_out = osp->smk_out; + struct smack_known *skp_in = ssp->smk_in; + struct smack_known *okp_in = osp->smk_in; struct smk_audit_info ad; int rc = 0; #ifdef CONFIG_AUDIT @@ -3618,19 +3608,15 @@ static int smack_unix_stream_connect(struct sock *sock, #endif if (!smack_privileged(CAP_MAC_OVERRIDE)) { - skp = ssp->smk_out; - okp = osp->smk_in; #ifdef CONFIG_AUDIT smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_setfield_u_net_sk(&ad, other); #endif - rc = smk_access(skp, okp, MAY_WRITE, &ad); - rc = smk_bu_note("UDS connect", skp, okp, MAY_WRITE, rc); + rc = smk_access(skp_out, okp_in, MAY_WRITE, &ad); + rc = smk_bu_note("UDS connect", skp_out, okp_in, MAY_WRITE, rc); if (rc == 0) { - okp = osp->smk_out; - skp = ssp->smk_in; - rc = smk_access(okp, skp, MAY_WRITE, &ad); - rc = smk_bu_note("UDS connect", okp, skp, + rc = smk_access(okp_out, skp_in, MAY_WRITE, &ad); + rc = smk_bu_note("UDS connect", okp_out, skp_in, MAY_WRITE, rc); } } @@ -3639,8 +3625,8 @@ static int smack_unix_stream_connect(struct sock *sock, * Cross reference the peer labels for SO_PEERSEC. */ if (rc == 0) { - nsp->smk_packet = ssp->smk_out; - ssp->smk_packet = osp->smk_out; + nsp->smk_packet = skp_out; + ssp->smk_packet = okp_out; } return rc; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index e5fb555..3149ec0 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -1827,7 +1827,7 @@ static const struct file_operations smk_mapped_ops = { static ssize_t smk_read_ambient(struct file *filp, char __user *buf, size_t cn, loff_t *ppos) { - ssize_t rc; + ssize_t rc = -EINVAL; char *cp; int asize; @@ -1847,8 +1847,6 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, if (cn >= asize) rc = simple_read_from_buffer(buf, cn, ppos, cp, asize); - else - rc = -EINVAL; mutex_unlock(&smack_ambient_lock); -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:42:03 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:42:03 +0200 Subject: [PATCH v4 09/11] smack: namespace groundwork In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-10-git-send-email-l.pawelczyk@samsung.com> This commit introduces several changes to Smack to prepare it for namespace implementation. All the changes are related to namespaces. Overview of the changes: - Adds required data structures for mapped labels and functions to operate on them. - Implements the proc interface /proc/$PID/attr/label_map that can be used for remapping of labels for a specific namespace. Also for checking the map. - Modifies handling of special built-in labels. Detects them on import and assigns the same char* pointer regardless whether it's used in a normal or a mapped label. This way we can always compare them by == instead of strcmp(). - Adds User namespace hooks implementation This patch introduces both internal and user-space visible APIs to handle namespaced labels and Smack namespaces but the behaviour of Smack should not be changed. The APIs are there, but they have no impact yet. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler --- security/smack/Kconfig | 10 ++ security/smack/Makefile | 1 + security/smack/smack.h | 45 ++++- security/smack/smack_access.c | 47 ++++- security/smack/smack_lsm.c | 134 +++++++++++++- security/smack/smack_ns.c | 404 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 626 insertions(+), 15 deletions(-) create mode 100644 security/smack/smack_ns.c diff --git a/security/smack/Kconfig b/security/smack/Kconfig index 271adae..b19a7fb 100644 --- a/security/smack/Kconfig +++ b/security/smack/Kconfig @@ -40,3 +40,13 @@ config SECURITY_SMACK_NETFILTER This enables security marking of network packets using Smack labels. If you are unsure how to answer this question, answer N. + +config SECURITY_SMACK_NS + bool "Smack namespace" + depends on SECURITY_SMACK + depends on USER_NS + help + This enables Smack namespace that makes it possible to map + specific labels within user namespace (analogously to mapping + UIDs) and to gain MAC capabilities over them. + If you are unsure how to answer this question, answer N. diff --git a/security/smack/Makefile b/security/smack/Makefile index ee2ebd5..5faebd7 100644 --- a/security/smack/Makefile +++ b/security/smack/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_SECURITY_SMACK) := smack.o smack-y := smack_lsm.o smack_access.o smackfs.o smack-$(CONFIG_SECURITY_SMACK_NETFILTER) += smack_netfilter.o +smack-$(CONFIG_SECURITY_SMACK_NS) += smack_ns.o diff --git a/security/smack/smack.h b/security/smack/smack.h index 98bb676..4b7489f 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -24,6 +24,7 @@ #include #include #include +#include /* * Use IPv6 port labeling if IPv6 is enabled and secmarks @@ -74,8 +75,36 @@ struct smack_known { struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ struct list_head smk_rules; /* access rules */ struct mutex smk_rules_lock; /* lock for rules */ +#ifdef CONFIG_SECURITY_SMACK_NS + struct list_head smk_mapped; /* namespaced labels */ + struct mutex smk_mapped_lock; +#endif /* CONFIG_SECURITY_SMACK_NS */ }; +#ifdef CONFIG_SECURITY_SMACK_NS + +/* + * User namespace security pointer content. + */ +struct smack_ns { + struct list_head smk_mapped; /* namespaced labels */ + struct mutex smk_mapped_lock; +}; + +/* + * A single entry for a namespaced/mapped label. + */ +struct smack_known_ns { + struct list_head smk_list_known; + struct list_head smk_list_ns; + struct user_namespace *smk_ns; + char *smk_mapped; + struct smack_known *smk_unmapped; + bool smk_allocated; +}; + +#endif /* CONFIG_SECURITY_SMACK_NS */ + /* * Maximum number of bytes for the levels in a CIPSO IP option. * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is @@ -295,7 +324,7 @@ int smk_tskacc(struct task_struct *, struct smack_known *, u32, struct smk_audit_info *); int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); struct smack_known *smack_from_secid(const u32); -char *smk_parse_smack(const char *string, int len); +char *smk_parse_smack(const char *string, int len, bool *allocated); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); struct smack_known *smk_import_entry(const char *, int); void smk_insert_entry(struct smack_known *skp); @@ -310,6 +339,20 @@ char *smk_find_label_name(struct smack_known *skp); struct smack_known *smk_get_label(const char *string, int len, bool import); /* + * These functions are in smack_ns.c + */ +#ifdef CONFIG_SECURITY_SMACK_NS +struct user_namespace *smk_find_mapped_ns(struct user_namespace *ns); +struct smack_known_ns *smk_find_mapped(struct smack_known *skp, + struct user_namespace *ns); +struct smack_known *smk_find_unmapped(const char *string, int len, + struct user_namespace *ns); +extern const struct seq_operations proc_label_map_seq_operations; +ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred, + void *value, size_t size); +#endif /* CONFIG_SECURITY_SMACK_NS */ + +/* * Shared data. */ extern int smack_enabled; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 750aa9c..17b7e2c 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -452,13 +452,16 @@ struct smack_known *smk_find_entry(const char *string) /** * smk_parse_smack - parse smack label from a text string * @string: a text string that might contain a Smack label - * @len: the maximum size, or zero if it is NULL terminated. + * @len: the maximum size, or zero if it is NULL terminated + * @allocated: (out) indicates whether the return string has been + * allocated and has to be freed with kfree() later + * (built-in labels returned are not allocated) * * Returns a pointer to the clean label or an error code. */ -char *smk_parse_smack(const char *string, int len) +char *smk_parse_smack(const char *string, int len, bool *allocated) { - char *smack; + char *smack = NULL; int i; if (len <= 0) @@ -480,11 +483,33 @@ char *smk_parse_smack(const char *string, int len) if (i == 0 || i >= SMK_LONGLABEL) return ERR_PTR(-EINVAL); + /* + * Look for special labels. This way we guarantee that we can compare + * special labels in mapped entries by ==, without strcmp(). + */ + if (len == 1 && !strcmp(string, smack_known_huh.smk_known)) + smack = smack_known_huh.smk_known; + else if (len == 1 && !strcmp(string, smack_known_hat.smk_known)) + smack = smack_known_hat.smk_known; + else if (len == 1 && !strcmp(string, smack_known_star.smk_known)) + smack = smack_known_star.smk_known; + else if (len == 1 && !strcmp(string, smack_known_floor.smk_known)) + smack = smack_known_floor.smk_known; + else if (len == 1 && !strcmp(string, smack_known_web.smk_known)) + smack = smack_known_web.smk_known; + + if (smack) { + *allocated = false; + + return smack; + } + smack = kzalloc(i + 1, GFP_KERNEL); if (smack == NULL) return ERR_PTR(-ENOMEM); strncpy(smack, string, i); + *allocated = true; return smack; } @@ -540,8 +565,9 @@ struct smack_known *smk_import_entry(const char *string, int len) char *smack; int slen; int rc; + bool allocated; - smack = smk_parse_smack(string, len); + smack = smk_parse_smack(string, len, &allocated); if (IS_ERR(smack)) return ERR_CAST(smack); @@ -577,6 +603,10 @@ struct smack_known *smk_import_entry(const char *string, int len) if (rc >= 0) { INIT_LIST_HEAD(&skp->smk_rules); mutex_init(&skp->smk_rules_lock); +#ifdef CONFIG_SECURITY_SMACK_NS + INIT_LIST_HEAD(&skp->smk_mapped); + mutex_init(&skp->smk_mapped_lock); +#endif /* CONFIG_SECURITY_SMACK_NS */ /* * Make sure that the entry is actually * filled before putting it on the list. @@ -590,7 +620,8 @@ struct smack_known *smk_import_entry(const char *string, int len) kfree(skp); skp = ERR_PTR(rc); freeout: - kfree(smack); + if (allocated) + kfree(smack); unlockout: mutex_unlock(&smack_known_lock); @@ -748,17 +779,19 @@ char *smk_find_label_name(struct smack_known *skp) struct smack_known *smk_get_label(const char *string, int len, bool import) { struct smack_known *skp; + bool allocated; char *cp; if (import) { skp = smk_import_entry(string, len); } else { - cp = smk_parse_smack(string, len); + cp = smk_parse_smack(string, len, &allocated); if (IS_ERR(cp)) return ERR_CAST(cp); skp = smk_find_entry(cp); - kfree(cp); + if (allocated) + kfree(cp); } return skp; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 5a59836..206e0ce 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "smack.h" #define TRANS_TRUE "TRUE" @@ -3496,6 +3497,27 @@ unlockandout: } /** + * smack_getprocattr_seq - Smack process attribute access through seq + * @p: the object task + * @name: the name of the attribute in /proc/.../attr/ + * @ops: out, seq_operations to handle @name + * + * Returns 0 if @name is to be handled by seq, error otherwise. + */ +int smack_getprocattr_seq(struct task_struct *p, const char *name, + const struct seq_operations **ops) +{ +#ifdef CONFIG_SECURITY_SMACK_NS + if (strcmp(name, "label_map") == 0) { + *ops = &proc_label_map_seq_operations; + return 0; + } +#endif + + return -EOPNOTSUPP; +} + +/** * smack_getprocattr - Smack process attribute access * @p: the object task * @name: the name of the attribute in /proc/.../attr @@ -3527,9 +3549,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) } /** - * smack_setprocattr - Smack process attribute setting + * proc_current_write - Smack "current" process attribute setting * @p: the object task - * @name: the name of the attribute in /proc/.../attr * @value: the value to set * @size: the size of the value * @@ -3538,8 +3559,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) * * Returns the length of the smack label or an error code */ -static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, - char *name, void *value, size_t size) +static int proc_current_write(struct task_struct *p, void *value, size_t size) { struct task_smack *tsp; struct cred *new; @@ -3558,9 +3578,6 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, if (value == NULL || size == 0 || size >= SMK_LONGLABEL) return -EINVAL; - if (strcmp(name, "current") != 0) - return -EINVAL; - skp = smk_get_label(value, size, true); if (IS_ERR(skp)) return PTR_ERR(skp); @@ -3583,6 +3600,33 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, } /** + * smack_setprocattr - Smack process attribute setting + * @p: the object task + * @cred: the credentials of the file's opener + * @name: the name of the attribute in /proc/.../attr + * @value: the value to set + * @size: the size of the value + * + * Sets the proc attribute + * + * Returns the length of the written data or an error code + */ +static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, + char *name, void *value, size_t size) +{ +#ifdef CONFIG_SECURITY_SMACK_NS + if (strcmp(name, "label_map") == 0) + return proc_label_map_write(p, f_cred, value, size); +#endif + + if (strcmp(name, "current") == 0) + return proc_current_write(p, value, size); + + return -EINVAL; + +} + +/** * smack_unix_stream_connect - Smack access on UDS * @sock: one sock * @other: the other sock @@ -4434,6 +4478,53 @@ static void smack_audit_rule_free(void *vrule) #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_SECURITY_SMACK_NS + +static inline int smack_userns_create(struct user_namespace *ns) +{ + struct smack_ns *snsp; + + snsp = kzalloc(sizeof(*snsp), GFP_KERNEL); + if (snsp == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&snsp->smk_mapped); + mutex_init(&snsp->smk_mapped_lock); + + ns->security = snsp; + return 0; +} + +static inline void smack_userns_free(struct user_namespace *ns) +{ + struct smack_ns *snsp = ns->security; + struct smack_known *skp; + struct smack_known_ns *sknp, *n; + + list_for_each_entry_safe(sknp, n, &snsp->smk_mapped, smk_list_ns) { + skp = sknp->smk_unmapped; + + mutex_lock(&skp->smk_mapped_lock); + list_del_rcu(&sknp->smk_list_known); + if (sknp->smk_allocated) + kfree(sknp->smk_mapped); + kfree(sknp); + mutex_unlock(&skp->smk_mapped_lock); + + list_del(&sknp->smk_list_ns); + } + + kfree(snsp); +} + +static inline int smack_userns_setns(struct nsproxy *nsproxy, + struct user_namespace *ns) +{ + return 0; +} + +#endif /* CONFIG_SECURITY_SMACK_NS */ + /** * smack_ismaclabel - check if xattr @name references a smack MAC label * @name: Full xattr name to check. @@ -4610,6 +4701,7 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(d_instantiate, smack_d_instantiate), + LSM_HOOK_INIT(getprocattr_seq, smack_getprocattr_seq), LSM_HOOK_INIT(getprocattr, smack_getprocattr), LSM_HOOK_INIT(setprocattr, smack_setprocattr), @@ -4647,6 +4739,13 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(audit_rule_free, smack_audit_rule_free), #endif /* CONFIG_AUDIT */ + /* Namespace hooks */ +#ifdef CONFIG_SECURITY_SMACK_NS + LSM_HOOK_INIT(userns_create, smack_userns_create), + LSM_HOOK_INIT(userns_free, smack_userns_free), + LSM_HOOK_INIT(userns_setns, smack_userns_setns), +#endif /* CONFIG_SECURITY_SMACK_NS */ + LSM_HOOK_INIT(ismaclabel, smack_ismaclabel), LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx), LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid), @@ -4659,6 +4758,27 @@ static struct security_hook_list smack_hooks[] = { static __init void init_smack_known_list(void) { +#ifdef CONFIG_SECURITY_SMACK_NS + /* + * Initialize mapped list locks + */ + mutex_init(&smack_known_huh.smk_mapped_lock); + mutex_init(&smack_known_hat.smk_mapped_lock); + mutex_init(&smack_known_floor.smk_mapped_lock); + mutex_init(&smack_known_star.smk_mapped_lock); + mutex_init(&smack_known_invalid.smk_mapped_lock); + mutex_init(&smack_known_web.smk_mapped_lock); + /* + * Initialize mapped lists + */ + INIT_LIST_HEAD(&smack_known_huh.smk_mapped); + INIT_LIST_HEAD(&smack_known_hat.smk_mapped); + INIT_LIST_HEAD(&smack_known_star.smk_mapped); + INIT_LIST_HEAD(&smack_known_floor.smk_mapped); + INIT_LIST_HEAD(&smack_known_invalid.smk_mapped); + INIT_LIST_HEAD(&smack_known_web.smk_mapped); +#endif /* CONFIG_SECURITY_SMACK_NS */ + /* * Initialize rule list locks */ diff --git a/security/smack/smack_ns.c b/security/smack/smack_ns.c new file mode 100644 index 0000000..49223c4 --- /dev/null +++ b/security/smack/smack_ns.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2014 Samsung Electronics. + * + * Smack namespaces + * + * Author(s): + * Lukasz Pawelczyk + * + * This program is free software, you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include "smack.h" + +/** + * smk_find_mapped_ns - Finds a first namespace from this one through + * its parrents that has a map. This map is the effective map in this + * namespace. + * @ns: a user namespace for which we search for a mapped ns + * + * Returns a namespace that has a non-NULL map, or NULL if there is + * no mapped namespace. + * + * Can be effectively used to answer a question: "is there a Smack + * map for this namespace?" + */ +struct user_namespace *smk_find_mapped_ns(struct user_namespace *ns) +{ + struct user_namespace *user_ns = ns; + + do { + struct smack_ns *sns = user_ns->security; + + if (sns && !list_empty(&sns->smk_mapped)) + break; + + user_ns = user_ns->parent; + } while (user_ns); + + return user_ns; +} + +/** + * __smk_find_mapped - an internal version of smk_find_mapped + * that doesn't use smk_find_mapped_ns, but + * operates directly on the passed one. + */ +static struct smack_known_ns *__smk_find_mapped(struct smack_known *skp, + struct user_namespace *ns) +{ + struct smack_known_ns *sknp; + + if (ns == NULL) + return NULL; + + list_for_each_entry_rcu(sknp, &skp->smk_mapped, smk_list_known) + if (sknp->smk_ns == ns) + return sknp; + + return NULL; +} + +/** + * smk_find_mapped - Finds a mapped label on the smack_known's mapped list + * @skp: a label which mapped label we look for + * @ns: a user namespace the label we search for is assigned to + * + * Returns a pointer to the mapped label if one exists that is + * assigned to the specified user namespace or NULL if not found. + */ +struct smack_known_ns *smk_find_mapped(struct smack_known *skp, + struct user_namespace *ns) +{ + struct user_namespace *user_ns = smk_find_mapped_ns(ns); + + return __smk_find_mapped(skp, user_ns); +} + +/** + * __smk_find_unmapped - an internal version of smk_find_unmapped + * that doesn't use smk_find_mapped_ns, but + * operates directly on the passed one. + */ +static struct smack_known *__smk_find_unmapped(const char *string, int len, + struct user_namespace *ns) +{ + struct smack_ns *snsp; + struct smack_known *skp = NULL; + struct smack_known_ns *sknp; + char *smack; + bool allocated = false; + + if (ns == NULL) + return NULL; + + snsp = ns->security; + + smack = smk_parse_smack(string, len, &allocated); + if (IS_ERR(smack)) + return ERR_CAST(smack); + + list_for_each_entry_rcu(sknp, &snsp->smk_mapped, smk_list_ns) { + if (strcmp(smack, sknp->smk_mapped) == 0) { + skp = sknp->smk_unmapped; + break; + } + } + + if (allocated) + kfree(smack); + return skp; +} + +/** + * smk_find_unmapped - Finds an original label by a mapped label string + * and the namespace it could be mapped in + * @string: a name of a mapped label we look for + * @len: the string size, or zero if it is NULL terminated. + * @ns: a namespace the looked for label should be mapped in + * + * Returns a smack_known label that is mapped as 'string' in 'ns', + * NULL if not found or an error code. + */ +struct smack_known *smk_find_unmapped(const char *string, int len, + struct user_namespace *ns) +{ + struct user_namespace *user_ns = smk_find_mapped_ns(ns); + + return __smk_find_unmapped(string, len, user_ns); +} + +/** + * smk_import_mapped - Imports a mapped label effectively creating a mapping. + * @skp: a label we map + * @ns: a user namespace this label will be mapped in + * @string: a text string of the mapped label + * @len: the maximum size, or zero if it is NULL terminanted + * + * Returns a pointer to the mapped label entry or an error code. + * + * The mapped label will be added to 2 lists: + * - a list of mapped labels of skp + * - a list of labels mapped in ns + */ +static struct smack_known_ns *smk_import_mapped(struct smack_known *skp, + struct user_namespace *ns, + const char *string, int len) +{ + struct smack_ns *snsp = ns->security; + struct smack_known_ns *sknp; + char *mapped; + bool allocated; + + /* Mapping init_user_ns is against the design and pointless */ + if (ns == &init_user_ns) + return ERR_PTR(-EBADR); + + mapped = smk_parse_smack(string, len, &allocated); + if (IS_ERR(mapped)) + return ERR_CAST(mapped); + + mutex_lock(&skp->smk_mapped_lock); + + /* + * Don't allow one<->many mappings in namespace, rename. + * This code won't get triggered for now as trying to assign + * a duplicate is forbidden in proc_label_map_write(). + * Leaving this as this function might be also used elsewhere. + */ + sknp = smk_find_mapped(skp, ns); + if (sknp != NULL) { + if (sknp->smk_allocated) + kfree(sknp->smk_mapped); + sknp->smk_mapped = mapped; + sknp->smk_allocated = allocated; + goto unlockout; + } + + sknp = kzalloc(sizeof(*sknp), GFP_KERNEL); + if (sknp == NULL) { + sknp = ERR_PTR(-ENOMEM); + if (allocated) + kfree(mapped); + goto unlockout; + } + + sknp->smk_ns = ns; + sknp->smk_mapped = mapped; + sknp->smk_allocated = allocated; + sknp->smk_unmapped = skp; + list_add_rcu(&sknp->smk_list_known, &skp->smk_mapped); + + mutex_lock(&snsp->smk_mapped_lock); + list_add_rcu(&sknp->smk_list_ns, &snsp->smk_mapped); + mutex_unlock(&snsp->smk_mapped_lock); + +unlockout: + mutex_unlock(&skp->smk_mapped_lock); + + return sknp; +} + +static void *proc_label_map_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct smack_known *skp; + struct task_struct *task = seq->private; + struct user_namespace *ns = ns_of_task_struct(task); + loff_t counter = *pos; + + rcu_read_lock(); + list_for_each_entry_rcu(skp, &smack_known_list, list) + if (smk_find_mapped(skp, ns) && counter-- == 0) + return skp; + + return NULL; +} + +static void proc_label_map_seq_stop(struct seq_file *seq, void *v) +{ + rcu_read_unlock(); +} + +static void *proc_label_map_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct smack_known *skp = v; + struct task_struct *task = seq->private; + struct user_namespace *ns = ns_of_task_struct(task); + + list_for_each_entry_continue_rcu(skp, &smack_known_list, list) { + if (smk_find_mapped(skp, ns)) { + (*pos)++; + return skp; + } + } + + return NULL; +} + +static int proc_label_map_seq_show(struct seq_file *seq, void *v) +{ + struct smack_known *skp = v; + struct task_struct *task = seq->private; + struct user_namespace *ns = ns_of_task_struct(task); + struct smack_known_ns *sknp; + + sknp = smk_find_mapped(skp, ns); + if (sknp) + seq_printf(seq, "%s -> %s\n", skp->smk_known, sknp->smk_mapped); + + return 0; +} + +const struct seq_operations proc_label_map_seq_operations = { + .start = proc_label_map_seq_start, + .stop = proc_label_map_seq_stop, + .next = proc_label_map_seq_next, + .show = proc_label_map_seq_show, +}; + +static DEFINE_MUTEX(smk_map_mutex); + +static bool mapping_permitted(const struct cred *f_cred, + struct user_namespace *user_ns) +{ + /* + * Do not allow mapping own label. This is in contrast to user + * namespace where you can always map your own UID. In Smack having + * administrative privileges over your own label (which Smack + * namespace would effectively give you) is not equivalent to user + * namespace. E.g. things like setting exec/transmute labels that + * otherwise would be denied. Hence no own_label param here. + */ + + /* + * Adjusting namespace settings requires capabilities on the target. + */ + if (security_capable(f_cred, user_ns, CAP_MAC_ADMIN) != 0) + return false; + + /* + * And it requires capabilities in the parent. + * + * If the Smack namespace was properly hierarchical the user_ns to + * check against could be 'user_ns->parent'. Right now because of + * security concerns only privileged initial namespace is allowed + * to fill the map. For a hierarchical namespaces one would + * implement mapping (in the child namespaces) of only mapped labels + * (in parent namespace) and change '&init_user_ns' to + * 'user_ns->parent'. This will be added in the future. + */ + if (smack_ns_privileged(&init_user_ns, CAP_MAC_ADMIN) && + security_capable(f_cred, &init_user_ns, CAP_MAC_ADMIN) == 0) + return true; + + return false; +} + +ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred, + void *value, size_t size) +{ + struct user_namespace *ns = ns_of_task_struct(p); + struct user_namespace *cur_ns = ns_of_current(); + struct smack_known *skp; + struct smack_known_ns *sknp; + char *pos, *next_line, *tok[2]; + ssize_t ret; + int i; + + /* Mapping labels for the init ns makes no sense */ + if (ns == &init_user_ns) + return -EBADR; + + if (cur_ns != ns->parent) + return -EPERM; + + if (!mapping_permitted(f_cred, ns)) + return -EPERM; + + if (value == NULL || size == 0 || size >= PAGE_SIZE) + return -EINVAL; + + mutex_lock(&smk_map_mutex); + + /* Parse the user data */ + pos = value; + pos[size] = '\0'; + + for (; pos; pos = next_line) { + ret = -EINVAL; + + /* Find the end of line and ensure I don't look past it */ + next_line = strchr(pos, '\n'); + if (next_line) { + *next_line = '\0'; + next_line++; + if (*next_line == '\0') + next_line = NULL; + } + + /* Find tokens in line */ + for (i = 0; i < 2; ++i) { + while (isspace(*pos)) + *(pos++) = '\0'; + + /* unexpected end of file */ + if (*pos == '\0') + goto out; + + tok[i] = pos; + + /* find the end of the token */ + while (*pos != '\0' && !isspace(*pos)) + ++pos; + } + + /* NUL terminate the last token if not EOL */ + while (isspace(*pos)) + *(pos++) = '\0'; + + /* there should not be any trailing data */ + if (*pos != '\0') + goto out; + + ret = -EEXIST; + + /* do not allow to map 2 different labels to one name */ + skp = __smk_find_unmapped(tok[1], 0, ns); + if (IS_ERR(skp)) { + ret = PTR_ERR(skp); + goto out; + } + if (skp != NULL) + goto out; + + skp = smk_import_entry(tok[0], 0); + if (IS_ERR(skp)) { + ret = PTR_ERR(skp); + goto out; + } + + /* do not allow remapping */ + if (__smk_find_mapped(skp, ns)) + goto out; + + sknp = smk_import_mapped(skp, ns, tok[1], 0); + if (IS_ERR(sknp)) { + ret = PTR_ERR(sknp); + goto out; + } + } + + ret = size; + +out: + mutex_unlock(&smk_map_mutex); + + return ret; +} -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:42:04 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:42:04 +0200 Subject: [PATCH v4 10/11] smack: namespace implementation In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-11-git-send-email-l.pawelczyk@samsung.com> This commit uses all the changes introduced in "namespace groundwork" and previous preparation patches and makes smack aware of its namespace and mapped labels. It modifies the following functions to be namespace aware: - smk_access - smk_find_label_name - smk_get_label And all functions that use them (e.g. smk_tskacc). It also adds another function that is used throughout Smack LSM hooks: - smk_labels_valid - it checks whether both, subject and object labels are properly mapped in a namespace where they are to be used. This function is used mostly together with a capability check when there is no proper access check that usually checks for that. All the Smack LSM hooks have been adapted to be namespace aware. The capabilities (CAP_MAC_ADMIN, CAP_MAC_OVERRIDE) has been allowed in the namespace for few cases. Check the documentation for the details. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler --- security/smack/smack.h | 29 +++- security/smack/smack_access.c | 109 ++++++++++-- security/smack/smack_lsm.c | 390 ++++++++++++++++++++++++++++++------------ security/smack/smack_ns.c | 39 +++++ security/smack/smackfs.c | 63 ++++--- 5 files changed, 483 insertions(+), 147 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 4b7489f..3d432f4 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -119,6 +119,7 @@ struct superblock_smack { struct smack_known *smk_floor; struct smack_known *smk_hat; struct smack_known *smk_default; + struct user_namespace *smk_ns; int smk_initialized; }; @@ -126,6 +127,7 @@ struct socket_smack { struct smack_known *smk_out; /* outbound label */ struct smack_known *smk_in; /* inbound label */ struct smack_known *smk_packet; /* TCP peer label */ + struct user_namespace *smk_ns; /* user namespace */ }; /* @@ -146,6 +148,14 @@ struct task_smack { struct mutex smk_rules_lock; /* lock for the rules */ }; +/* + * Used for IPC objects (sem, shm, etc) + */ +struct ipc_smack { + struct smack_known *smk_known; /* label for access control */ + struct user_namespace *smk_ns; /* user namespace */ +}; + #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ #define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */ #define SMK_INODE_CHANGED 0x04 /* smack was transmuted */ @@ -319,10 +329,11 @@ struct smk_audit_info { */ int smk_access_entry(char *, char *, struct list_head *); int smk_access(struct smack_known *, struct smack_known *, - int, struct smk_audit_info *); + struct user_namespace *, int, struct smk_audit_info *); int smk_tskacc(struct task_struct *, struct smack_known *, + struct user_namespace *, u32, struct smk_audit_info *); +int smk_curacc(struct smack_known *, struct user_namespace *, u32, struct smk_audit_info *); -int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); struct smack_known *smack_from_secid(const u32); char *smk_parse_smack(const char *string, int len, bool *allocated); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); @@ -335,8 +346,9 @@ int smack_has_ns_privilege(struct task_struct *task, int smack_has_privilege(struct task_struct *task, int cap); int smack_ns_privileged(struct user_namespace *user_ns, int cap); int smack_privileged(int cap); -char *smk_find_label_name(struct smack_known *skp); -struct smack_known *smk_get_label(const char *string, int len, bool import); +char *smk_find_label_name(struct smack_known *skp, struct user_namespace *ns); +struct smack_known *smk_get_label(const char *string, int len, bool import, + struct user_namespace *ns); /* * These functions are in smack_ns.c @@ -350,6 +362,15 @@ struct smack_known *smk_find_unmapped(const char *string, int len, extern const struct seq_operations proc_label_map_seq_operations; ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred, void *value, size_t size); +bool smk_labels_valid(struct smack_known *sbj, struct smack_known *obj, + struct user_namespace *ns); +#else +static inline bool smk_labels_valid(struct smack_known *sbj, + struct smack_known *obj, + struct user_namespace *ns) +{ + return true; +} #endif /* CONFIG_SECURITY_SMACK_NS */ /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 17b7e2c..e230948 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "smack.h" struct smack_known smack_known_huh = { @@ -113,6 +114,7 @@ int smk_access_entry(char *subject_label, char *object_label, * smk_access - determine if a subject has a specific access to an object * @subject: a pointer to the subject's Smack label entry * @object: a pointer to the object's Smack label entry + * @ns: user namespace to check against (usually subject's) * @request: the access requested, in "MAY" format * @a : a pointer to the audit data * @@ -123,10 +125,34 @@ int smk_access_entry(char *subject_label, char *object_label, * Smack labels are shared on smack_list */ int smk_access(struct smack_known *subject, struct smack_known *object, - int request, struct smk_audit_info *a) + struct user_namespace *ns, int request, struct smk_audit_info *a) { int may = MAY_NOT; int rc = 0; + char *subject_label = subject->smk_known; + char *object_label = object->smk_known; +#ifdef CONFIG_SECURITY_SMACK_NS + struct smack_known_ns *sknp; + struct smack_known_ns *oknp; + + /* + * For the namespaced case we need to check whether the labels + * are mapped. If not, refuse. If yes check the builtin rules + * on the mapped label strings so the builtin labels can + * work properly inside the namespace. + */ + if (smk_find_mapped_ns(ns)) { + sknp = smk_find_mapped(subject, ns); + oknp = smk_find_mapped(object, ns); + if (!sknp || !oknp) { + rc = -EACCES; + goto out_audit; + } + + subject_label = sknp->smk_mapped; + object_label = oknp->smk_mapped; + } +#endif /* * Hardcoded comparisons. @@ -134,7 +160,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, /* * A star subject can't access any object. */ - if (subject == &smack_known_star) { + if (subject_label == smack_known_star.smk_known) { rc = -EACCES; goto out_audit; } @@ -143,18 +169,19 @@ int smk_access(struct smack_known *subject, struct smack_known *object, * Tasks cannot be assigned the internet label. * An internet subject can access any object. */ - if (object == &smack_known_web || subject == &smack_known_web) + if (object_label == smack_known_web.smk_known || + subject_label == smack_known_web.smk_known) goto out_audit; /* * A star object can be accessed by any subject. */ - if (object == &smack_known_star) + if (object_label == smack_known_star.smk_known) goto out_audit; /* * An object can be accessed in any way by a subject * with the same label. */ - if (subject->smk_known == object->smk_known) + if (subject_label == object_label) goto out_audit; /* * A hat subject can read or lock any object. @@ -162,9 +189,9 @@ int smk_access(struct smack_known *subject, struct smack_known *object, */ if ((request & MAY_ANYREAD) == request || (request & MAY_LOCK) == request) { - if (object == &smack_known_floor) + if (object_label == smack_known_floor.smk_known) goto out_audit; - if (subject == &smack_known_hat) + if (subject_label == smack_known_hat.smk_known) goto out_audit; } @@ -174,6 +201,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, * access (e.g. read is included in readwrite) it's * good. A negative response from smk_access_entry() * indicates there is no entry for this pair. + * For this check we need real, not mapped labels. */ rcu_read_lock(); may = smk_access_entry(subject->smk_known, object->smk_known, @@ -219,6 +247,7 @@ out_audit: * smk_tskacc - determine if a task has a specific access to an object * @tsp: a pointer to the subject's task * @obj_known: a pointer to the object's label entry + * @obj_ns: an object's namespace to check the caps against * @mode: the access requested, in "MAY" format * @a : common audit data * @@ -228,16 +257,18 @@ out_audit: * to override the rules. */ int smk_tskacc(struct task_struct *task, struct smack_known *obj_known, - u32 mode, struct smk_audit_info *a) + struct user_namespace *obj_ns, u32 mode, + struct smk_audit_info *a) { struct smack_known *sbj_known = smk_of_task_struct(task); + struct user_namespace *sbj_ns = ns_of_task_struct(task); int may; int rc; /* * Check the global rule list */ - rc = smk_access(sbj_known, obj_known, mode, NULL); + rc = smk_access(sbj_known, obj_known, sbj_ns, mode, NULL); if (rc >= 0) { struct task_smack *tsp; @@ -261,8 +292,10 @@ int smk_tskacc(struct task_struct *task, struct smack_known *obj_known, /* * Allow for priviliged to override policy. + * Either in init_ns or when both labels are mapped. */ - if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE)) + if (rc != 0 && smk_labels_valid(sbj_known, obj_known, sbj_ns) + && smack_has_ns_privilege(task, obj_ns, CAP_MAC_OVERRIDE)) rc = 0; out_audit: @@ -277,6 +310,7 @@ out_audit: /** * smk_curacc - determine if current has a specific access to an object * @obj_known: a pointer to the object's Smack label entry + * @obj_ns: an object's namespace to check the caps against * @mode: the access requested, in "MAY" format * @a : common audit data * @@ -285,10 +319,10 @@ out_audit: * non zero otherwise. It allows that current may have the capability * to override the rules. */ -int smk_curacc(struct smack_known *obj_known, +int smk_curacc(struct smack_known *obj_known, struct user_namespace *obj_ns, u32 mode, struct smk_audit_info *a) { - return smk_tskacc(current, obj_known, mode, a); + return smk_tskacc(current, obj_known, obj_ns, mode, a); } #ifdef CONFIG_AUDIT @@ -671,12 +705,13 @@ DEFINE_MUTEX(smack_onlycap_lock); * * For a capability in smack related checks to be effective it needs to: * - be allowed to be privileged by the onlycap rule. - * - be in the initial user ns + * - be in the initial user ns or have a filled map in the child ns */ static int smack_capability_allowed(struct smack_known *skp, struct user_namespace *user_ns) { struct smack_onlycap *sop; + struct smack_ns *sns; /* * All kernel tasks are privileged @@ -684,8 +719,15 @@ static int smack_capability_allowed(struct smack_known *skp, if (unlikely(current->flags & PF_KTHREAD)) return 1; +#ifdef CONFIG_SECURITY_SMACK_NS + sns = user_ns->security; + + if (user_ns != &init_user_ns && list_empty(&sns->smk_mapped)) + return 0; +#else if (user_ns != &init_user_ns) return 0; +#endif /* CONFIG_SECURITY_SMACK_NS */ rcu_read_lock(); if (list_empty(&smack_onlycap_list)) { @@ -755,14 +797,32 @@ int smack_privileged(int cap) } /** - * smk_find_label_name - A helper to get a string value of a label + * smk_find_label_name - A helper to get a string value of either a label or a + * mapped label when inside a namespace * @skp: a label we want a string value from + * @ns: namespace against which we want to get the value * * Returns a pointer to a label name or NULL if label name not found. */ -char *smk_find_label_name(struct smack_known *skp) +char *smk_find_label_name(struct smack_known *skp, struct user_namespace *ns) { - return skp->smk_known; + char *name = NULL; + +#ifdef CONFIG_SECURITY_SMACK_NS + struct smack_known_ns *sknp; + + if (smk_find_mapped_ns(ns)) { + sknp = smk_find_mapped(skp, ns); + if (sknp != NULL) + name = sknp->smk_mapped; + } else { + name = skp->smk_known; + } +#else + name = skp->smk_known; +#endif + + return name; } /** @@ -771,17 +831,32 @@ char *smk_find_label_name(struct smack_known *skp) * @string: a name of a label we look for or want to import * @len: the string size, or zero if it is NULL terminated * @import: whether we should import the label if not found + * @ns: a namespace the looked for label should be in * * Returns a smack_known label that is either imported or found. * NULL if label not found (only when import == false). * Error code otherwise. */ -struct smack_known *smk_get_label(const char *string, int len, bool import) +struct smack_known *smk_get_label(const char *string, int len, bool import, + struct user_namespace *ns) { struct smack_known *skp; bool allocated; char *cp; +#ifdef CONFIG_SECURITY_SMACK_NS + if (smk_find_mapped_ns(ns)) { + skp = smk_find_unmapped(string, len, ns); + + /* Label not found but we can't import in namespaces */ + if (skp == NULL && import) + skp = ERR_PTR(-EBADR); + + /* will also return error codes from smk_find_unmapped() */ + return skp; + } +#endif + if (import) { skp = smk_import_entry(string, len); } else { diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 206e0ce..8e0da67 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -383,6 +383,7 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) * smk_ptrace_rule_check - helper for ptrace access * @tracer: tracer process * @tracee_known: label entry of the process that's about to be traced + * @tracee_ns: a tracee's namespace to check the caps against * @mode: ptrace attachment mode (PTRACE_MODE_*) * @func: name of the function that called us, used for audit * @@ -390,6 +391,7 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) */ static int smk_ptrace_rule_check(struct task_struct *tracer, struct smack_known *tracee_known, + struct user_namespace *tracee_ns, unsigned int mode, const char *func) { int rc; @@ -401,21 +403,28 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, saip = &ad; } - if ((mode & PTRACE_MODE_ATTACH) && (smack_ptrace_rule == SMACK_PTRACE_EXACT || smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { struct smack_known *tracer_known = smk_of_task_struct(tracer); + struct user_namespace *tracer_ns = ns_of_task_struct(tracer); + + if (!smk_labels_valid(tracer_known, tracee_known, tracer_ns)) { + rc = -EACCES; + goto out; + } if (tracer_known->smk_known == tracee_known->smk_known) rc = 0; else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) rc = -EACCES; - else if (smack_has_privilege(tracer, CAP_SYS_PTRACE)) + else if (smack_has_ns_privilege(tracer, tracee_ns, + CAP_SYS_PTRACE)) rc = 0; else rc = -EPERM; +out: if (saip) smack_log(tracer_known->smk_known, tracee_known->smk_known, @@ -425,7 +434,8 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, } /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ - return smk_tskacc(tracer, tracee_known, smk_ptrace_mode(mode), saip); + return smk_tskacc(tracer, tracee_known, tracee_ns, + smk_ptrace_mode(mode), saip); } /* @@ -445,8 +455,9 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { struct smack_known *skp = smk_of_task_struct(ctp); + struct user_namespace *ns = ns_of_task_struct(ctp); - return smk_ptrace_rule_check(current, skp, mode, __func__); + return smk_ptrace_rule_check(current, skp, ns, mode, __func__); } /** @@ -460,8 +471,10 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) static int smack_ptrace_traceme(struct task_struct *ptp) { struct smack_known *skp = smk_of_current(); + struct user_namespace *ns = ns_of_current(); - return smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); + return smk_ptrace_rule_check(ptp, skp, ns, PTRACE_MODE_ATTACH, + __func__); } /** @@ -508,6 +521,7 @@ static int smack_sb_alloc_security(struct super_block *sb) sbsp->smk_default = &smack_known_floor; sbsp->smk_floor = &smack_known_floor; sbsp->smk_hat = &smack_known_hat; + sbsp->smk_ns = get_user_ns(&init_user_ns); /* * smk_initialized will be zero from kzalloc. */ @@ -523,6 +537,9 @@ static int smack_sb_alloc_security(struct super_block *sb) */ static void smack_sb_free_security(struct super_block *sb) { + struct superblock_smack *sbsp = sb->s_security; + + put_user_ns(sbsp->smk_ns); kfree(sb->s_security); sb->s_security = NULL; } @@ -724,6 +741,7 @@ static int smack_set_mnt_opts(struct super_block *sb, struct smack_known *skp; int i; int num_opts = opts->num_mnt_opts; + struct user_namespace *ns = ns_of_current(); int transmute = 0; if (sp->smk_initialized) @@ -734,31 +752,31 @@ static int smack_set_mnt_opts(struct super_block *sb, for (i = 0; i < num_opts; i++) { switch (opts->mnt_opts_flags[i]) { case FSDEFAULT_MNT: - skp = smk_get_label(opts->mnt_opts[i], 0, true); + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_default = skp; break; case FSFLOOR_MNT: - skp = smk_get_label(opts->mnt_opts[i], 0, true); + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_floor = skp; break; case FSHAT_MNT: - skp = smk_get_label(opts->mnt_opts[i], 0, true); + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_hat = skp; break; case FSROOT_MNT: - skp = smk_get_label(opts->mnt_opts[i], 0, true); + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_root = skp; break; case FSTRANS_MNT: - skp = smk_get_label(opts->mnt_opts[i], 0, true); + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); sp->smk_root = skp; @@ -769,7 +787,12 @@ static int smack_set_mnt_opts(struct super_block *sb, } } - if (!smack_privileged(CAP_MAC_ADMIN)) { + /* + * Check for non-privileged case. If current is inside the namespace + * and the it has privileges the validity of labels has already been + * checked during smk_get_label() + */ + if (!smack_ns_privileged(ns, CAP_MAC_ADMIN)) { /* * Unprivileged mounts don't get to specify Smack values. */ @@ -798,6 +821,12 @@ static int smack_set_mnt_opts(struct super_block *sb, if (transmute) isp->smk_flags |= SMK_INODE_TRANSMUTE; + /* + * Set the superblock namespace from a mounting process + */ + put_user_ns(sp->smk_ns); + sp->smk_ns = get_user_ns(ns); + return 0; } @@ -848,7 +877,7 @@ static int smack_sb_statfs(struct dentry *dentry) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad); + rc = smk_curacc(sbp->smk_floor, sbp->smk_ns, MAY_READ, &ad); rc = smk_bu_current("statfs", sbp->smk_floor, MAY_READ, rc); return rc; } @@ -868,6 +897,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) struct inode *inode = file_inode(bprm->file); struct task_smack *bsp = bprm->cred->security; struct inode_smack *isp; + struct user_namespace *ns = ns_of_current(); int rc; if (bprm->cred_prepared) @@ -877,6 +907,13 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) return 0; +#ifdef CONFIG_SECURITY_SMACK_NS + /* one label version of smk_labels_valid() */ + if (smk_find_mapped_ns(ns) && + smk_find_mapped(isp->smk_task, ns) == NULL) + return -EACCES; +#endif + if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { struct task_struct *tracer; rc = 0; @@ -884,9 +921,8 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) rcu_read_lock(); tracer = ptrace_parent(current); if (likely(tracer != NULL)) - rc = smk_ptrace_rule_check(tracer, - isp->smk_task, - PTRACE_MODE_ATTACH, + rc = smk_ptrace_rule_check(tracer, isp->smk_task, + ns, PTRACE_MODE_ATTACH, __func__); rcu_read_unlock(); @@ -1027,6 +1063,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct smack_known *isp; + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int rc; @@ -1034,13 +1071,13 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); isp = smk_of_inode(d_backing_inode(old_dentry)); - rc = smk_curacc(isp, MAY_WRITE, &ad); + rc = smk_curacc(isp, ns, MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(old_dentry), MAY_WRITE, rc); if (rc == 0 && d_is_positive(new_dentry)) { isp = smk_of_inode(d_backing_inode(new_dentry)); smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); - rc = smk_curacc(isp, MAY_WRITE, &ad); + rc = smk_curacc(isp, ns, MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(new_dentry), MAY_WRITE, rc); } @@ -1058,6 +1095,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) { struct inode *ip = d_backing_inode(dentry); + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int rc; @@ -1067,7 +1105,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) /* * You need write access to the thing you're unlinking */ - rc = smk_curacc(smk_of_inode(ip), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(ip), ns, MAY_WRITE, &ad); rc = smk_bu_inode(ip, MAY_WRITE, rc); if (rc == 0) { /* @@ -1075,7 +1113,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) */ smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, dir); - rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(dir), ns, MAY_WRITE, &ad); rc = smk_bu_inode(dir, MAY_WRITE, rc); } return rc; @@ -1091,6 +1129,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) */ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) { + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int rc; @@ -1100,7 +1139,8 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) /* * You need write access to the thing you're removing */ - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), ns, + MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); if (rc == 0) { /* @@ -1108,7 +1148,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) */ smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, dir); - rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(dir), ns, MAY_WRITE, &ad); rc = smk_bu_inode(dir, MAY_WRITE, rc); } @@ -1132,21 +1172,22 @@ static int smack_inode_rename(struct inode *old_inode, struct inode *new_inode, struct dentry *new_dentry) { - int rc; struct smack_known *isp; + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; + int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); isp = smk_of_inode(d_backing_inode(old_dentry)); - rc = smk_curacc(isp, MAY_READWRITE, &ad); + rc = smk_curacc(isp, ns, MAY_READWRITE, &ad); rc = smk_bu_inode(d_backing_inode(old_dentry), MAY_READWRITE, rc); if (rc == 0 && d_is_positive(new_dentry)) { isp = smk_of_inode(d_backing_inode(new_dentry)); smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); - rc = smk_curacc(isp, MAY_READWRITE, &ad); + rc = smk_curacc(isp, ns, MAY_READWRITE, &ad); rc = smk_bu_inode(d_backing_inode(new_dentry), MAY_READWRITE, rc); } return rc; @@ -1163,6 +1204,7 @@ static int smack_inode_rename(struct inode *old_inode, */ static int smack_inode_permission(struct inode *inode, int mask) { + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int no_block = mask & MAY_NOT_BLOCK; int rc; @@ -1179,7 +1221,7 @@ static int smack_inode_permission(struct inode *inode, int mask) return -ECHILD; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); smk_ad_setfield_u_fs_inode(&ad, inode); - rc = smk_curacc(smk_of_inode(inode), mask, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, mask, &ad); rc = smk_bu_inode(inode, mask, rc); return rc; } @@ -1193,6 +1235,7 @@ static int smack_inode_permission(struct inode *inode, int mask) */ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) { + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int rc; @@ -1204,7 +1247,8 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), ns, + MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); return rc; } @@ -1218,13 +1262,14 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) */ static int smack_inode_getattr(const struct path *path) { + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; struct inode *inode = d_backing_inode(path->dentry); int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, *path); - rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, MAY_READ, &ad); rc = smk_bu_inode(inode, MAY_READ, rc); return rc; } @@ -1246,6 +1291,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, { struct smk_audit_info ad; struct smack_known *skp; + struct smack_known *sbj = smk_of_current(); + struct smack_known *obj = smk_of_inode(d_backing_inode(dentry)); + struct user_namespace *ns = ns_of_current(); int check_priv = 0; int check_import = 0; int check_star = 0; @@ -1272,11 +1320,12 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, } else rc = cap_inode_setxattr(dentry, name, value, size, flags); - if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) + if (check_priv && !(smk_labels_valid(sbj, obj, ns) && + smack_ns_privileged(ns, CAP_MAC_ADMIN))) rc = -EPERM; if (rc == 0 && check_import) { - skp = size ? smk_get_label(value, size, true) : NULL; + skp = size ? smk_get_label(value, size, true, ns) : NULL; if (IS_ERR(skp)) rc = PTR_ERR(skp); else if (skp == NULL || (check_star && @@ -1288,7 +1337,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, smk_ad_setfield_u_fs_path_dentry(&ad, dentry); if (rc == 0) { - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + rc = smk_curacc(obj, ns, MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); } @@ -1296,6 +1345,40 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, } /** + * smack_inode_pre_setxattr - Unmap the namespaced label + * @dentry: object + * @name: attribute name + * @value: attribute value + * @size: attribute size + * @flags: unused + * @alloc: unused + * + * Guarantees that the real label value will be written to the filesystem. + */ +static int smack_inode_pre_setxattr(struct dentry *dentry, const char *name, + const void **value, size_t *size, int flags, + bool *alloc) +{ + struct smack_known *skp; + struct user_namespace *ns = ns_of_current(); + + if (strcmp(name, XATTR_NAME_SMACK) != 0 && + strcmp(name, XATTR_NAME_SMACKEXEC) != 0 && + strcmp(name, XATTR_NAME_SMACKMMAP) != 0) + return 0; + + /* Convert value to non namespaced label */ + skp = smk_get_label(*value, *size, true, ns); + if (IS_ERR(skp)) + return PTR_ERR(skp); + + *value = skp->smk_known; + *size = strlen(skp->smk_known); + + return 0; +} + +/** * smack_inode_post_setxattr - Apply the Smack update approved above * @dentry: object * @name: attribute name @@ -1326,7 +1409,8 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, skpp = &isp->smk_mmap; if (skpp) { - skp = smk_get_label(value, size, true); + /* value has been un-namespaced in inode_pre_setxattr() */ + skp = smk_get_label(value, size, true, &init_user_ns); if (!IS_ERR(skp)) *skpp = skp; @@ -1344,13 +1428,15 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, */ static int smack_inode_getxattr(struct dentry *dentry, const char *name) { + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), ns, + MAY_READ, &ad); rc = smk_bu_inode(d_backing_inode(dentry), MAY_READ, rc); return rc; } @@ -1367,6 +1453,9 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name) static int smack_inode_removexattr(struct dentry *dentry, const char *name) { struct inode_smack *isp; + struct smack_known *sbj = smk_of_current(); + struct smack_known *obj = smk_of_inode(d_backing_inode(dentry)); + struct user_namespace *ns = ns_of_current(); struct smk_audit_info ad; int rc = 0; @@ -1376,7 +1465,8 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 || strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - if (!smack_privileged(CAP_MAC_ADMIN)) + if (!smk_labels_valid(sbj, obj, ns) || + !smack_ns_privileged(ns, CAP_MAC_ADMIN)) rc = -EPERM; } else rc = cap_inode_removexattr(dentry, name); @@ -1387,7 +1477,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); + rc = smk_curacc(obj, ns, MAY_WRITE, &ad); rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); if (rc != 0) return rc; @@ -1427,13 +1517,18 @@ static int smack_inode_getsecurity(const struct inode *inode, struct super_block *sbp; struct inode *ip = (struct inode *)inode; struct smack_known *isp = NULL; + struct user_namespace *ns = ns_of_current(); int rc = 0; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) isp = smk_of_inode(inode); + else if (strcmp(name, XATTR_SMACK_EXEC) == 0) + isp = smk_of_exec(inode); + else if (strcmp(name, XATTR_SMACK_MMAP) == 0) + isp = smk_of_mmap(inode); if (isp) { - *buffer = smk_find_label_name(isp); + *buffer = smk_find_label_name(isp, ns); if (*buffer == NULL) *buffer = smack_known_huh.smk_known; return strlen(*buffer); @@ -1460,7 +1555,7 @@ static int smack_inode_getsecurity(const struct inode *inode, return -EOPNOTSUPP; if (rc == 0) { - *buffer = smk_find_label_name(isp); + *buffer = smk_find_label_name(isp, ns); if (*buffer == NULL) *buffer = smack_known_huh.smk_known; rc = strlen(*buffer); @@ -1571,18 +1666,19 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, { int rc = 0; struct smk_audit_info ad; + struct user_namespace *ns = ns_of_current(); struct inode *inode = file_inode(file); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); if (_IOC_DIR(cmd) & _IOC_WRITE) { - rc = smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, MAY_WRITE, &ad); rc = smk_bu_file(file, MAY_WRITE, rc); } if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) { - rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, MAY_READ, &ad); rc = smk_bu_file(file, MAY_READ, rc); } @@ -1600,11 +1696,12 @@ static int smack_file_lock(struct file *file, unsigned int cmd) { struct smk_audit_info ad; int rc; + struct user_namespace *ns = ns_of_current(); struct inode *inode = file_inode(file); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, MAY_LOCK, &ad); rc = smk_bu_file(file, MAY_LOCK, rc); return rc; } @@ -1626,6 +1723,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, { struct smk_audit_info ad; int rc = 0; + struct user_namespace *ns = ns_of_current(); struct inode *inode = file_inode(file); switch (cmd) { @@ -1635,14 +1733,14 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, case F_SETLKW: smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, MAY_LOCK, &ad); rc = smk_bu_file(file, MAY_LOCK, rc); break; case F_SETOWN: case F_SETSIG: smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - rc = smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, MAY_WRITE, &ad); rc = smk_bu_file(file, MAY_WRITE, rc); break; default: @@ -1672,6 +1770,7 @@ static int smack_mmap_file(struct file *file, struct task_smack *tsp; struct smack_known *okp; struct inode_smack *isp; + struct user_namespace *sns; int may; int mmay; int tmay; @@ -1682,12 +1781,16 @@ static int smack_mmap_file(struct file *file, tsp = current_security(); skp = smk_of_task(tsp); + sns = ns_of_current(); isp = file_inode(file)->i_security; mkp = isp->smk_mmap; if (mkp == NULL) return 0; + if (!smk_labels_valid(skp, mkp, sns)) + return -EACCES; + rc = 0; rcu_read_lock(); @@ -1703,6 +1806,7 @@ static int smack_mmap_file(struct file *file, */ if (mkp->smk_known == okp->smk_known) continue; + /* * If there is a matching local rule take * that into account as well. @@ -1782,8 +1886,10 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { struct smack_known *skp; - struct smack_known *tkp = smk_of_task(tsk->cred->security); + struct smack_known *tkp; struct file *file; + struct user_namespace *sns; + struct user_namespace *tns; int rc; struct smk_audit_info ad; @@ -1791,12 +1897,17 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, * struct fown_struct is never outside the context of a struct file */ file = container_of(fown, struct file, f_owner); + skp = file->f_security; + sns = file->f_cred->user_ns; + + tkp = smk_of_task_struct(tsk); + tns = ns_of_task_struct(tsk); /* we don't log here as rc can be overriden */ - skp = file->f_security; - rc = smk_access(skp, tkp, MAY_WRITE, NULL); + rc = smk_access(skp, tkp, sns, MAY_WRITE, NULL); rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); - if (rc != 0 && smack_has_privilege(tsk, CAP_MAC_OVERRIDE)) + if (rc != 0 && smk_labels_valid(skp, tkp, sns) + && smack_has_ns_privilege(tsk, tns, CAP_MAC_OVERRIDE)) rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); @@ -1816,6 +1927,7 @@ static int smack_file_receive(struct file *file) int rc; int may = 0; struct smk_audit_info ad; + struct user_namespace *ns = ns_of_current(); struct inode *inode = file_inode(file); if (unlikely(IS_PRIVATE(inode))) @@ -1831,7 +1943,7 @@ static int smack_file_receive(struct file *file) if (file->f_mode & FMODE_WRITE) may |= MAY_WRITE; - rc = smk_curacc(smk_of_inode(inode), may, &ad); + rc = smk_curacc(smk_of_inode(inode), ns, may, &ad); rc = smk_bu_file(file, may, rc); return rc; } @@ -1851,16 +1963,19 @@ static int smack_file_receive(struct file *file) static int smack_file_open(struct file *file, const struct cred *cred) { struct task_smack *tsp = cred->security; + struct user_namespace *ns = cred->user_ns; struct inode *inode = file_inode(file); + struct inode_smack *isp = file_inode(file)->i_security; struct smk_audit_info ad; int rc; - if (smack_privileged(CAP_MAC_OVERRIDE)) + if (smk_labels_valid(tsp->smk_task, isp->smk_inode, ns) && + smack_ns_privileged(ns, CAP_MAC_OVERRIDE)) return 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); - rc = smk_access(tsp->smk_task, smk_of_inode(inode), MAY_READ, &ad); + rc = smk_access(tsp->smk_task, smk_of_inode(inode), ns, MAY_READ, &ad); rc = smk_bu_credfile(cred, file, MAY_READ, rc); return rc; @@ -2015,12 +2130,13 @@ static int smk_curacc_on_task(struct task_struct *p, int access, const char *caller) { struct smk_audit_info ad; - struct smack_known *skp = smk_of_task_struct(p); + struct smack_known *tkp = smk_of_task_struct(p); + struct user_namespace *tns = ns_of_task_struct(p); int rc; smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); - rc = smk_curacc(skp, access, &ad); + rc = smk_curacc(tkp, tns, access, &ad); rc = smk_bu_task(p, access, rc); return rc; } @@ -2161,6 +2277,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, struct smk_audit_info ad; struct smack_known *skp; struct smack_known *tkp = smk_of_task_struct(p); + struct user_namespace *tns = ns_of_task_struct(p); int rc; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); @@ -2170,7 +2287,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * can write the receiver. */ if (secid == 0) { - rc = smk_curacc(tkp, MAY_WRITE, &ad); + rc = smk_curacc(tkp, tns, MAY_WRITE, &ad); rc = smk_bu_task(p, MAY_WRITE, rc); return rc; } @@ -2180,8 +2297,9 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * we can't take privilege into account. */ skp = smack_from_secid(secid); - rc = smk_access(skp, tkp, MAY_WRITE, &ad); + rc = smk_access(skp, tkp, tns, MAY_WRITE, &ad); rc = smk_bu_note("USB signal", skp, tkp, MAY_WRITE, rc); + return rc; } @@ -2236,6 +2354,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) { struct smack_known *skp = smk_of_current(); + struct user_namespace *ns = ns_of_current(); struct socket_smack *ssp; ssp = kzalloc(sizeof(struct socket_smack), gfp_flags); @@ -2245,6 +2364,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ssp->smk_in = skp; ssp->smk_out = skp; ssp->smk_packet = NULL; + ssp->smk_ns = get_user_ns(ns); sk->sk_security = ssp; @@ -2259,7 +2379,11 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) */ static void smack_sk_free_security(struct sock *sk) { + struct socket_smack *ssp = sk->sk_security; + + put_user_ns(ssp->smk_ns); kfree(sk->sk_security); + sk->sk_security = NULL; } /** @@ -2420,6 +2544,7 @@ static int smack_netlabel(struct sock *sk, int labeled) static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) { struct smack_known *skp; + struct user_namespace *sns; int rc; int sk_lbl; struct smack_known *hkp; @@ -2439,7 +2564,8 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) #endif sk_lbl = SMACK_UNLABELED_SOCKET; skp = ssp->smk_out; - rc = smk_access(skp, hkp, MAY_WRITE, &ad); + sns = ssp->smk_ns; + rc = smk_access(skp, hkp, sns, MAY_WRITE, &ad); rc = smk_bu_note("IPv4 host check", skp, hkp, MAY_WRITE, rc); } else { sk_lbl = SMACK_CIPSO_SOCKET; @@ -2464,6 +2590,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) */ static int smk_ipv6_check(struct smack_known *subject, struct smack_known *object, + struct user_namespace *ns, struct sockaddr_in6 *address, int act) { #ifdef CONFIG_AUDIT @@ -2481,7 +2608,7 @@ static int smk_ipv6_check(struct smack_known *subject, else ad.a.u.net->v6info.daddr = address->sin6_addr; #endif - rc = smk_access(subject, object, MAY_WRITE, &ad); + rc = smk_access(subject, object, ns, MAY_WRITE, &ad); rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc); return rc; } @@ -2574,6 +2701,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, struct smk_port_label *spp; struct socket_smack *ssp = sk->sk_security; struct smack_known *skp = NULL; + struct user_namespace *sns = ssp->smk_ns; unsigned short port; struct smack_known *object; @@ -2617,7 +2745,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, break; } - return smk_ipv6_check(skp, object, address, act); + return smk_ipv6_check(skp, object, sns, address, act); } #endif /* SMACK_IPV6_PORT_LABELING */ @@ -2640,12 +2768,13 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, struct inode_smack *nsp = inode->i_security; struct socket_smack *ssp; struct socket *sock; + struct user_namespace *ns = ns_of_current(); int rc = 0; if (value == NULL || size > SMK_LONGLABEL || size == 0) return -EINVAL; - skp = smk_get_label(value, size, true); + skp = smk_get_label(value, size, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); @@ -2765,6 +2894,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, #ifdef SMACK_IPV6_SECMARK_LABELING struct smack_known *rsp; struct socket_smack *ssp = sock->sk->sk_security; + struct user_namespace *sns = ssp->smk_ns; #endif if (sock->sk == NULL) @@ -2782,7 +2912,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, #ifdef SMACK_IPV6_SECMARK_LABELING rsp = smack_ipv6host_label(sip); if (rsp != NULL) - rc = smk_ipv6_check(ssp->smk_out, rsp, sip, + rc = smk_ipv6_check(ssp->smk_out, rsp, sns, sip, SMK_CONNECTING); #endif #ifdef SMACK_IPV6_PORT_LABELING @@ -2839,14 +2969,14 @@ static void smack_msg_msg_free_security(struct msg_msg *msg) } /** - * smack_of_shm - the smack pointer for the shm + * security_of_shm - the smack pointer for the shm * @shp: the object * - * Returns a pointer to the smack value + * Returns a pointer to the security_smack struct */ -static struct smack_known *smack_of_shm(struct shmid_kernel *shp) +static struct ipc_smack *security_of_shm(struct shmid_kernel *shp) { - return (struct smack_known *)shp->shm_perm.security; + return (struct ipc_smack *)shp->shm_perm.security; } /** @@ -2858,9 +2988,16 @@ static struct smack_known *smack_of_shm(struct shmid_kernel *shp) static int smack_shm_alloc_security(struct shmid_kernel *shp) { struct kern_ipc_perm *isp = &shp->shm_perm; - struct smack_known *skp = smk_of_current(); + struct ipc_smack *ssp; + + ssp = kzalloc(sizeof(struct ipc_smack), GFP_KERNEL); + if (ssp == NULL) + return -ENOMEM; + + ssp->smk_known = smk_of_current(); + ssp->smk_ns = get_user_ns(ns_of_current()); - isp->security = skp; + isp->security = ssp; return 0; } @@ -2873,7 +3010,10 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp) static void smack_shm_free_security(struct shmid_kernel *shp) { struct kern_ipc_perm *isp = &shp->shm_perm; + struct ipc_smack *ssp = isp->security; + put_user_ns(ssp->smk_ns); + kfree(isp->security); isp->security = NULL; } @@ -2886,7 +3026,7 @@ static void smack_shm_free_security(struct shmid_kernel *shp) */ static int smk_curacc_shm(struct shmid_kernel *shp, int access) { - struct smack_known *ssp = smack_of_shm(shp); + struct ipc_smack *ssp = security_of_shm(shp); struct smk_audit_info ad; int rc; @@ -2894,8 +3034,8 @@ static int smk_curacc_shm(struct shmid_kernel *shp, int access) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = shp->shm_perm.id; #endif - rc = smk_curacc(ssp, access, &ad); - rc = smk_bu_current("shm", ssp, access, rc); + rc = smk_curacc(ssp->smk_known, ssp->smk_ns, access, &ad); + rc = smk_bu_current("shm", ssp->smk_known, access, rc); return rc; } @@ -2966,14 +3106,14 @@ static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, } /** - * smack_of_sem - the smack pointer for the sem + * security_of_sem - the smack pointer for the sem * @sma: the object * - * Returns a pointer to the smack value + * Returns a pointer to the ipc_smack struct */ -static struct smack_known *smack_of_sem(struct sem_array *sma) +static struct ipc_smack *security_of_sem(struct sem_array *sma) { - return (struct smack_known *)sma->sem_perm.security; + return (struct ipc_smack *)sma->sem_perm.security; } /** @@ -2985,9 +3125,16 @@ static struct smack_known *smack_of_sem(struct sem_array *sma) static int smack_sem_alloc_security(struct sem_array *sma) { struct kern_ipc_perm *isp = &sma->sem_perm; - struct smack_known *skp = smk_of_current(); + struct ipc_smack *ssp; + + ssp = kzalloc(sizeof(struct ipc_smack), GFP_KERNEL); + if (ssp == NULL) + return -ENOMEM; + + ssp->smk_known = smk_of_current(); + ssp->smk_ns = get_user_ns(ns_of_current()); - isp->security = skp; + isp->security = ssp; return 0; } @@ -3000,7 +3147,10 @@ static int smack_sem_alloc_security(struct sem_array *sma) static void smack_sem_free_security(struct sem_array *sma) { struct kern_ipc_perm *isp = &sma->sem_perm; + struct ipc_smack *ssp = isp->security; + put_user_ns(ssp->smk_ns); + kfree(isp->security); isp->security = NULL; } @@ -3013,7 +3163,7 @@ static void smack_sem_free_security(struct sem_array *sma) */ static int smk_curacc_sem(struct sem_array *sma, int access) { - struct smack_known *ssp = smack_of_sem(sma); + struct ipc_smack *ssp = security_of_sem(sma); struct smk_audit_info ad; int rc; @@ -3021,8 +3171,8 @@ static int smk_curacc_sem(struct sem_array *sma, int access) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = sma->sem_perm.id; #endif - rc = smk_curacc(ssp, access, &ad); - rc = smk_bu_current("sem", ssp, access, rc); + rc = smk_curacc(ssp->smk_known, ssp->smk_ns, access, &ad); + rc = smk_bu_current("sem", ssp->smk_known, access, rc); return rc; } @@ -3107,9 +3257,16 @@ static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, static int smack_msg_queue_alloc_security(struct msg_queue *msq) { struct kern_ipc_perm *kisp = &msq->q_perm; - struct smack_known *skp = smk_of_current(); + struct ipc_smack *ssp; + + ssp = kzalloc(sizeof(struct ipc_smack), GFP_KERNEL); + if (ssp == NULL) + return -ENOMEM; - kisp->security = skp; + ssp->smk_known = smk_of_current(); + ssp->smk_ns = get_user_ns(ns_of_current()); + + kisp->security = ssp; return 0; } @@ -3122,19 +3279,22 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq) static void smack_msg_queue_free_security(struct msg_queue *msq) { struct kern_ipc_perm *kisp = &msq->q_perm; + struct ipc_smack *ssp = kisp->security; + put_user_ns(ssp->smk_ns); + kfree(kisp->security); kisp->security = NULL; } /** - * smack_of_msq - the smack pointer for the msq + * security_of_msq - the smack pointer for the msq * @msq: the object * - * Returns a pointer to the smack label entry + * Returns a pointer to the ipc_smack struct */ -static struct smack_known *smack_of_msq(struct msg_queue *msq) +static struct ipc_smack *security_of_msq(struct msg_queue *msq) { - return (struct smack_known *)msq->q_perm.security; + return (struct ipc_smack *)msq->q_perm.security; } /** @@ -3146,7 +3306,7 @@ static struct smack_known *smack_of_msq(struct msg_queue *msq) */ static int smk_curacc_msq(struct msg_queue *msq, int access) { - struct smack_known *msp = smack_of_msq(msq); + struct ipc_smack *msp = security_of_msq(msq); struct smk_audit_info ad; int rc; @@ -3154,8 +3314,8 @@ static int smk_curacc_msq(struct msg_queue *msq, int access) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = msq->q_perm.id; #endif - rc = smk_curacc(msp, access, &ad); - rc = smk_bu_current("msq", msp, access, rc); + rc = smk_curacc(msp->smk_known, msp->smk_ns, access, &ad); + rc = smk_bu_current("msq", msp->smk_known, access, rc); return rc; } @@ -3249,7 +3409,7 @@ static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, */ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) { - struct smack_known *iskp = ipp->security; + struct ipc_smack *isp = ipp->security; int may = smack_flags_to_may(flag); struct smk_audit_info ad; int rc; @@ -3258,8 +3418,8 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = ipp->id; #endif - rc = smk_curacc(iskp, may, &ad); - rc = smk_bu_current("svipc", iskp, may, rc); + rc = smk_curacc(isp->smk_known, isp->smk_ns, may, &ad); + rc = smk_bu_current("svipc", isp->smk_known, may, rc); return rc; } @@ -3270,9 +3430,9 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) */ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) { - struct smack_known *iskp = ipp->security; + struct ipc_smack *iskp = ipp->security; - *secid = iskp->smk_secid; + *secid = iskp->smk_known->smk_secid; } /** @@ -3530,13 +3690,14 @@ int smack_getprocattr_seq(struct task_struct *p, const char *name, static int smack_getprocattr(struct task_struct *p, char *name, char **value) { struct smack_known *skp = smk_of_task_struct(p); + struct user_namespace *ns = ns_of_current(); char *cp; int slen; if (strcmp(name, "current") != 0) return -EINVAL; - cp = smk_find_label_name(skp); + cp = smk_find_label_name(skp, ns); if (cp == NULL) cp = smack_known_huh.smk_known; cp = kstrdup(cp, GFP_KERNEL); @@ -3564,6 +3725,7 @@ static int proc_current_write(struct task_struct *p, void *value, size_t size) struct task_smack *tsp; struct cred *new; struct smack_known *skp; + struct user_namespace *ns; /* * Changing another process' Smack value is too dangerous @@ -3572,13 +3734,15 @@ static int proc_current_write(struct task_struct *p, void *value, size_t size) if (p != current) return -EPERM; - if (!smack_privileged(CAP_MAC_ADMIN)) + ns = ns_of_current(); + + if (!smack_ns_privileged(ns, CAP_MAC_ADMIN)) return -EPERM; if (value == NULL || size == 0 || size >= SMK_LONGLABEL) return -EINVAL; - skp = smk_get_label(value, size, true); + skp = smk_get_label(value, size, true, ns); if (IS_ERR(skp)) return PTR_ERR(skp); @@ -3645,23 +3809,27 @@ static int smack_unix_stream_connect(struct sock *sock, struct smack_known *okp_out = osp->smk_out; struct smack_known *skp_in = ssp->smk_in; struct smack_known *okp_in = osp->smk_in; + struct user_namespace *sns = ssp->smk_ns; + struct user_namespace *ons = osp->smk_ns; struct smk_audit_info ad; int rc = 0; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif - if (!smack_privileged(CAP_MAC_OVERRIDE)) { + if (!smack_ns_privileged(ons, CAP_MAC_OVERRIDE) || + !smk_labels_valid(skp_out, okp_in, sns) || + !smk_labels_valid(okp_out, skp_in, ons)) { #ifdef CONFIG_AUDIT smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); smk_ad_setfield_u_net_sk(&ad, other); #endif - rc = smk_access(skp_out, okp_in, MAY_WRITE, &ad); + rc = smk_access(skp_out, okp_in, sns, MAY_WRITE, &ad); rc = smk_bu_note("UDS connect", skp_out, okp_in, MAY_WRITE, rc); if (rc == 0) { - rc = smk_access(okp_out, skp_in, MAY_WRITE, &ad); + rc = smk_access(okp_out, skp_in, ons, MAY_WRITE, &ad); rc = smk_bu_note("UDS connect", okp_out, skp_in, - MAY_WRITE, rc); + MAY_WRITE, rc); } } @@ -3688,6 +3856,8 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) { struct socket_smack *ssp = sock->sk->sk_security; struct socket_smack *osp = other->sk->sk_security; + struct user_namespace *sns = ssp->smk_ns; + struct user_namespace *ons = osp->smk_ns; struct smk_audit_info ad; int rc; @@ -3698,10 +3868,11 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) smk_ad_setfield_u_net_sk(&ad, other->sk); #endif - if (smack_privileged(CAP_MAC_OVERRIDE)) + if (smk_labels_valid(ssp->smk_out, osp->smk_in, sns) && + smack_ns_privileged(ons, CAP_MAC_OVERRIDE)) return 0; - rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + rc = smk_access(ssp->smk_out, osp->smk_in, sns, MAY_WRITE, &ad); rc = smk_bu_note("UDS send", ssp->smk_out, osp->smk_in, MAY_WRITE, rc); return rc; } @@ -3724,8 +3895,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name; #endif #ifdef SMACK_IPV6_SECMARK_LABELING - struct socket_smack *ssp = sock->sk->sk_security; struct smack_known *rsp; + struct socket_smack *ssp = sock->sk->sk_security; + struct user_namespace *sns = ssp->smk_ns; #endif int rc = 0; @@ -3743,7 +3915,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, #ifdef SMACK_IPV6_SECMARK_LABELING rsp = smack_ipv6host_label(sap); if (rsp != NULL) - rc = smk_ipv6_check(ssp->smk_out, rsp, sap, + rc = smk_ipv6_check(ssp->smk_out, rsp, sns, sap, SMK_CONNECTING); #endif #ifdef SMACK_IPV6_PORT_LABELING @@ -3951,7 +4123,7 @@ access_check: * This is the simplist possible security model * for networking. */ - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, ssp->smk_ns, MAY_WRITE, &ad); rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) @@ -3975,7 +4147,7 @@ access_check: ad.a.u.net->netif = skb->skb_iif; ipv6_skb_to_auditdata(skb, &ad.a, NULL); #endif /* CONFIG_AUDIT */ - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, ssp->smk_ns, MAY_WRITE, &ad); rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in, MAY_WRITE, rc); #endif /* SMACK_IPV6_SECMARK_LABELING */ @@ -4187,7 +4359,7 @@ access_check: * Receiving a packet requires that the other end be able to write * here. Read access is not required. */ - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, ssp->smk_ns, MAY_WRITE, &ad); rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) return rc; @@ -4291,6 +4463,7 @@ static int smack_key_permission(key_ref_t key_ref, struct key *keyp; struct smk_audit_info ad; struct smack_known *tkp = smk_of_task(cred->security); + struct user_namespace *tns = cred->user_ns; int request = 0; int rc; @@ -4317,7 +4490,7 @@ static int smack_key_permission(key_ref_t key_ref, request = MAY_READ; if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) request = MAY_WRITE; - rc = smk_access(tkp, keyp->security, request, &ad); + rc = smk_access(tkp, keyp->security, tns, request, &ad); rc = smk_bu_note("key access", tkp, keyp->security, request, rc); return rc; } @@ -4334,6 +4507,7 @@ static int smack_key_permission(key_ref_t key_ref, static int smack_key_getsecurity(struct key *key, char **_buffer) { struct smack_known *skp = key->security; + struct user_namespace *ns = ns_of_current(); size_t length; char *copy; @@ -4342,7 +4516,7 @@ static int smack_key_getsecurity(struct key *key, char **_buffer) return 0; } - copy = smk_find_label_name(skp); + copy = smk_find_label_name(skp, ns); if (copy == NULL) copy = smack_known_huh.smk_known; copy = kstrdup(copy, GFP_KERNEL); @@ -4520,6 +4694,11 @@ static inline void smack_userns_free(struct user_namespace *ns) static inline int smack_userns_setns(struct nsproxy *nsproxy, struct user_namespace *ns) { + struct smack_known *skp = smk_of_current(); + + if (smk_find_mapped(skp, ns) == NULL) + return -EACCES; + return 0; } @@ -4632,6 +4811,7 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(inode_setattr, smack_inode_setattr), LSM_HOOK_INIT(inode_getattr, smack_inode_getattr), LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr), + LSM_HOOK_INIT(inode_pre_setxattr, smack_inode_pre_setxattr), LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr), LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr), LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr), diff --git a/security/smack/smack_ns.c b/security/smack/smack_ns.c index 49223c4..dc2a666 100644 --- a/security/smack/smack_ns.c +++ b/security/smack/smack_ns.c @@ -206,6 +206,45 @@ unlockout: return sknp; } +/** + * smk_labels_valid - A helper to check whether labels are valid/mapped + * in the namespace and can be used there + * @sbj: a subject label to be checked + * @obj: an object label to be checked + * @ns: user namespace to check against (usually subject's) + * + * Returns true if both valid/mapped, false otherwise. + * This helper is mostly used while checking capabilities. + * The access functions check the validity of labels by themselves. + */ +bool smk_labels_valid(struct smack_known *sbj, struct smack_known *obj, + struct user_namespace *ns) +{ + struct user_namespace *user_ns; + + /* + * labels are always valid if there is no map + * (init_user_ns or unmapped descendants) + */ + user_ns = smk_find_mapped_ns(ns); + if (user_ns == NULL) + return true; + + /* + * If we have a map though, both labels need to be mapped. + */ + if (__smk_find_mapped(sbj, user_ns) == NULL) + return false; + if (__smk_find_mapped(obj, user_ns) == NULL) + return false; + + return true; +} + +/* + * proc mapping operations + */ + static void *proc_label_map_seq_start(struct seq_file *seq, loff_t *pos) { struct smack_known *skp; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 3149ec0..fe4ad24 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -340,13 +340,15 @@ static int smk_fill_rule(const char *subject, const char *object, struct smack_parsed_rule *rule, int import, int len) { - rule->smk_subject = smk_get_label(subject, len, import); + struct user_namespace *ns = ns_of_current(); + + rule->smk_subject = smk_get_label(subject, len, import, ns); if (IS_ERR(rule->smk_subject)) return PTR_ERR(rule->smk_subject); if (rule->smk_subject == NULL) return -ENOENT; - rule->smk_object = smk_get_label(object, len, import); + rule->smk_object = smk_get_label(object, len, import, ns); if (IS_ERR(rule->smk_object)) return PTR_ERR(rule->smk_object); if (rule->smk_object == NULL) @@ -573,6 +575,7 @@ static void smk_seq_stop(struct seq_file *s, void *v) static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) { + struct user_namespace *ns = ns_of_current(); char *sbj; char *obj; @@ -581,6 +584,7 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) * interface file (/smack/load or /smack/load2) * because you should expect to be able to write * anything you read back. + * Show only fully mapped rules in a namespace (both labels mapped). */ if (strlen(srp->smk_subject->smk_known) >= max || strlen(srp->smk_object->smk_known) >= max) @@ -589,8 +593,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) if (srp->smk_access == 0) return; - sbj = smk_find_label_name(srp->smk_subject); - obj = smk_find_label_name(srp->smk_object); + sbj = smk_find_label_name(srp->smk_subject, ns); + obj = smk_find_label_name(srp->smk_object, ns); if (sbj == NULL || obj == NULL) return; @@ -785,6 +789,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) struct smack_known *skp = list_entry_rcu(list, struct smack_known, list); struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + struct user_namespace *ns = ns_of_current(); char sep = '/'; char *cp; int i; @@ -800,7 +805,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) if (strlen(skp->smk_known) >= SMK_LABELLEN) return 0; - cp = smk_find_label_name(skp); + cp = smk_find_label_name(skp, ns); if (cp == NULL) return 0; @@ -853,6 +858,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, { struct smack_known *skp; struct netlbl_lsm_secattr ncats; + struct user_namespace *ns = ns_of_current(); char mapcatset[SMK_CIPSOLEN]; int maplevel; unsigned int cat; @@ -893,7 +899,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, */ mutex_lock(&smack_cipso_lock); - skp = smk_get_label(rule, 0, true); + skp = smk_get_label(rule, 0, true, ns); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto out; @@ -981,11 +987,12 @@ static int cipso2_seq_show(struct seq_file *s, void *v) struct smack_known *skp = list_entry_rcu(list, struct smack_known, list); struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + struct user_namespace *ns = ns_of_current(); char sep = '/'; char *cp; int i; - cp = smk_find_label_name(skp); + cp = smk_find_label_name(skp, ns); if (cp == NULL) return 0; @@ -1066,12 +1073,13 @@ static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos) static int net4addr_seq_show(struct seq_file *s, void *v) { struct list_head *list = v; + struct user_namespace *ns = ns_of_current(); struct smk_net4addr *skp = list_entry_rcu(list, struct smk_net4addr, list); char *kp = SMACK_CIPSO_OPTION; if (skp->smk_label != NULL) { - kp = smk_find_label_name(skp->smk_label); + kp = smk_find_label_name(skp->smk_label, ns); if (kp == NULL) kp = smack_known_huh.smk_known; } @@ -1167,6 +1175,7 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, int rc; struct netlbl_audit audit_info; struct in_addr mask; + struct user_namespace *ns = ns_of_current(); unsigned int m; unsigned int masks; int found; @@ -1226,7 +1235,7 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, * If smack begins with '-', it is an option, don't import it */ if (smack[0] != '-') { - skp = smk_get_label(smack, 0, true); + skp = smk_get_label(smack, 0, true, ns); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto free_out; @@ -1345,10 +1354,11 @@ static int net6addr_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smk_net6addr *skp = list_entry(list, struct smk_net6addr, list); + struct user_namespace *ns = ns_of_current(); char *kp; if (skp->smk_label != NULL) { - kp = smk_find_label_name(skp->smk_label); + kp = smk_find_label_name(skp->smk_label, ns); if (kp == NULL) kp = smack_known_huh.smk_known; @@ -1438,6 +1448,7 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, struct in6_addr newname; struct in6_addr fullmask; struct smack_known *skp = NULL; + struct user_namespace *ns = ns_of_current(); char *smack; char *data; int rc = 0; @@ -1508,7 +1519,7 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, * If smack begins with '-', it is an option, don't import it */ if (smack[0] != '-') { - skp = smk_get_label(smack, 0, true); + skp = smk_get_label(smack, 0, true, ns); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto free_out; @@ -1827,6 +1838,7 @@ static const struct file_operations smk_mapped_ops = { static ssize_t smk_read_ambient(struct file *filp, char __user *buf, size_t cn, loff_t *ppos) { + struct user_namespace *ns = ns_of_current(); ssize_t rc = -EINVAL; char *cp; int asize; @@ -1839,7 +1851,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, */ mutex_lock(&smack_ambient_lock); - cp = smk_find_label_name(smack_net_ambient); + cp = smk_find_label_name(smack_net_ambient, ns); if (cp == NULL) cp = smack_known_huh.smk_known; @@ -1866,6 +1878,7 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct smack_known *skp; + struct user_namespace *ns = ns_of_current(); char *oldambient; char *data; int rc = count; @@ -1882,7 +1895,7 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, goto out; } - skp = smk_get_label(data, count, true); + skp = smk_get_label(data, count, true, ns); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto out; @@ -1923,11 +1936,12 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) static int onlycap_seq_show(struct seq_file *s, void *v) { char *smack; + struct user_namespace *ns = ns_of_current(); struct list_head *list = v; struct smack_onlycap *sop = list_entry_rcu(list, struct smack_onlycap, list); - smack = smk_find_label_name(sop->smk_label); + smack = smk_find_label_name(sop->smk_label, ns); if (smack == NULL) smack = smack_known_huh.smk_known; @@ -2006,6 +2020,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, struct smack_onlycap *sop; struct smack_onlycap *sop2; LIST_HEAD(list_tmp); + struct user_namespace *ns = ns_of_current(); int rc = count; if (!smack_privileged(CAP_MAC_ADMIN)) @@ -2025,7 +2040,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (!*tok) continue; - skp = smk_get_label(tok, 0, true); + skp = smk_get_label(tok, 0, true, ns); if (IS_ERR(skp)) { rc = PTR_ERR(skp); break; @@ -2091,12 +2106,13 @@ static ssize_t smk_read_unconfined(struct file *filp, char __user *buf, char *smack = ""; ssize_t rc = -EINVAL; int asize; + struct user_namespace *ns = ns_of_current(); if (*ppos != 0) return 0; if (smack_unconfined != NULL) { - smack = smk_find_label_name(smack_unconfined); + smack = smk_find_label_name(smack_unconfined, ns); if (smack == NULL) smack = smack_known_huh.smk_known; } @@ -2123,6 +2139,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, { char *data; struct smack_known *skp; + struct user_namespace *ns = ns_of_current(); int rc = count; if (!smack_privileged(CAP_MAC_ADMIN)) @@ -2146,7 +2163,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, * * But do so only on invalid label, not on system errors. */ - skp = smk_get_label(data, count, true); + skp = smk_get_label(data, count, true, ns); if (PTR_ERR(skp) == -EINVAL) skp = NULL; else if (IS_ERR(skp)) { @@ -2318,6 +2335,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, size_t count, loff_t *ppos, int format) { struct smack_parsed_rule rule; + struct user_namespace *ns = ns_of_current(); char *data; int res; @@ -2337,7 +2355,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, } if (res >= 0) - res = smk_access(rule.smk_subject, rule.smk_object, + res = smk_access(rule.smk_subject, rule.smk_object, ns, rule.smk_access1, NULL); else if (res != -ENOENT) return res; @@ -2547,6 +2565,7 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, struct smack_rule *sp; struct list_head *rule_list; struct mutex *rule_lock; + struct user_namespace *ns = ns_of_current(); int rc = count; if (*ppos != 0) @@ -2567,7 +2586,7 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, goto out_data; } - skp = smk_get_label(data, count, false); + skp = smk_get_label(data, count, false, ns); if (IS_ERR(skp)) { rc = PTR_ERR(skp); goto out_data; @@ -2649,12 +2668,13 @@ static ssize_t smk_read_syslog(struct file *filp, char __user *buf, char *smack = ""; ssize_t rc = -EINVAL; int asize; + struct user_namespace *ns = ns_of_current(); if (*ppos != 0) return 0; if (smack_syslog_label != NULL) { - smack = smk_find_label_name(smack_syslog_label); + smack = smk_find_label_name(smack_syslog_label, ns); if (smack == NULL) smack = smack_known_huh.smk_known; } @@ -2681,6 +2701,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, { char *data; struct smack_known *skp; + struct user_namespace *ns = ns_of_current(); int rc = count; if (!smack_privileged(CAP_MAC_ADMIN)) @@ -2704,7 +2725,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, * * But do so only on invalid label, not on system errors. */ - skp = smk_get_label(data, count, true); + skp = smk_get_label(data, count, true, ns); if (PTR_ERR(skp) == -EINVAL) skp = NULL; else if (IS_ERR(skp)) { -- 2.4.3 From l.pawelczyk at samsung.com Wed Oct 14 12:42:05 2015 From: l.pawelczyk at samsung.com (Lukasz Pawelczyk) Date: Wed, 14 Oct 2015 14:42:05 +0200 Subject: [PATCH v4 11/11] smack: documentation for the Smack namespace In-Reply-To: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> Message-ID: <1444826525-9758-12-git-send-email-l.pawelczyk@samsung.com> Adds Documentation/smack-namespace.txt. Signed-off-by: Lukasz Pawelczyk Reviewed-by: Casey Schaufler --- Documentation/security/00-INDEX | 2 + Documentation/security/Smack-namespace.txt | 231 +++++++++++++++++++++++++++++ MAINTAINERS | 1 + security/smack/Kconfig | 2 + 4 files changed, 236 insertions(+) create mode 100644 Documentation/security/Smack-namespace.txt diff --git a/Documentation/security/00-INDEX b/Documentation/security/00-INDEX index 45c82fd..c03a220 100644 --- a/Documentation/security/00-INDEX +++ b/Documentation/security/00-INDEX @@ -6,6 +6,8 @@ SELinux.txt - how to get started with the SELinux security enhancement. Smack.txt - documentation on the Smack Linux Security Module. +Smack-namespace.txt + - documentation on the Smack namespace implementation. Yama.txt - documentation on the Yama Linux Security Module. apparmor.txt diff --git a/Documentation/security/Smack-namespace.txt b/Documentation/security/Smack-namespace.txt new file mode 100644 index 0000000..5304355 --- /dev/null +++ b/Documentation/security/Smack-namespace.txt @@ -0,0 +1,231 @@ + + "Quis custodiet ipsos custodes?" + - Satires of Juvenal + + +--- What is a Smack namespace --- + +Smack namespace was developed to make it possible for Smack to work +nicely with Linux containers where there is a full operating system +with its own init inside the namespace. Such a system working with +Smack expects to have at least partially working SMACK_MAC_ADMIN to be +able to change labels of processes and files. This is required to be +able to securely start applications under the control of Smack and +manage their access rights. + +It was implemented using new LSM hooks added to the user namespace +that were developed together with Smack namespace. + + +--- Design ideas --- + +"Smack namespace" is rather "Smack labels namespace" as not the whole +MAC is namespaced, only the labels. There is a great analogy between +Smack labels namespace and the user namespace part that remaps UIDs. + +The idea is to create a map of labels for a namespace so the namespace +is only allowed to use those labels. Smack rules are always the same +as in the init namespace (limited only by what labels are mapped) and +cannot be manipulated from the child namespace. The map is actually +only for labels' names. The underlying structures for labels remain +the same. The filesystem also stores the "unmapped" labels from the +init namespace. + +Let's say we have those labels in the init namespace: +label1 +label2 +label3 + +and those rules: +label1 label2 rwx +label1 label3 rwx +label2 label3 rwx + +We create a map for a namespace: +label1 -> mapped1 +label2 -> mapped2 + +This means that 'label3' is completely invisible in the namespace. As if +it didn't exist. All the rules that include it are ignored. + +Effectively in the namespace we have only one rule: +mapped1 mapped2 rwx + +Which in reality is: +label1 label2 rwx + +All requests to access an object with a 'label3' will be denied. If it +ever comes to a situation where 'label3' would have to be printed +(e.g. reading an exec or mmap label from a file to which we have +access) then huh sign '?' will be printed instead. + +All the operations in the namespace on the remaining labels will have +to be performed using their mapped names. Things like changing own +process's label, changing filesystem label. Labels will also be +printed with their mapped names. + +You cannot import new labels in a namespace. Every operation that +would do so in an init namespace will return an error in the child +namespace. You cannot assign an unmapped or not existing label to an +object. You can only operate on labels that have been explicitly +mapped. + + +--- Capabilities --- + +Enabling Smack related capabilities (CAP_MAC_ADMIN and +CAP_MAC_OVERRIDE) is main goal of Smack namespace, so it can work +properly in the container. And those capabilities do work to some +extent. In several places where capabilities are checked compatibility +with Smack namespace has been introduced. Capabilities are of course +limited to operate only on mapped labels. + +CAP_MAC_OVERRIDE works fully, will allow you to ignore Smack access +rules, but only between objects that have labels mapped. So in the +example above having this CAP will allow e.g. label2 to write to +label1, but will not allow any access to label3. + +With CAP_MAC_ADMIN the following operations has been allowed inside +the namespace: +- setting and removing xattr on files, including the security.* ones +- setting process's own label (/proc/self/attr/current) +- mounting in a privileged Smack mode, which means one can specify + additional mount options like: smackfsdef, smackfsfloor etc. + +Again this is also allowed only on the mapped labels. Labels on the +filesystem will be stored in unmapped form so they are preserved +through reboots. + +Such a namespace construct allows e.g. systemd (with Smack support) +working in a container to assign labels properly to daemons and other +processes. + + +--- Usage --- + +Smack namespace is written using LSM hooks inside user namespace. That +means it's connected to it. + +To create a new Smack namespace you need to unshare() user namespace +as usual. If that is all you do though, than there is no difference to +what is now. To activate the Smack namespace you need to fill the +labels' map. It is in a file /proc/$PID/attr/label_map. + +By default the map is empty and Smack namespaces are inactive (labels +are taken directly from a parent namespace). It also means that the +Smack capabilities will be inactive. After you fill the map it starts +to take effect in the namespace and Smack capabilities (only on mapped +labels) start to work. + +Due to the way Smack works only CAP_MAC_ADMIN from the parent +namespace (init_user_ns for now, see the "Current limitations" below) +is allowed to fill the map. That means that an unprivileged user is +still allowed to create the user namespace but it will not be able to +fill the labels' map (activate Smack namespace). An administrator +intervention is required. + +The attr_map write format is: +unmapped_label mapped_label + +When reading the file it shows an active map for a namespace the +process in question is in in the format: +unmapped_label -> mapped_label + +If the label_map file is empty it means the namespace is not mapped +and Smack namespace is inactive (no mappings, MAC related capabilities +behave as they did before, meaning they are active only in +init_user_ns). For init_user_ns the map will always be empty. + +Writing to the map file is not disabled after the first write as it is +in uid_map. For Smack we have no means to map ranges of labels, hence +it can really be advantageous to be able to expand the map later +on. But you can only add to the map. You cannot remove already mapped +labels. You cannot change the already existing mappings. Also mappings +has to be 1-1. All requests to create a map where either the unmapped +or the mapped label already exists in the map will be denied. + +setns() with Smack namespace active has an additional check that the +label of a process that is calling setns() has to be already mapped in +the target Smack namespace for the call to succeed. + + +--- Special labels --- + +Smack is using some special labels that have built-in rules. Things +like floor '_', dash '^', star '*', etc. Those labels are not +automatically mapped to the namespace. Moreover, you can choose to map +a different label from the init namespace to behave e.g. like floor +inside the namespace. + +Let's say we have no rules and those labels in the init namespace: +_ +floor_to_be +label + +Both 'label' and 'floor_to_be' can read objects with '_'. But they +have no access rights to each other. + +Now let's create a map like this: +_ ordinary_label +floor_to_be _ +label mapped + +Right now label 'mapped' can read label '_' which means that +effectively inside this namespace label 'label' has gained read access +to the 'floor_to_be'. The label 'ordinary_label' is exactly it, an +ordinary label that the built-in rules no longer apply to inside the +namespace. + +To sum up, special labels in the namespace behave the same as in the +init namespace. Not the original special labels though, but the ones +we map to specials. This is the only case where a namespace can have +access rights the init namespace does not have (like the 'label' to +'floor_to_be' in the example above). + +Of course mappings like these are perfectly legal: +_ _ +* * +^ ^ + + +--- Current limitations --- + +The Smack namespace is not hierarchical yet. It is currently not +possible to fill a label_map of a nested user namespace (you can still +create nested user namespace, it will just inherit its parent's map +and won't have active Smack capabilities). When hierarchy will be +implemented the process creating another namespace will be allowed to +map only labels that it has permission to itself (those that it has in +its own map). + +Special files inside the virtual smackfs needs to be reviewed whether +it's beneficial to have some of their functionality namespaced as well +(e.g. onlycap, syslog. ambient, etc). This would increase +CAP_MAC_ADMIN privileges inside the namespace. + + +--- Error codes --- + +While working in the namespace patches the error codes has been made +to propagate properly from a place they occurred. New error codes has +also been introduced for Smack in the context of namespace usage. This +is a complete summary of error codes used throughout the Smack now: + +ENOMEM and other system errors that might come from low level + kernel functions like memory allocations +EOPNOTSUPP means the underlying system operation is not + supported (eg. getxattr) +EINVAL means invalid syntax (e.g. empty label or one starting + with '-') +EEXIST when creating map means that a label is already mapped +EBADR is used for wrong namespace usage: + - trying to import a label inside a namespace (like trying + to use an unmapped label that would otherwise be imported) + - trying to create a Smack label map in the init namespace +ENOENT when failed to find a label we expected to exist (will not + be propagated to user-space) +EPERM means no permission to operate on an object, e.g. due to + insufficient capabilities or simply because the object + cannot be operated on in the current context +EACCESS when access has been denied due to Smack access checks + (including object being outside of a namespace) diff --git a/MAINTAINERS b/MAINTAINERS index 797236b..c77be5a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9590,6 +9590,7 @@ W: http://schaufler-ca.com T: git git://git.gitorious.org/smack-next/kernel.git S: Maintained F: Documentation/security/Smack.txt +F: Documentation/security/Smack-namespace.txt F: security/smack/ DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS) diff --git a/security/smack/Kconfig b/security/smack/Kconfig index b19a7fb..a6e0f3f 100644 --- a/security/smack/Kconfig +++ b/security/smack/Kconfig @@ -49,4 +49,6 @@ config SECURITY_SMACK_NS This enables Smack namespace that makes it possible to map specific labels within user namespace (analogously to mapping UIDs) and to gain MAC capabilities over them. + Documentation is availabile here: + Documentation/security/Smack-namespace.txt If you are unsure how to answer this question, answer N. -- 2.4.3 From lex25 at meitanbank.net Wed Oct 14 19:13:32 2015 From: lex25 at meitanbank.net (lex25) Date: Thu, 15 Oct 2015 03:13:32 +0800 Subject: =?GB2312?B?zeLDs7/Nu6fL0cv3v6q3osjtvP6jrL21tc3N4sOzv827p7+qt6LNttfKw8W8?= =?GB2312?B?97qjzeK1xL/Nu6fV5rXExMfDtMTRv6q3osLwo78=?= Message-ID: <201510150313323754956@meitanbank.net> be3uBV7LwE3AoHo35 ????????? ????????????????????????????????????????????????????????????????????????????????????????????? ?????????????????????????????????????????????????????????????????????????????? ????????????????????????????????????B2B?????????????????????????????????????????????????????????????????????????????????????????? F692wp3G3gEEX4Lz6vCV ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? NgACj4N2D7Bi2N9hEfjx ????????????????????????????????????????????????????????????????????????????????????????????????OEM??????????????????????????????????????????????????? ????????????B2B????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? 8NzLB8sQe76HmHFy304G ??????????????????????????? ??????QQ: 562624923 SFZ3sa3M9xda8K4w ?????????13410395337 myI95nhsze8RFf0QzkUZ ???QQ?????????????????????????????????????????????????????????????????? From khlebnikov at yandex-team.ru Tue Oct 20 10:04:23 2015 From: khlebnikov at yandex-team.ru (Konstantin Khlebnikov) Date: Tue, 20 Oct 2015 13:04:23 +0300 Subject: [PATCH RFC v3 2/2] pidns: introduce syscall getvpid In-Reply-To: <87d1x25vng.fsf@x220.int.ebiederm.org> References: <20150925135246.27620.97496.stgit@buzz> <20150925135247.27620.37109.stgit@buzz> <87d1x25vng.fsf@x220.int.ebiederm.org> Message-ID: <562611A7.7070606@yandex-team.ru> On 28.09.2015 19:57, Eric W. Biederman wrote: > Konstantin Khlebnikov writes: > >> If pid is negative then getvpid() returns pid of parent task for -pid. > > Now that I am noticing this. I don't think I have seen any discussion > about justifying a syscall getting another processes parent pid. My > apologies if I just missed it. > Sorry for late response. This completely fell out of my mind after LinuxCon. > Why do we want the the parent pid? We can we usefully do with it? > Is proc really that bad of an interface? > > Fetching a parent pid feels like a separate logical operation > from pid translation. Which makes me a bit uneasy about this > part of the conversation. Yep proc interface is bad. /proc/$pid/stat is almost impossible to parse without flaws because task could set second field "comm" into any string and fake ppid - for example ") Z 1". /proc/$pid/status is better but it has more information and thus slower. This trick for distant getppid looks cheap useful: in this interface space of negative pids is free for use. > >> Examples: >> getvpid(pid, ns, -1) - get pid in our pid namespace >> getvpid(pid, -1, ns) - get pid in container >> getvpid(pid, -1, ns) > 0 - is pid is reachable from container? >> getvpid(1, ns1, ns2) > 0 - is ns1 inside ns2? >> getvpid(1, ns1, ns2) == 0 - is ns1 outside ns2? >> getvpid(1, ns, -1) - get init task of pid-namespace >> getvpid(-1, ns, -1) - get reaper of init task in parent pid-namespace >> getvpid(-pid, -1, -1) - get ppid by pid > > As I step back and pay attention to this case I am half wondering if > perhaps what would be most useful is a file descriptor that refers > to a pid and an updated set of system calls that takes pid file > descriptors instead of pids. Fd which pins pids isn't a good idea. I think it's better to refer (but not hold) task rather than pid. For example inode of taskfd will hold small buffer for task exit status: task holds reference to its own taskfd inode and populates status when exits. Here will be no zombies and delayed reaping. Something like: task_fd = clonefd() ... select(...) exit(...) pread(task_fd, &status_rusage_etc, sizeof, 0); close(task_fd); Task pid also could be part of structure in that fd. Potentially it could provide the same information as /proc/$pid/... in effective binary format: we can read only required fields of structure and kernel can skip unneeded calculations. > > Something like: > > getpidfd(int pidnsfd, pid_t pid); > > waitfd(int pidfd, int *status, int options, struct rusage *rusage); > > killfd(int pidfd, int sig); > > clonefd(...); > > And perhaps: > pid_nr_ns(int pidnsfd, int pidfd); > > parentfd(int pidfd); > > Eric > -- Konstantin From khlebnikov at yandex-team.ru Wed Oct 21 10:08:54 2015 From: khlebnikov at yandex-team.ru (Konstantin Khlebnikov) Date: Wed, 21 Oct 2015 13:08:54 +0300 Subject: [PATCH] TTY, devpts: document pty count limiting Message-ID: <20151021100854.7888.90252.stgit@buzz> Logic was changed in kernel 3.4 by commit e9aba5158a80 ("tty: rework pty count limiting") but still isn't documented. Better late than never. Signed-off-by: Konstantin Khlebnikov --- Documentation/filesystems/devpts.txt | 9 +++++++++ Documentation/sysctl/kernel.txt | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/Documentation/filesystems/devpts.txt b/Documentation/filesystems/devpts.txt index 68dffd87f9b7..30d2fcb32f72 100644 --- a/Documentation/filesystems/devpts.txt +++ b/Documentation/filesystems/devpts.txt @@ -51,6 +51,15 @@ where 'ns_exec -cm /bin/bash' calls clone() with CLONE_NEWNS flag and execs /bin/bash in the child process. A pty created by the sshd is not visible in the original mount of /dev/pts. +Total count of pty pairs in all instances is limited by sysctls: +kernel.pty.max = 4096 - global limit +kernel.pty.reserve = 1024 - reserve for initial instance +kernel.pty.nr - current count of ptys + +Per-instance limit could be set by adding mount option "max=". +This feature was added in kernel 3.4 together with sysctl kernel.pty.reserve. +In kernels older than 3.4 sysctl kernel.pty.max works as per-instance limit. + User-space changes ------------------ diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 6fccb69c03e7..01a42021c2e5 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -63,6 +63,7 @@ show up in /proc/sys/kernel: - printk_delay - printk_ratelimit - printk_ratelimit_burst +- pty/ - randomize_va_space - real-root-dev ==> Documentation/initrd.txt - reboot-cmd [ SPARC only ] @@ -683,6 +684,12 @@ send before ratelimiting kicks in. ============================================================== +pty/: + +See Documentation/filesystems/devpts.txt. + +============================================================== + randomize_va_space: This option can be used to select the type of process address From dzf at ncs-cyber.com.cn Wed Oct 21 14:59:19 2015 From: dzf at ncs-cyber.com.cn (542508503) Date: Wed, 21 Oct 2015 22:59:19 +0800 Subject: =?gb2312?B?UmU6Y29udGFpbmVyc8rVtb272Li0o6E=?= Message-ID: <201510212259261224013@ncs-cyber.com.cn> containers ????2015??????5000?f????24??0???????????????????????????????????? ?????????t?????^ ???????????????????????????????n?????????????????????????????L???????????????????????????????????????????????????????????A?????????????????????M?????f???????????????r?????????????u?????????????????????????????o?????????r???????????????????????????????????????????????????????????L???????????????Z?????????h???????????????????????????W???????? From gregkh at linuxfoundation.org Wed Oct 21 17:04:23 2015 From: gregkh at linuxfoundation.org (Greg KH) Date: Wed, 21 Oct 2015 10:04:23 -0700 Subject: [PATCH] TTY, devpts: document pty count limiting In-Reply-To: <20151021100854.7888.90252.stgit@buzz> References: <20151021100854.7888.90252.stgit@buzz> Message-ID: <20151021170423.GA29006@kroah.com> On Wed, Oct 21, 2015 at 01:08:54PM +0300, Konstantin Khlebnikov wrote: > Logic was changed in kernel 3.4 by commit e9aba5158a80 ("tty: rework pty > count limiting") but still isn't documented. Better late than never. > > Signed-off-by: Konstantin Khlebnikov > --- > Documentation/filesystems/devpts.txt | 9 +++++++++ > Documentation/sysctl/kernel.txt | 7 +++++++ > 2 files changed, 16 insertions(+) Any reason you didn't cc: the tty maintainer on this? From ebiederm at xmission.com Thu Oct 22 04:08:52 2015 From: ebiederm at xmission.com (Eric W. Biederman) Date: Wed, 21 Oct 2015 23:08:52 -0500 Subject: [PATCH] TTY, devpts: document pty count limiting In-Reply-To: <20151021170423.GA29006@kroah.com> (Greg KH's message of "Wed, 21 Oct 2015 10:04:23 -0700") References: <20151021100854.7888.90252.stgit@buzz> <20151021170423.GA29006@kroah.com> Message-ID: <87twpjczor.fsf@x220.int.ebiederm.org> Greg KH writes: > On Wed, Oct 21, 2015 at 01:08:54PM +0300, Konstantin Khlebnikov wrote: >> Logic was changed in kernel 3.4 by commit e9aba5158a80 ("tty: rework pty >> count limiting") but still isn't documented. Better late than never. >> >> Signed-off-by: Konstantin Khlebnikov >> --- >> Documentation/filesystems/devpts.txt | 9 +++++++++ >> Documentation/sysctl/kernel.txt | 7 +++++++ >> 2 files changed, 16 insertions(+) > > Any reason you didn't cc: the tty maintainer on this? It looks like he attempted to and managed to get your old suse.de address. Eric From yuzhonu at mail.com Thu Oct 22 04:55:48 2015 From: yuzhonu at mail.com (David White) Date: Thu, 22 Oct 2015 06:55:48 +0200 Subject: grow your business Message-ID: <7de970f48d7df5fa3d3147eb2ec088b2@basingstokeleisure.com> Hey, Our email marketing and lead generation solutions can help your business grow fast. Increase your client base and market your product to millions. We have worked on a number of projects and campaigns If you are interested, I would be more than happy to develop a plan that works with your business type. Our goal is to increase your business sales 1-2 times than now. If you would require more information please contact us soon. Yours, David White Contact: lopitomn at sina.com From gregkh at linuxfoundation.org Thu Oct 22 14:33:49 2015 From: gregkh at linuxfoundation.org (Greg KH) Date: Thu, 22 Oct 2015 07:33:49 -0700 Subject: [PATCH] TTY, devpts: document pty count limiting In-Reply-To: <87twpjczor.fsf@x220.int.ebiederm.org> References: <20151021100854.7888.90252.stgit@buzz> <20151021170423.GA29006@kroah.com> <87twpjczor.fsf@x220.int.ebiederm.org> Message-ID: <20151022143349.GA21861@kroah.com> On Wed, Oct 21, 2015 at 11:08:52PM -0500, Eric W. Biederman wrote: > Greg KH writes: > > > On Wed, Oct 21, 2015 at 01:08:54PM +0300, Konstantin Khlebnikov wrote: > >> Logic was changed in kernel 3.4 by commit e9aba5158a80 ("tty: rework pty > >> count limiting") but still isn't documented. Better late than never. > >> > >> Signed-off-by: Konstantin Khlebnikov > >> --- > >> Documentation/filesystems/devpts.txt | 9 +++++++++ > >> Documentation/sysctl/kernel.txt | 7 +++++++ > >> 2 files changed, 16 insertions(+) > > > > Any reason you didn't cc: the tty maintainer on this? > > It looks like he attempted to and managed to get your old suse.de > address. Ah, you are right, my apologies, that address is long dead :) greg k-h From ebiederm at xmission.com Thu Oct 22 20:45:09 2015 From: ebiederm at xmission.com (Eric W. Biederman) Date: Thu, 22 Oct 2015 15:45:09 -0500 Subject: [PATCH] userns/capability: Add user namespace capability In-Reply-To: <5623FD82.4030902@miglix.eu> (Tobias Markus's message of "Sun, 18 Oct 2015 22:13:54 +0200") References: <5622700C.9090107@miglix.eu> <20151017215501.GA22900@mail.hallyn.com> <5623FD82.4030902@miglix.eu> Message-ID: <87twpi63ai.fsf@x220.int.ebiederm.org> Thank you for a creative solution to a problem that you perceive. I appreciate it when people aim to solve problems they see. Tobias Markus writes: > On 17.10.2015 23:55, Serge E. Hallyn wrote: >> On Sat, Oct 17, 2015 at 05:58:04PM +0200, Tobias Markus wrote: >>> Add capability CAP_SYS_USER_NS. >>> Tasks having CAP_SYS_USER_NS are allowed to create a new user namespace >>> when calling clone or unshare with CLONE_NEWUSER. >>> >>> Rationale: >>> >>> Linux 3.8 saw the introduction of unpriviledged user namespaces, >>> allowing unpriviledged users (without CAP_SYS_ADMIN) to be a "fake" root >>> inside a separate user namespace. Before that, any namespace creation >>> required CAP_SYS_ADMIN (or, in practice, the user had to be root). >>> Unfortunately, there have been some security-relevant bugs in the >>> meantime. Because of the fairly complex nature of user namespaces, it is >>> reasonable to say that future vulnerabilties can not be excluded. Some >>> distributions even wholly disable user namespaces because of this. >> >> Fwiw I'm not in favor of this. Debian has a patch (I believe the one >> I originally wrote for Ubuntu but which Ubuntu dropped long ago) adding a >> sysctl, off by default, for enabling user namespaces. > > While it certainly works, enabling a feature like this at runtime > doesn't seem like a long term solution. > > The fact that Debian added this patch in the first place already > demonstrates that there is demand for a way to limit unpriviledged user > namespace creation. Please, don't get me wrong: I would *really like* to > see widespread adoption and continued development of user namespaces! > But the status quo remains: Distributions outright disabling user > namespaces (e.g. Arch Linux) won't make it easier. Let me say I applaud Arch Linux for not doing what so many distributions do and enable every feature in the kernel. I appreciate a distribution that does not enable interesting kernel features while they are still having their bugs shaken out of them. I also think Debians approach to limit things while they mature is also wisdom. >> Posix capabilities are intended for privileged actions, not for >> actions which explicitly should not require privilege, but which >> we feel are in development. >> > > Certainly, in an ideal world, user namespaces will never lead to any > kernel-level exploits. But reality is different: There *have been* > serious kernel vulnerabilities due to user namespaces, and there *will > be* serious kernel vulnerabilities due to user namespaces. When you start talk about the future that is not yet real you have stopped talking about reality. That sounds like a pessimists world view rather than reality. The reality is new features are buggy and take time to mature. It takes time for understanding to percolate through peoples heads. > Now, those are the alternatives imho: > > * Status quo: Some distributions will disable user namespaces by default > in some way or another. User wishing to use user namespaces will have to > use a custom kernel or enable a sysctl flag that was patched in by the > downstream developers. On distributions that enable user namespaces by > default, even users that don't wish to use them in the first places will > be affected by vulnerabilities. Again I disagree. I see distributions waiting to enable user namespaces until they mature and until they are interesting enough. I do not see rushing to enable the newest features as wisdom, unless that the point of your distribution is to enable people to play with the latest features. I suspect we are quickly coming to a point where user namespaces will be sufficiently compelling that they will be enabled more widely. At this point the most helpful things I can see to be done are. - Verify all userns related fixes have made it back into 4.1.x - Play with and/or audit the userns code to see if more bugs can be found. - Analyze user namespaces and see if they are uniquely worse than anything else. I agree that if user namespaces pose a unique security challenge to the kernel we should do something about them. I think it is a healthy question to ask. For the conversation to be productive I think we need numbers and analsysis, not just worst case analsysis based on fear. To date all I see are teething pains. My back of the napkin analysis is that there are maybe 3,000 lines of code executed in user namespaces (mostly from fs/namespace.c) that are not otherwise reachable from unprivileged users, while there are perhaps 100,000 - 250,000 lines of code reachable by unprivileged users (not counting drivers). At this point I do not expect that removing access to 3 lines out of 100 will significanlty reduce the probability that someone will find exploitable code in the kernel. I do think I goofed and enabled the code in fs/namespace.c before it was ready to be accessed by unprivileged users. My apologies to everyone inconvinenced by that. Tobias I do think you have fallen into a fault in your analysis of the situtation that many other people have. The assumption that by limiting access to who can create user namespaces that we limit badness by people who are root in a user namespace. Very few of the problems I have seen go away if a user is not able to create a user namespace. Most problems exist in some when an application is root inside a user namespace. Tobias your proposal to me reads as enabling a feature only for those users most likely to exploit it, which honestly seems backwards. Eric From luto at amacapital.net Thu Oct 22 21:02:09 2015 From: luto at amacapital.net (Andy Lutomirski) Date: Thu, 22 Oct 2015 14:02:09 -0700 Subject: [PATCH] userns/capability: Add user namespace capability In-Reply-To: <87twpi63ai.fsf@x220.int.ebiederm.org> References: <5622700C.9090107@miglix.eu> <20151017215501.GA22900@mail.hallyn.com> <5623FD82.4030902@miglix.eu> <87twpi63ai.fsf@x220.int.ebiederm.org> Message-ID: On Thu, Oct 22, 2015 at 1:45 PM, Eric W. Biederman wrote: > > Thank you for a creative solution to a problem that you perceive. I > appreciate it when people aim to solve problems they see. > > Tobias Markus writes: > >> On 17.10.2015 23:55, Serge E. Hallyn wrote: >>> On Sat, Oct 17, 2015 at 05:58:04PM +0200, Tobias Markus wrote: >>>> Add capability CAP_SYS_USER_NS. >>>> Tasks having CAP_SYS_USER_NS are allowed to create a new user namespace >>>> when calling clone or unshare with CLONE_NEWUSER. >>>> >>>> Rationale: >>>> >>>> Linux 3.8 saw the introduction of unpriviledged user namespaces, >>>> allowing unpriviledged users (without CAP_SYS_ADMIN) to be a "fake" root >>>> inside a separate user namespace. Before that, any namespace creation >>>> required CAP_SYS_ADMIN (or, in practice, the user had to be root). >>>> Unfortunately, there have been some security-relevant bugs in the >>>> meantime. Because of the fairly complex nature of user namespaces, it is >>>> reasonable to say that future vulnerabilties can not be excluded. Some >>>> distributions even wholly disable user namespaces because of this. >>> >>> Fwiw I'm not in favor of this. Debian has a patch (I believe the one >>> I originally wrote for Ubuntu but which Ubuntu dropped long ago) adding a >>> sysctl, off by default, for enabling user namespaces. >> >> While it certainly works, enabling a feature like this at runtime >> doesn't seem like a long term solution. >> >> The fact that Debian added this patch in the first place already >> demonstrates that there is demand for a way to limit unpriviledged user >> namespace creation. Please, don't get me wrong: I would *really like* to >> see widespread adoption and continued development of user namespaces! >> But the status quo remains: Distributions outright disabling user >> namespaces (e.g. Arch Linux) won't make it easier. > > Let me say I applaud Arch Linux for not doing what so many distributions > do and enable every feature in the kernel. I appreciate a distribution > that does not enable interesting kernel features while they are still > having their bugs shaken out of them. > > I also think Debians approach to limit things while they mature is also > wisdom. > >>> Posix capabilities are intended for privileged actions, not for >>> actions which explicitly should not require privilege, but which >>> we feel are in development. >>> >> >> Certainly, in an ideal world, user namespaces will never lead to any >> kernel-level exploits. But reality is different: There *have been* >> serious kernel vulnerabilities due to user namespaces, and there *will >> be* serious kernel vulnerabilities due to user namespaces. > > When you start talk about the future that is not yet real you have > stopped talking about reality. That sounds like a pessimists world view > rather than reality. > > The reality is new features are buggy and take time to mature. It takes > time for understanding to percolate through peoples heads. > >> Now, those are the alternatives imho: >> >> * Status quo: Some distributions will disable user namespaces by default >> in some way or another. User wishing to use user namespaces will have to >> use a custom kernel or enable a sysctl flag that was patched in by the >> downstream developers. On distributions that enable user namespaces by >> default, even users that don't wish to use them in the first places will >> be affected by vulnerabilities. > > Again I disagree. I see distributions waiting to enable user namespaces > until they mature and until they are interesting enough. I do not see > rushing to enable the newest features as wisdom, unless that the point > of your distribution is to enable people to play with the latest > features. > > I suspect we are quickly coming to a point where user namespaces will be > sufficiently compelling that they will be enabled more widely. > > > At this point the most helpful things I can see to be done are. > - Verify all userns related fixes have made it back into 4.1.x > - Play with and/or audit the userns code to see if more bugs can be > found. > - Analyze user namespaces and see if they are uniquely worse than > anything else. > > I agree that if user namespaces pose a unique security challenge to > the kernel we should do something about them. I think it is a healthy > question to ask. For the conversation to be productive I think we need > numbers and analsysis, not just worst case analsysis based on fear. To > date all I see are teething pains. > > My back of the napkin analysis is that there are maybe 3,000 lines of > code executed in user namespaces (mostly from fs/namespace.c) that > are not otherwise reachable from unprivileged users, while there are > perhaps 100,000 - 250,000 lines of code reachable by unprivileged users > (not counting drivers). At the risk of pointing out a can of worms, the attack surface also includes things like the iptables configuration APIs, parsers, and filter/conntrack/action modules. --Andy From ebiederm at xmission.com Thu Oct 22 21:44:49 2015 From: ebiederm at xmission.com (Eric W. Biederman) Date: Thu, 22 Oct 2015 16:44:49 -0500 Subject: [PATCH] userns/capability: Add user namespace capability In-Reply-To: (Andy Lutomirski's message of "Thu, 22 Oct 2015 14:02:09 -0700") References: <5622700C.9090107@miglix.eu> <20151017215501.GA22900@mail.hallyn.com> <5623FD82.4030902@miglix.eu> <87twpi63ai.fsf@x220.int.ebiederm.org> Message-ID: <874mhi4lym.fsf@x220.int.ebiederm.org> Andy Lutomirski writes: > At the risk of pointing out a can of worms, the attack surface also > includes things like the iptables configuration APIs, parsers, and > filter/conntrack/action modules. It is worth noting that module auto-load does not happen if the triggering code does not have the proper permissions in the initial user namespace. I agree that is another piece of code that should be counted. How that compares to the other 130,000 or so lines of code in the network stack an unprivileged user can caused to be exercised already I don't know. In my back of the napkin swag I had totally forgotten to count anything in the network stack. A lot of the netfilter code that I have read and looked at is compartively simple and clean so I don't expect there is much risk except from sheer volume of code there. It is also tricky to count because the entire network side of the networking stack is exposed to hostile users on the internet so anything except the configuration is already exposed to hostile users. The average check entry is 15-20 lines long. There appear to be 117 unique check entry functions in the kernel so there may be another 2.5k lines of code there. Hmm. And we have not had any design issues with the network stack. Absent of design issues where the code even when implemented correctly has the wrong semantics, we are left with the probability of exploitable buggy code. I suspect we have enough code even without user namespaces enabled that the probability of exploitable buggy code someone in the code that unprivilged users can cause to be exercised run is > 50%. I wonder if there are any good statistical models that give realistic estimates of those things. Eric From casey at schaufler-ca.com Thu Oct 29 22:49:09 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:49:09 -0700 Subject: [PATCH v4 01/11] user_ns: 3 new LSM hooks for user namespace operations In-Reply-To: <1444826525-9758-2-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-2-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A265.7020402@schaufler-ca.com> On 10/14/2015 5:41 AM, Lukasz Pawelczyk wrote: > This commit implements 3 new LSM hooks that provide the means for LSMs > to embed their own security context within user namespace, effectively > creating some sort of a user_ns related security namespace. > > The first one to take advantage of this mechanism is Smack. > > The hooks has been documented in the in the security.h below. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler > Acked-by: Paul Moore Acked-by: Casey Schaufler > --- > include/linux/lsm_hooks.h | 28 ++++++++++++++++++++++++++++ > include/linux/security.h | 23 +++++++++++++++++++++++ > include/linux/user_namespace.h | 4 ++++ > kernel/user.c | 3 +++ > kernel/user_namespace.c | 18 ++++++++++++++++++ > security/security.c | 28 ++++++++++++++++++++++++++++ > 6 files changed, 104 insertions(+) > > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h > index ec3a6ba..18c9160 100644 > --- a/include/linux/lsm_hooks.h > +++ b/include/linux/lsm_hooks.h > @@ -1261,6 +1261,23 @@ > * audit_rule_init. > * @rule contains the allocated rule > * > + * @userns_create: > + * Allocates and fills the security part of a new user namespace. > + * @ns points to a newly created user namespace. > + * Returns 0 or an error code. > + * > + * @userns_free: > + * Deallocates the security part of a user namespace. > + * @ns points to a user namespace about to be destroyed. > + * > + * @userns_setns: > + * Run during a setns syscall to add a process to an already existing > + * user namespace. Returning failure here will block the operation > + * requested from userspace (setns() with CLONE_NEWUSER). > + * @nsproxy contains nsproxy to which the user namespace will be assigned. > + * @ns contains user namespace that is to be incorporated to the nsproxy. > + * Returns 0 or an error code. > + * > * @inode_notifysecctx: > * Notify the security module of what the security context of an inode > * should be. Initializes the incore security context managed by the > @@ -1613,6 +1630,12 @@ union security_list_options { > struct audit_context *actx); > void (*audit_rule_free)(void *lsmrule); > #endif /* CONFIG_AUDIT */ > + > +#ifdef CONFIG_USER_NS > + int (*userns_create)(struct user_namespace *ns); > + void (*userns_free)(struct user_namespace *ns); > + int (*userns_setns)(struct nsproxy *nsproxy, struct user_namespace *ns); > +#endif /* CONFIG_USER_NS */ > }; > > struct security_hook_heads { > @@ -1824,6 +1847,11 @@ struct security_hook_heads { > struct list_head audit_rule_match; > struct list_head audit_rule_free; > #endif /* CONFIG_AUDIT */ > +#ifdef CONFIG_USER_NS > + struct list_head userns_create; > + struct list_head userns_free; > + struct list_head userns_setns; > +#endif /* CONFIG_USER_NS */ > }; > > /* > diff --git a/include/linux/security.h b/include/linux/security.h > index 2f4c1f7..91ffba2 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -1584,6 +1584,29 @@ static inline void security_audit_rule_free(void *lsmrule) > #endif /* CONFIG_SECURITY */ > #endif /* CONFIG_AUDIT */ > > +#ifdef CONFIG_USER_NS > +int security_userns_create(struct user_namespace *ns); > +void security_userns_free(struct user_namespace *ns); > +int security_userns_setns(struct nsproxy *nsproxy, struct user_namespace *ns); > + > +#else > + > +static inline int security_userns_create(struct user_namespace *ns) > +{ > + return 0; > +} > + > +static inline void security_userns_free(struct user_namespace *ns) > +{ } > + > +static inline int security_userns_setns(struct nsproxy *nsproxy, > + struct user_namespace *ns) > +{ > + return 0; > +} > + > +#endif /* CONFIG_USER_NS */ > + > #ifdef CONFIG_SECURITYFS > > extern struct dentry *securityfs_create_file(const char *name, umode_t mode, > diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h > index 8297e5b..a9400cc 100644 > --- a/include/linux/user_namespace.h > +++ b/include/linux/user_namespace.h > @@ -39,6 +39,10 @@ struct user_namespace { > struct key *persistent_keyring_register; > struct rw_semaphore persistent_keyring_register_sem; > #endif > + > +#ifdef CONFIG_SECURITY > + void *security; > +#endif > }; > > extern struct user_namespace init_user_ns; > diff --git a/kernel/user.c b/kernel/user.c > index b069ccb..ce5419e 100644 > --- a/kernel/user.c > +++ b/kernel/user.c > @@ -59,6 +59,9 @@ struct user_namespace init_user_ns = { > .persistent_keyring_register_sem = > __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem), > #endif > +#ifdef CONFIG_SECURITY > + .security = NULL, > +#endif > }; > EXPORT_SYMBOL_GPL(init_user_ns); > > diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c > index 88fefa6..daef188 100644 > --- a/kernel/user_namespace.c > +++ b/kernel/user_namespace.c > @@ -22,6 +22,7 @@ > #include > #include > #include > +#include > > static struct kmem_cache *user_ns_cachep __read_mostly; > static DEFINE_MUTEX(userns_state_mutex); > @@ -109,6 +110,15 @@ int create_user_ns(struct cred *new) > > set_cred_user_ns(new, ns); > > +#ifdef CONFIG_SECURITY > + ret = security_userns_create(ns); > + if (ret) { > + ns_free_inum(&ns->ns); > + kmem_cache_free(user_ns_cachep, ns); > + return ret; > + } > +#endif > + > #ifdef CONFIG_PERSISTENT_KEYRINGS > init_rwsem(&ns->persistent_keyring_register_sem); > #endif > @@ -144,6 +154,9 @@ void free_user_ns(struct user_namespace *ns) > #ifdef CONFIG_PERSISTENT_KEYRINGS > key_put(ns->persistent_keyring_register); > #endif > +#ifdef CONFIG_SECURITY > + security_userns_free(ns); > +#endif > ns_free_inum(&ns->ns); > kmem_cache_free(user_ns_cachep, ns); > ns = parent; > @@ -970,6 +983,7 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns) > { > struct user_namespace *user_ns = to_user_ns(ns); > struct cred *cred; > + int err; > > /* Don't allow gaining capabilities by reentering > * the same user namespace. > @@ -987,6 +1001,10 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns) > if (!ns_capable(user_ns, CAP_SYS_ADMIN)) > return -EPERM; > > + err = security_userns_setns(nsproxy, user_ns); > + if (err) > + return err; > + > cred = prepare_creds(); > if (!cred) > return -ENOMEM; > diff --git a/security/security.c b/security/security.c > index 46f405c..e571127 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -25,6 +25,7 @@ > #include > #include > #include > +#include > #include > > #define MAX_LSM_EVM_XATTR 2 > @@ -1538,6 +1539,25 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, > } > #endif /* CONFIG_AUDIT */ > > +#ifdef CONFIG_USER_NS > + > +int security_userns_create(struct user_namespace *ns) > +{ > + return call_int_hook(userns_create, 0, ns); > +} > + > +void security_userns_free(struct user_namespace *ns) > +{ > + call_void_hook(userns_free, ns); > +} > + > +int security_userns_setns(struct nsproxy *nsproxy, struct user_namespace *ns) > +{ > + return call_int_hook(userns_setns, 0, nsproxy, ns); > +} > + > +#endif /* CONFIG_USER_NS */ > + > struct security_hook_heads security_hook_heads = { > .binder_set_context_mgr = > LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr), > @@ -1882,4 +1902,12 @@ struct security_hook_heads security_hook_heads = { > .audit_rule_free = > LIST_HEAD_INIT(security_hook_heads.audit_rule_free), > #endif /* CONFIG_AUDIT */ > +#ifdef CONFIG_USER_NS > + .userns_create = > + LIST_HEAD_INIT(security_hook_heads.userns_create), > + .userns_free = > + LIST_HEAD_INIT(security_hook_heads.userns_free), > + .userns_setns = > + LIST_HEAD_INIT(security_hook_heads.userns_setns), > +#endif /* CONFIG_USER_NS */ > }; From casey at schaufler-ca.com Thu Oct 29 22:49:37 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:49:37 -0700 Subject: [PATCH v4 02/11] lsm: /proc/$PID/attr/label_map file and getprocattr_seq hook In-Reply-To: <1444826525-9758-3-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-3-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A281.70006@schaufler-ca.com> On 10/14/2015 5:41 AM, Lukasz Pawelczyk wrote: > This commit adds a new proc attribute, label_map that is required by an > upcoming Smack namespace. In general it can be used to hold a map of > labels, e.g. to be used in namespaces. > > Due to the nature of this file, the standard getprocattr hook might not > be enough to handle it. The map's output can in principle be greater > than page size to which the aforementioned hook is limited. > To handle this properly a getprocattr_seq LSM hook has been added that > makes it possible to handle any chosen proc attr by seq operations. > > See the documentation in the patch below for the details about how to > use the hook. > > Signed-off-by: Lukasz Pawelczyk > Acked-by: Serge Hallyn Acked-by: Casey Schaufler > --- > fs/proc/base.c | 81 +++++++++++++++++++++++++++++++++++++++++++---- > include/linux/lsm_hooks.h | 15 +++++++++ > include/linux/security.h | 9 ++++++ > security/security.c | 8 +++++ > 4 files changed, 107 insertions(+), 6 deletions(-) > > diff --git a/fs/proc/base.c b/fs/proc/base.c > index b25eee4..9ec88b8 100644 > --- a/fs/proc/base.c > +++ b/fs/proc/base.c > @@ -2327,20 +2327,77 @@ out: > } > > #ifdef CONFIG_SECURITY > +static int proc_pid_attr_open(struct inode *inode, struct file *file) > +{ > + const char *name = file->f_path.dentry->d_name.name; > + const struct seq_operations *ops; > + struct task_struct *task; > + struct seq_file *seq; > + int ret; > + > + file->private_data = NULL; > + > + task = get_proc_task(inode); > + if (!task) > + return -ESRCH; > + > + /* don't use seq_ops if they are not provided by LSM */ > + ret = security_getprocattr_seq(task, name, &ops); > + if (ret == -EOPNOTSUPP) { > + ret = 0; > + goto put_task; > + } > + if (ret) > + goto put_task; > + > + ret = seq_open(file, ops); > + if (ret) > + goto put_task; > + > + seq = file->private_data; > + seq->private = task; > + > + return 0; > + > +put_task: > + put_task_struct(task); > + return ret; > +} > + > +static int proc_pid_attr_release(struct inode *inode, struct file *file) > +{ > + struct seq_file *seq; > + struct task_struct *task; > + > + /* don't use seq_ops if they were not provided by LSM */ > + if (file->private_data == NULL) > + return 0; > + > + seq = file->private_data; > + task = seq->private; > + put_task_struct(task); > + > + return seq_release(inode, file); > +} > + > static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, > size_t count, loff_t *ppos) > { > - struct inode * inode = file_inode(file); > + struct inode *inode = file_inode(file); > + const char *name = file->f_path.dentry->d_name.name; > char *p = NULL; > ssize_t length; > - struct task_struct *task = get_proc_task(inode); > + struct task_struct *task; > > + /* use seq_ops if they were provided by LSM */ > + if (file->private_data) > + return seq_read(file, buf, count, ppos); > + > + task = get_proc_task(inode); > if (!task) > return -ESRCH; > > - length = security_getprocattr(task, > - (char*)file->f_path.dentry->d_name.name, > - &p); > + length = security_getprocattr(task, (char *)name, &p); > put_task_struct(task); > if (length > 0) > length = simple_read_from_buffer(buf, count, ppos, p, length); > @@ -2348,6 +2405,15 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, > return length; > } > > +static loff_t proc_pid_attr_lseek(struct file *file, loff_t offset, int whence) > +{ > + /* use seq_ops if they were provided by LSM */ > + if (file->private_data) > + return seq_lseek(file, offset, whence); > + > + return generic_file_llseek(file, offset, whence); > +} > + > static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, > size_t count, loff_t *ppos) > { > @@ -2394,9 +2460,11 @@ out_no_task: > } > > static const struct file_operations proc_pid_attr_operations = { > + .open = proc_pid_attr_open, > + .release = proc_pid_attr_release, > .read = proc_pid_attr_read, > + .llseek = proc_pid_attr_lseek, > .write = proc_pid_attr_write, > - .llseek = generic_file_llseek, > }; > > static const struct pid_entry attr_dir_stuff[] = { > @@ -2406,6 +2474,7 @@ static const struct pid_entry attr_dir_stuff[] = { > REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > + REG("label_map", S_IRUGO|S_IWUGO, proc_pid_attr_operations), > }; > > static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h > index 18c9160..7049db0 100644 > --- a/include/linux/lsm_hooks.h > +++ b/include/linux/lsm_hooks.h > @@ -1208,6 +1208,18 @@ > * @name full extended attribute name to check against > * LSM as a MAC label. > * > + * @getprocattr_seq: > + * An alternative to the getprocattr, that makes it possible for an attr > + * file to be handled by seq operations. If this function returns valid > + * @ops for a specific @name, those operations will be used and > + * getprocattr will not be called. > + * A proper task for the file is then passed in seq_file->private. > + * @p a task associated with the proc file. > + * @name name of the attr file under /proc/$PID/attr/ to be handled. > + * @ops (out) seq_operations to be used for @name. > + * Return 0 if @name is to be handled by seq, EOPNOTSUPP if getprocattr() > + * should be used. Other errors will be passed to user-space. > + * > * @secid_to_secctx: > * Convert secid to security context. If secdata is NULL the length of > * the result will be returned in seclen, but no secdata will be returned. > @@ -1525,6 +1537,8 @@ union security_list_options { > > void (*d_instantiate)(struct dentry *dentry, struct inode *inode); > > + int (*getprocattr_seq)(struct task_struct *p, const char *name, > + const struct seq_operations **ops); > int (*getprocattr)(struct task_struct *p, char *name, char **value); > int (*setprocattr)(struct task_struct *p, char *name, void *value, > size_t size); > @@ -1774,6 +1788,7 @@ struct security_hook_heads { > struct list_head sem_semop; > struct list_head netlink_send; > struct list_head d_instantiate; > + struct list_head getprocattr_seq; > struct list_head getprocattr; > struct list_head setprocattr; > struct list_head ismaclabel; > diff --git a/include/linux/security.h b/include/linux/security.h > index 91ffba2..dddea2f 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -345,6 +345,8 @@ int security_sem_semctl(struct sem_array *sma, int cmd); > int security_sem_semop(struct sem_array *sma, struct sembuf *sops, > unsigned nsops, int alter); > void security_d_instantiate(struct dentry *dentry, struct inode *inode); > +int security_getprocattr_seq(struct task_struct *p, const char *name, > + const struct seq_operations **ops); > int security_getprocattr(struct task_struct *p, char *name, char **value); > int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); > int security_netlink_send(struct sock *sk, struct sk_buff *skb); > @@ -1057,6 +1059,13 @@ static inline int security_sem_semop(struct sem_array *sma, > static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) > { } > > +static inline int security_getprocattr_seq(struct task_struct *p, > + const char *name, > + const struct seq_operations **ops) > +{ > + return -EOPNOTSUPP; > +} > + > static inline int security_getprocattr(struct task_struct *p, char *name, char **value) > { > return -EINVAL; > diff --git a/security/security.c b/security/security.c > index e571127..da2bcd4 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -1121,6 +1121,12 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) > } > EXPORT_SYMBOL(security_d_instantiate); > > +int security_getprocattr_seq(struct task_struct *p, const char *name, > + const struct seq_operations **ops) > +{ > + return call_int_hook(getprocattr_seq, -EOPNOTSUPP, p, name, ops); > +} > + > int security_getprocattr(struct task_struct *p, char *name, char **value) > { > return call_int_hook(getprocattr, -EINVAL, p, name, value); > @@ -1774,6 +1780,8 @@ struct security_hook_heads security_hook_heads = { > .netlink_send = LIST_HEAD_INIT(security_hook_heads.netlink_send), > .d_instantiate = > LIST_HEAD_INIT(security_hook_heads.d_instantiate), > + .getprocattr_seq = > + LIST_HEAD_INIT(security_hook_heads.getprocattr_seq), > .getprocattr = LIST_HEAD_INIT(security_hook_heads.getprocattr), > .setprocattr = LIST_HEAD_INIT(security_hook_heads.setprocattr), > .ismaclabel = LIST_HEAD_INIT(security_hook_heads.ismaclabel), From casey at schaufler-ca.com Thu Oct 29 22:49:59 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:49:59 -0700 Subject: [PATCH v4 03/11] lsm: add file opener's cred to a setprocattr arguments In-Reply-To: <1444826525-9758-4-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-4-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A297.5050608@schaufler-ca.com> On 10/14/2015 5:41 AM, Lukasz Pawelczyk wrote: > setprocattr hook for Smack's label_map attribute needs to know the > capabilities of file opener. Add those credentials to the hook's > arguments. > > While at it add documentation on get/setprocattr hooks. > > Signed-off-by: Lukasz Pawelczyk > Acked-by: Serge Hallyn Acked-by: Casey Schaufler > --- > fs/proc/base.c | 2 +- > include/linux/lsm_hooks.h | 18 ++++++++++++++++-- > include/linux/security.h | 7 +++++-- > security/apparmor/lsm.c | 5 +++-- > security/security.c | 6 ++++-- > security/selinux/hooks.c | 2 +- > security/smack/smack_lsm.c | 4 ++-- > 7 files changed, 32 insertions(+), 12 deletions(-) > > diff --git a/fs/proc/base.c b/fs/proc/base.c > index 9ec88b8..2b38969 100644 > --- a/fs/proc/base.c > +++ b/fs/proc/base.c > @@ -2447,7 +2447,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, > if (length < 0) > goto out_free; > > - length = security_setprocattr(task, > + length = security_setprocattr(task, file->f_cred, > (char*)file->f_path.dentry->d_name.name, > (void*)page, count); > mutex_unlock(&task->signal->cred_guard_mutex); > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h > index 7049db0..4f16640 100644 > --- a/include/linux/lsm_hooks.h > +++ b/include/linux/lsm_hooks.h > @@ -1220,6 +1220,20 @@ > * Return 0 if @name is to be handled by seq, EOPNOTSUPP if getprocattr() > * should be used. Other errors will be passed to user-space. > * > + * @getprocattr: > + * Get a value of a proc security attribute in /proc/$PID/attr/. > + * @p a task associated with the proc file. > + * @name a name of the file in question. > + * @value a pointer where to return the attribute's value. > + * > + * @setprocattr: > + * Set a value of a proc security attribute in /proc/$PID/attr/. > + * @p a task associated with the proc file. > + * @f_cred credentials of a file's opener. > + * @name a name of the file in question. > + * @value a pointer where a value to set is kept. > + * @size a number of bytes to read from the @value pointer. > + * > * @secid_to_secctx: > * Convert secid to security context. If secdata is NULL the length of > * the result will be returned in seclen, but no secdata will be returned. > @@ -1540,8 +1554,8 @@ union security_list_options { > int (*getprocattr_seq)(struct task_struct *p, const char *name, > const struct seq_operations **ops); > int (*getprocattr)(struct task_struct *p, char *name, char **value); > - int (*setprocattr)(struct task_struct *p, char *name, void *value, > - size_t size); > + int (*setprocattr)(struct task_struct *p, const struct cred *f_cred, > + char *name, void *value, size_t size); > int (*ismaclabel)(const char *name); > int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); > int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); > diff --git a/include/linux/security.h b/include/linux/security.h > index dddea2f..12bd011 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -348,7 +348,8 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode); > int security_getprocattr_seq(struct task_struct *p, const char *name, > const struct seq_operations **ops); > int security_getprocattr(struct task_struct *p, char *name, char **value); > -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); > +int security_setprocattr(struct task_struct *p, const struct cred *f_cred, > + char *name, void *value, size_t size); > int security_netlink_send(struct sock *sk, struct sk_buff *skb); > int security_ismaclabel(const char *name); > int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); > @@ -1071,7 +1072,9 @@ static inline int security_getprocattr(struct task_struct *p, char *name, char * > return -EINVAL; > } > > -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) > +static inline int security_setprocattr(struct task_struct *p, > + const struct cred *f_cred, > + char *name, void *value, size_t size) > { > return -EINVAL; > } > diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c > index dec607c..1212927 100644 > --- a/security/apparmor/lsm.c > +++ b/security/apparmor/lsm.c > @@ -518,8 +518,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, > return error; > } > > -static int apparmor_setprocattr(struct task_struct *task, char *name, > - void *value, size_t size) > +static int apparmor_setprocattr(struct task_struct *task, > + const struct cred *f_cred, > + char *name, void *value, size_t size) > { > struct common_audit_data sa; > struct apparmor_audit_data aad = {0,}; > diff --git a/security/security.c b/security/security.c > index da2bcd4..abfc207 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -1132,9 +1132,11 @@ int security_getprocattr(struct task_struct *p, char *name, char **value) > return call_int_hook(getprocattr, -EINVAL, p, name, value); > } > > -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) > +int security_setprocattr(struct task_struct *p, const struct cred *f_cred, > + char *name, void *value, size_t size) > { > - return call_int_hook(setprocattr, -EINVAL, p, name, value, size); > + return call_int_hook(setprocattr, -EINVAL, p, f_cred, > + name, value, size); > } > > int security_netlink_send(struct sock *sk, struct sk_buff *skb) > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index e4369d8..470eff8 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -5603,7 +5603,7 @@ invalid: > return -EINVAL; > } > > -static int selinux_setprocattr(struct task_struct *p, > +static int selinux_setprocattr(struct task_struct *p, const struct cred *f_cred, > char *name, void *value, size_t size) > { > struct task_security_struct *tsec; > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index c2d66ca..c439370 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -3549,8 +3549,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) > * > * Returns the length of the smack label or an error code > */ > -static int smack_setprocattr(struct task_struct *p, char *name, > - void *value, size_t size) > +static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > + char *name, void *value, size_t size) > { > struct task_smack *tsp; > struct cred *new; From casey at schaufler-ca.com Thu Oct 29 22:50:18 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:50:18 -0700 Subject: [PATCH v4 04/11] lsm: inode_pre_setxattr hook In-Reply-To: <1444826525-9758-5-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-5-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A2AA.7010202@schaufler-ca.com> On 10/14/2015 5:41 AM, Lukasz Pawelczyk wrote: > Add a new LSM hook called before inode's setxattr. It is required for > LSM to be able to reliably replace the xattr's value to be set to > filesystem in __vfs_setxattr_noperm(). Useful for mapped values, like in > the upcoming Smack namespace patches. > > Signed-off-by: Lukasz Pawelczyk > Acked-by: Serge Hallyn Acked-by: Casey Schaufler > --- > fs/xattr.c | 10 ++++++++++ > include/linux/lsm_hooks.h | 9 +++++++++ > include/linux/security.h | 10 ++++++++++ > security/security.c | 12 ++++++++++++ > 4 files changed, 41 insertions(+) > > diff --git a/fs/xattr.c b/fs/xattr.c > index 072fee1..cbc8d19 100644 > --- a/fs/xattr.c > +++ b/fs/xattr.c > @@ -100,12 +100,22 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name, > if (issec) > inode->i_flags &= ~S_NOSEC; > if (inode->i_op->setxattr) { > + bool alloc = false; > + > + error = security_inode_pre_setxattr(dentry, name, &value, > + &size, flags, &alloc); > + if (error) > + return error; > + > error = inode->i_op->setxattr(dentry, name, value, size, flags); > if (!error) { > fsnotify_xattr(dentry); > security_inode_post_setxattr(dentry, name, value, > size, flags); > } > + > + if (alloc) > + kfree(value); > } else if (issec) { > const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; > error = security_inode_setsecurity(inode, suffix, value, > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h > index 4f16640..85bfdde 100644 > --- a/include/linux/lsm_hooks.h > +++ b/include/linux/lsm_hooks.h > @@ -349,6 +349,11 @@ > * Check permission before setting the extended attributes > * @value identified by @name for @dentry. > * Return 0 if permission is granted. > + * @inode_pre_setxattr: > + * Be able to do some operation before setting the @value identified > + * by @name on the filesystem. Replacing the @value and its @size is > + * possible. Useful for mapped values. Set @alloc to true if @value > + * needs to be kfreed afterwards. > * @inode_post_setxattr: > * Update inode security field after successful setxattr operation. > * @value identified by @name for @dentry. > @@ -1448,6 +1453,9 @@ union security_list_options { > int (*inode_getattr)(const struct path *path); > int (*inode_setxattr)(struct dentry *dentry, const char *name, > const void *value, size_t size, int flags); > + int (*inode_pre_setxattr)(struct dentry *dentry, const char *name, > + const void **value, size_t *size, > + int flags, bool *alloc); > void (*inode_post_setxattr)(struct dentry *dentry, const char *name, > const void *value, size_t size, > int flags); > @@ -1730,6 +1738,7 @@ struct security_hook_heads { > struct list_head inode_setattr; > struct list_head inode_getattr; > struct list_head inode_setxattr; > + struct list_head inode_pre_setxattr; > struct list_head inode_post_setxattr; > struct list_head inode_getxattr; > struct list_head inode_listxattr; > diff --git a/include/linux/security.h b/include/linux/security.h > index 12bd011..4de4865 100644 > --- a/include/linux/security.h > +++ b/include/linux/security.h > @@ -263,6 +263,9 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr); > int security_inode_getattr(const struct path *path); > int security_inode_setxattr(struct dentry *dentry, const char *name, > const void *value, size_t size, int flags); > +int security_inode_pre_setxattr(struct dentry *dentry, const char *name, > + const void **value, size_t *size, int flags, > + bool *alloc); > void security_inode_post_setxattr(struct dentry *dentry, const char *name, > const void *value, size_t size, int flags); > int security_inode_getxattr(struct dentry *dentry, const char *name); > @@ -691,6 +694,13 @@ static inline int security_inode_setxattr(struct dentry *dentry, > return cap_inode_setxattr(dentry, name, value, size, flags); > } > > +static inline int security_inode_pre_setxattr(struct dentry *dentry, > + const char *name, const void **value, > + size_t *size, int flags, bool *alloc) > +{ > + return 0; > +} > + > static inline void security_inode_post_setxattr(struct dentry *dentry, > const char *name, const void *value, size_t size, int flags) > { } > diff --git a/security/security.c b/security/security.c > index abfc207..75d46b6 100644 > --- a/security/security.c > +++ b/security/security.c > @@ -644,6 +644,16 @@ int security_inode_setxattr(struct dentry *dentry, const char *name, > return evm_inode_setxattr(dentry, name, value, size); > } > > +int security_inode_pre_setxattr(struct dentry *dentry, const char *name, > + const void **value, size_t *size, int flags, > + bool *alloc) > +{ > + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) > + return 0; > + return call_int_hook(inode_pre_setxattr, 0, dentry, name, value, size, > + flags, alloc); > +} > + > void security_inode_post_setxattr(struct dentry *dentry, const char *name, > const void *value, size_t size, int flags) > { > @@ -1662,6 +1672,8 @@ struct security_hook_heads security_hook_heads = { > LIST_HEAD_INIT(security_hook_heads.inode_getattr), > .inode_setxattr = > LIST_HEAD_INIT(security_hook_heads.inode_setxattr), > + .inode_pre_setxattr = > + LIST_HEAD_INIT(security_hook_heads.inode_pre_setxattr), > .inode_post_setxattr = > LIST_HEAD_INIT(security_hook_heads.inode_post_setxattr), > .inode_getxattr = From casey at schaufler-ca.com Thu Oct 29 22:50:38 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:50:38 -0700 Subject: [PATCH v4 05/11] smack: extend capability functions and fix 2 checks In-Reply-To: <1444826525-9758-6-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-6-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A2BE.6070905@schaufler-ca.com> On 10/14/2015 5:41 AM, Lukasz Pawelczyk wrote: > This patch extends smack capability functions to a full list to those > equivalent in the kernel > > has_ns_capability -> smack_has_ns_privilege > has_capability -> smack_has_privilege > ns_capable -> smack_ns_privileged > capable -> smack_privileged > > It also puts the smack related part to a common function: > smack_capability_allowed() > > Those functions will be needed for capability checks in the upcoming > Smack namespace patches. > > Additionally there were 2 smack capability checks that used generic > capability functions instead of specific Smack ones effectively ignoring > the onlycap rule. This has been fixed now with the introduction of those > new functions. > > This has implications on the Smack namespace as well as the additional > Smack checks in smack_capability_allowed() will be extended beyond the > onlycap rule. Not using Smack specific checks in those 2 places could > mean breaking the Smack label namespace separation. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler > Acked-by: Serge Hallyn Acked-by: Casey Schaufler > --- > security/smack/smack.h | 5 ++++ > security/smack/smack_access.c | 64 +++++++++++++++++++++++++++++++++++++++---- > security/smack/smack_lsm.c | 4 +-- > 3 files changed, 65 insertions(+), 8 deletions(-) > > diff --git a/security/smack/smack.h b/security/smack/smack.h > index fff0c61..ca8fb7c 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -300,6 +300,11 @@ int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); > struct smack_known *smk_import_entry(const char *, int); > void smk_insert_entry(struct smack_known *skp); > struct smack_known *smk_find_entry(const char *); > +int smack_has_ns_privilege(struct task_struct *task, > + struct user_namespace *user_ns, > + int cap); > +int smack_has_privilege(struct task_struct *task, int cap); > +int smack_ns_privileged(struct user_namespace *user_ns, int cap); > int smack_privileged(int cap); > > /* > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index bc1053f..72f848e 100644 > --- a/security/smack/smack_access.c > +++ b/security/smack/smack_access.c > @@ -629,14 +629,16 @@ LIST_HEAD(smack_onlycap_list); > DEFINE_MUTEX(smack_onlycap_lock); > > /* > - * Is the task privileged and allowed to be privileged > - * by the onlycap rule. > + * Internal smack capability check complimentary to the > + * set of kernel capable() and has_capability() functions > * > - * Returns 1 if the task is allowed to be privileged, 0 if it's not. > + * For a capability in smack related checks to be effective it needs to: > + * - be allowed to be privileged by the onlycap rule. > + * - be in the initial user ns > */ > -int smack_privileged(int cap) > +static int smack_capability_allowed(struct smack_known *skp, > + struct user_namespace *user_ns) > { > - struct smack_known *skp = smk_of_current(); > struct smack_onlycap *sop; > > /* > @@ -645,7 +647,7 @@ int smack_privileged(int cap) > if (unlikely(current->flags & PF_KTHREAD)) > return 1; > > - if (!capable(cap)) > + if (user_ns != &init_user_ns) > return 0; > > rcu_read_lock(); > @@ -664,3 +666,53 @@ int smack_privileged(int cap) > > return 0; > } > + > +/* > + * Is the task privileged in a namespace and allowed to be privileged > + * by additional smack rules. > + */ > +int smack_has_ns_privilege(struct task_struct *task, > + struct user_namespace *user_ns, > + int cap) > +{ > + struct smack_known *skp = smk_of_task_struct(task); > + > + if (!has_ns_capability(task, user_ns, cap)) > + return 0; > + if (smack_capability_allowed(skp, user_ns)) > + return 1; > + return 0; > +} > + > +/* > + * Is the task privileged and allowed to be privileged > + * by additional smack rules. > + */ > +int smack_has_privilege(struct task_struct *task, int cap) > +{ > + return smack_has_ns_privilege(task, &init_user_ns, cap); > +} > + > +/* > + * Is the current task privileged in a namespace and allowed to be privileged > + * by additional smack rules. > + */ > +int smack_ns_privileged(struct user_namespace *user_ns, int cap) > +{ > + struct smack_known *skp = smk_of_current(); > + > + if (!ns_capable(user_ns, cap)) > + return 0; > + if (smack_capability_allowed(skp, user_ns)) > + return 1; > + return 0; > +} > + > +/* > + * Is the current task privileged and allowed to be privileged > + * by additional smack rules. > + */ > +int smack_privileged(int cap) > +{ > + return smack_ns_privileged(&init_user_ns, cap); > +} > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index c439370..198d3d6 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -413,7 +413,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > rc = 0; > else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) > rc = -EACCES; > - else if (capable(CAP_SYS_PTRACE)) > + else if (smack_has_privilege(tracer, CAP_SYS_PTRACE)) > rc = 0; > else > rc = -EACCES; > @@ -1809,7 +1809,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, > skp = file->f_security; > rc = smk_access(skp, tkp, MAY_WRITE, NULL); > rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); > - if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) > + if (rc != 0 && smack_has_privilege(tsk, CAP_MAC_OVERRIDE)) > rc = 0; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); From casey at schaufler-ca.com Thu Oct 29 22:50:51 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:50:51 -0700 Subject: [PATCH v4 06/11] smack: don't use implicit star to display smackfs/syslog In-Reply-To: <1444826525-9758-7-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-7-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A2CB.1000702@schaufler-ca.com> On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > Smackfs/syslog is analogous to onlycap and unconfined. When not filled > they don't do anything. In such cases onlycap and unconfined displayed > nothing when read, but syslog unconditionally displayed star. This > doesn't work well with namespaces where the star could have been > unmapped. Besides the meaning of this star was different then a star > that could be written to this file. This was misleading. > > This also brings syslog read/write functions on par with onlycap and > unconfined where it is possible to reset the value to NULL as should be > possible according to comment in smackfs.c describing smack_syslog_label > variable. > > Before that the initial state was to allow (smack_syslog_label was > NULL), but after writing star to it the current had to be labeled star > as well to have an access, even thought reading the smackfs/syslog > returned the same result in both cases. > > Signed-off-by: Lukasz Pawelczyk > Acked-by: Serge Hallyn Acked-by: Casey Schaufler > --- > security/smack/smackfs.c | 42 +++++++++++++++++++++++++++--------------- > 1 file changed, 27 insertions(+), 15 deletions(-) > > diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c > index ce8d503..05e09ee2 100644 > --- a/security/smack/smackfs.c > +++ b/security/smack/smackfs.c > @@ -2634,23 +2634,20 @@ static const struct file_operations smk_change_rule_ops = { > static ssize_t smk_read_syslog(struct file *filp, char __user *buf, > size_t cn, loff_t *ppos) > { > - struct smack_known *skp; > + char *smack = ""; > ssize_t rc = -EINVAL; > int asize; > > if (*ppos != 0) > return 0; > > - if (smack_syslog_label == NULL) > - skp = &smack_known_star; > - else > - skp = smack_syslog_label; > + if (smack_syslog_label != NULL) > + smack = smack_syslog_label->smk_known; > > - asize = strlen(skp->smk_known) + 1; > + asize = strlen(smack) + 1; > > if (cn >= asize) > - rc = simple_read_from_buffer(buf, cn, ppos, skp->smk_known, > - asize); > + rc = simple_read_from_buffer(buf, cn, ppos, smack, asize); > > return rc; > } > @@ -2678,16 +2675,31 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, > if (data == NULL) > return -ENOMEM; > > - if (copy_from_user(data, buf, count) != 0) > + if (copy_from_user(data, buf, count) != 0) { > rc = -EFAULT; > - else { > - skp = smk_import_entry(data, count); > - if (IS_ERR(skp)) > - rc = PTR_ERR(skp); > - else > - smack_syslog_label = skp; > + goto freeout; > } > > + /* > + * Clear the smack_syslog_label on invalid label errors. This means > + * that we can pass a null string to unset the syslog value. > + * > + * Importing will also reject a label beginning with '-', > + * so "-syslog" will also work. > + * > + * But do so only on invalid label, not on system errors. > + */ > + skp = smk_import_entry(data, count); > + if (PTR_ERR(skp) == -EINVAL) > + skp = NULL; > + else if (IS_ERR(skp)) { > + rc = PTR_ERR(skp); > + goto freeout; > + } > + > + smack_syslog_label = skp; > + > +freeout: > kfree(data); > return rc; > } From casey at schaufler-ca.com Thu Oct 29 22:51:15 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:51:15 -0700 Subject: [PATCH v4 07/11] smack: abstraction layer for 2 common Smack operations In-Reply-To: <1444826525-9758-8-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-8-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A2E3.7050504@schaufler-ca.com> On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > This patch adds two new functions that provide an abstraction layer for > two common internal Smack operations: > > smk_find_label_name() - returns a label name (char*) from a struct > smack_known pointer > smk_get_label() - either finds or imports a label from a raw label > name (char*) and returns struct smack_known > pointer > > This patch also simplifies some pieces of code due to addition of those > 2 functions (e.g. smack_inode_post_setxattr, smk_fill_rule, > smk_write_revoke_subj). > > It is meant as a preparation for namespaces patches. Those 2 functions > will serve as entry points for namespace operations. > > This patch should not change the Smack behaviour in any way. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler Acked-by: Casey Schaufler > --- > security/smack/smack.h | 2 + > security/smack/smack_access.c | 41 ++++++++++++ > security/smack/smack_lsm.c | 78 +++++++++++----------- > security/smack/smackfs.c | 147 +++++++++++++++++++++++------------------- > 4 files changed, 166 insertions(+), 102 deletions(-) > > diff --git a/security/smack/smack.h b/security/smack/smack.h > index ca8fb7c..091efc2 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -306,6 +306,8 @@ int smack_has_ns_privilege(struct task_struct *task, > int smack_has_privilege(struct task_struct *task, int cap); > int smack_ns_privileged(struct user_namespace *user_ns, int cap); > int smack_privileged(int cap); > +char *smk_find_label_name(struct smack_known *skp); > +struct smack_known *smk_get_label(const char *string, int len, bool import); > > /* > * Shared data. > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index 72f848e..131c742 100644 > --- a/security/smack/smack_access.c > +++ b/security/smack/smack_access.c > @@ -716,3 +716,44 @@ int smack_privileged(int cap) > { > return smack_ns_privileged(&init_user_ns, cap); > } > + > +/** > + * smk_find_label_name - A helper to get a string value of a label > + * @skp: a label we want a string value from > + * > + * Returns a pointer to a label name or NULL if label name not found. > + */ > +char *smk_find_label_name(struct smack_known *skp) > +{ > + return skp->smk_known; > +} > + > +/** > + * smk_get_label - A helper to get the smack_known value from a string using > + * either import or find functions if it already exists > + * @string: a name of a label we look for or want to import > + * @len: the string size, or zero if it is NULL terminated > + * @import: whether we should import the label if not found > + * > + * Returns a smack_known label that is either imported or found. > + * NULL if label not found (only when import == false). > + * Error code otherwise. > + */ > +struct smack_known *smk_get_label(const char *string, int len, bool import) > +{ > + struct smack_known *skp; > + char *cp; > + > + if (import) { > + skp = smk_import_entry(string, len); > + } else { > + cp = smk_parse_smack(string, len); > + if (IS_ERR(cp)) > + return ERR_CAST(cp); > + > + skp = smk_find_entry(cp); > + kfree(cp); > + } > + > + return skp; > +} > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 198d3d6..7303c37 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -746,31 +746,31 @@ static int smack_set_mnt_opts(struct super_block *sb, > for (i = 0; i < num_opts; i++) { > switch (opts->mnt_opts_flags[i]) { > case FSDEFAULT_MNT: > - skp = smk_import_entry(opts->mnt_opts[i], 0); > + skp = smk_get_label(opts->mnt_opts[i], 0, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_default = skp; > break; > case FSFLOOR_MNT: > - skp = smk_import_entry(opts->mnt_opts[i], 0); > + skp = smk_get_label(opts->mnt_opts[i], 0, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_floor = skp; > break; > case FSHAT_MNT: > - skp = smk_import_entry(opts->mnt_opts[i], 0); > + skp = smk_get_label(opts->mnt_opts[i], 0, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_hat = skp; > break; > case FSROOT_MNT: > - skp = smk_import_entry(opts->mnt_opts[i], 0); > + skp = smk_get_label(opts->mnt_opts[i], 0, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_root = skp; > break; > case FSTRANS_MNT: > - skp = smk_import_entry(opts->mnt_opts[i], 0); > + skp = smk_get_label(opts->mnt_opts[i], 0, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_root = skp; > @@ -1288,7 +1288,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, > rc = -EPERM; > > if (rc == 0 && check_import) { > - skp = size ? smk_import_entry(value, size) : NULL; > + skp = size ? smk_get_label(value, size, true) : NULL; > if (IS_ERR(skp)) > rc = PTR_ERR(skp); > else if (skp == NULL || (check_star && > @@ -1322,6 +1322,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, > const void *value, size_t size, int flags) > { > struct smack_known *skp; > + struct smack_known **skpp = NULL; > struct inode_smack *isp = d_backing_inode(dentry)->i_security; > > if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { > @@ -1329,27 +1330,21 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, > return; > } > > - if (strcmp(name, XATTR_NAME_SMACK) == 0) { > - skp = smk_import_entry(value, size); > - if (!IS_ERR(skp)) > - isp->smk_inode = skp; > - else > - isp->smk_inode = &smack_known_invalid; > - } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { > - skp = smk_import_entry(value, size); > - if (!IS_ERR(skp)) > - isp->smk_task = skp; > - else > - isp->smk_task = &smack_known_invalid; > - } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { > - skp = smk_import_entry(value, size); > + if (strcmp(name, XATTR_NAME_SMACK) == 0) > + skpp = &isp->smk_inode; > + else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) > + skpp = &isp->smk_task; > + else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) > + skpp = &isp->smk_mmap; > + > + if (skpp) { > + skp = smk_get_label(value, size, true); > + > if (!IS_ERR(skp)) > - isp->smk_mmap = skp; > + *skpp = skp; > else > - isp->smk_mmap = &smack_known_invalid; > + *skpp = &smack_known_invalid; > } > - > - return; > } > > /** > @@ -1443,15 +1438,17 @@ static int smack_inode_getsecurity(const struct inode *inode, > struct socket *sock; > struct super_block *sbp; > struct inode *ip = (struct inode *)inode; > - struct smack_known *isp; > - int ilen; > + struct smack_known *isp = NULL; > int rc = 0; > > - if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { > + if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) > isp = smk_of_inode(inode); > - ilen = strlen(isp->smk_known); > - *buffer = isp->smk_known; > - return ilen; > + > + if (isp) { > + *buffer = smk_find_label_name(isp); > + if (*buffer == NULL) > + *buffer = smack_known_huh.smk_known; > + return strlen(*buffer); > } > > /* > @@ -1474,10 +1471,11 @@ static int smack_inode_getsecurity(const struct inode *inode, > else > return -EOPNOTSUPP; > > - ilen = strlen(isp->smk_known); > if (rc == 0) { > - *buffer = isp->smk_known; > - rc = ilen; > + *buffer = smk_find_label_name(isp); > + if (*buffer == NULL) > + *buffer = smack_known_huh.smk_known; > + rc = strlen(*buffer); > } > > return rc; > @@ -2658,7 +2656,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, > if (value == NULL || size > SMK_LONGLABEL || size == 0) > return -EINVAL; > > - skp = smk_import_entry(value, size); > + skp = smk_get_label(value, size, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > > @@ -3528,7 +3526,10 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) > if (strcmp(name, "current") != 0) > return -EINVAL; > > - cp = kstrdup(skp->smk_known, GFP_KERNEL); > + cp = smk_find_label_name(skp); > + if (cp == NULL) > + cp = smack_known_huh.smk_known; > + cp = kstrdup(cp, GFP_KERNEL); > if (cp == NULL) > return -ENOMEM; > > @@ -3572,7 +3573,7 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > if (strcmp(name, "current") != 0) > return -EINVAL; > > - skp = smk_import_entry(value, size); > + skp = smk_get_label(value, size, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > > @@ -4311,7 +4312,10 @@ static int smack_key_getsecurity(struct key *key, char **_buffer) > return 0; > } > > - copy = kstrdup(skp->smk_known, GFP_KERNEL); > + copy = smk_find_label_name(skp); > + if (copy == NULL) > + copy = smack_known_huh.smk_known; > + copy = kstrdup(copy, GFP_KERNEL); > if (copy == NULL) > return -ENOMEM; > length = strlen(copy) + 1; > diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c > index 05e09ee2..e5fb555 100644 > --- a/security/smack/smackfs.c > +++ b/security/smack/smackfs.c > @@ -340,36 +340,17 @@ static int smk_fill_rule(const char *subject, const char *object, > struct smack_parsed_rule *rule, int import, > int len) > { > - const char *cp; > - struct smack_known *skp; > - > - if (import) { > - rule->smk_subject = smk_import_entry(subject, len); > - if (IS_ERR(rule->smk_subject)) > - return PTR_ERR(rule->smk_subject); > - > - rule->smk_object = smk_import_entry(object, len); > - if (IS_ERR(rule->smk_object)) > - return PTR_ERR(rule->smk_object); > - } else { > - cp = smk_parse_smack(subject, len); > - if (IS_ERR(cp)) > - return PTR_ERR(cp); > - skp = smk_find_entry(cp); > - kfree(cp); > - if (skp == NULL) > - return -ENOENT; > - rule->smk_subject = skp; > - > - cp = smk_parse_smack(object, len); > - if (IS_ERR(cp)) > - return PTR_ERR(cp); > - skp = smk_find_entry(cp); > - kfree(cp); > - if (skp == NULL) > - return -ENOENT; > - rule->smk_object = skp; > - } > + rule->smk_subject = smk_get_label(subject, len, import); > + if (IS_ERR(rule->smk_subject)) > + return PTR_ERR(rule->smk_subject); > + if (rule->smk_subject == NULL) > + return -ENOENT; > + > + rule->smk_object = smk_get_label(object, len, import); > + if (IS_ERR(rule->smk_object)) > + return PTR_ERR(rule->smk_object); > + if (rule->smk_object == NULL) > + return -ENOENT; > > rule->smk_access1 = smk_perm_from_str(access1); > if (access2) > @@ -592,6 +573,9 @@ static void smk_seq_stop(struct seq_file *s, void *v) > > static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) > { > + char *sbj; > + char *obj; > + > /* > * Don't show any rules with label names too long for > * interface file (/smack/load or /smack/load2) > @@ -605,9 +589,13 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) > if (srp->smk_access == 0) > return; > > - seq_printf(s, "%s %s", > - srp->smk_subject->smk_known, > - srp->smk_object->smk_known); > + sbj = smk_find_label_name(srp->smk_subject); > + obj = smk_find_label_name(srp->smk_object); > + > + if (sbj == NULL || obj == NULL) > + return; > + > + seq_printf(s, "%s %s", sbj, obj); > > seq_putc(s, ' '); > > @@ -798,6 +786,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) > list_entry_rcu(list, struct smack_known, list); > struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; > char sep = '/'; > + char *cp; > int i; > > /* > @@ -811,7 +800,11 @@ static int cipso_seq_show(struct seq_file *s, void *v) > if (strlen(skp->smk_known) >= SMK_LABELLEN) > return 0; > > - seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); > + cp = smk_find_label_name(skp); > + if (cp == NULL) > + return 0; > + > + seq_printf(s, "%s %3d", cp, skp->smk_netlabel.attr.mls.lvl); > > for (i = netlbl_catmap_walk(cmp, 0); i >= 0; > i = netlbl_catmap_walk(cmp, i + 1)) { > @@ -900,7 +893,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, > */ > mutex_lock(&smack_cipso_lock); > > - skp = smk_import_entry(rule, 0); > + skp = smk_get_label(rule, 0, true); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto out; > @@ -989,9 +982,14 @@ static int cipso2_seq_show(struct seq_file *s, void *v) > list_entry_rcu(list, struct smack_known, list); > struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; > char sep = '/'; > + char *cp; > int i; > > - seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); > + cp = smk_find_label_name(skp); > + if (cp == NULL) > + return 0; > + > + seq_printf(s, "%s %3d", cp, skp->smk_netlabel.attr.mls.lvl); > > for (i = netlbl_catmap_walk(cmp, 0); i >= 0; > i = netlbl_catmap_walk(cmp, i + 1)) { > @@ -1072,8 +1070,12 @@ static int net4addr_seq_show(struct seq_file *s, void *v) > list_entry_rcu(list, struct smk_net4addr, list); > char *kp = SMACK_CIPSO_OPTION; > > - if (skp->smk_label != NULL) > - kp = skp->smk_label->smk_known; > + if (skp->smk_label != NULL) { > + kp = smk_find_label_name(skp->smk_label); > + if (kp == NULL) > + kp = smack_known_huh.smk_known; > + } > + > seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr, > skp->smk_masks, kp); > > @@ -1224,7 +1226,7 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, > * If smack begins with '-', it is an option, don't import it > */ > if (smack[0] != '-') { > - skp = smk_import_entry(smack, 0); > + skp = smk_get_label(smack, 0, true); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto free_out; > @@ -1343,10 +1345,16 @@ static int net6addr_seq_show(struct seq_file *s, void *v) > struct list_head *list = v; > struct smk_net6addr *skp = > list_entry(list, struct smk_net6addr, list); > + char *kp; > > - if (skp->smk_label != NULL) > - seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks, > - skp->smk_label->smk_known); > + if (skp->smk_label != NULL) { > + kp = smk_find_label_name(skp->smk_label); > + if (kp == NULL) > + kp = smack_known_huh.smk_known; > + > + seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, > + skp->smk_masks, kp); > + } > > return 0; > } > @@ -1500,7 +1508,7 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, > * If smack begins with '-', it is an option, don't import it > */ > if (smack[0] != '-') { > - skp = smk_import_entry(smack, 0); > + skp = smk_get_label(smack, 0, true); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto free_out; > @@ -1820,6 +1828,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, > size_t cn, loff_t *ppos) > { > ssize_t rc; > + char *cp; > int asize; > > if (*ppos != 0) > @@ -1830,12 +1839,14 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, > */ > mutex_lock(&smack_ambient_lock); > > - asize = strlen(smack_net_ambient->smk_known) + 1; > + cp = smk_find_label_name(smack_net_ambient); > + if (cp == NULL) > + cp = smack_known_huh.smk_known; > + > + asize = strlen(cp) + 1; > > if (cn >= asize) > - rc = simple_read_from_buffer(buf, cn, ppos, > - smack_net_ambient->smk_known, > - asize); > + rc = simple_read_from_buffer(buf, cn, ppos, cp, asize); > else > rc = -EINVAL; > > @@ -1873,7 +1884,7 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, > goto out; > } > > - skp = smk_import_entry(data, count); > + skp = smk_get_label(data, count, true); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto out; > @@ -1913,11 +1924,16 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) > > static int onlycap_seq_show(struct seq_file *s, void *v) > { > + char *smack; > struct list_head *list = v; > struct smack_onlycap *sop = > list_entry_rcu(list, struct smack_onlycap, list); > > - seq_puts(s, sop->smk_label->smk_known); > + smack = smk_find_label_name(sop->smk_label); > + if (smack == NULL) > + smack = smack_known_huh.smk_known; > + > + seq_puts(s, smack); > seq_putc(s, ' '); > > return 0; > @@ -2011,7 +2027,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, > if (!*tok) > continue; > > - skp = smk_import_entry(tok, 0); > + skp = smk_get_label(tok, 0, true); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > break; > @@ -2081,8 +2097,11 @@ static ssize_t smk_read_unconfined(struct file *filp, char __user *buf, > if (*ppos != 0) > return 0; > > - if (smack_unconfined != NULL) > - smack = smack_unconfined->smk_known; > + if (smack_unconfined != NULL) { > + smack = smk_find_label_name(smack_unconfined); > + if (smack == NULL) > + smack = smack_known_huh.smk_known; > + } > > asize = strlen(smack) + 1; > > @@ -2129,7 +2148,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, > * > * But do so only on invalid label, not on system errors. > */ > - skp = smk_import_entry(data, count); > + skp = smk_get_label(data, count, true); > if (PTR_ERR(skp) == -EINVAL) > skp = NULL; > else if (IS_ERR(skp)) { > @@ -2526,7 +2545,6 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, > size_t count, loff_t *ppos) > { > char *data; > - const char *cp; > struct smack_known *skp; > struct smack_rule *sp; > struct list_head *rule_list; > @@ -2551,15 +2569,13 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, > goto out_data; > } > > - cp = smk_parse_smack(data, count); > - if (IS_ERR(cp)) { > - rc = PTR_ERR(cp); > + skp = smk_get_label(data, count, false); > + if (IS_ERR(skp)) { > + rc = PTR_ERR(skp); > goto out_data; > } > - > - skp = smk_find_entry(cp); > if (skp == NULL) > - goto out_cp; > + goto out_data; > > rule_list = &skp->smk_rules; > rule_lock = &skp->smk_rules_lock; > @@ -2571,8 +2587,6 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, > > mutex_unlock(rule_lock); > > -out_cp: > - kfree(cp); > out_data: > kfree(data); > > @@ -2641,8 +2655,11 @@ static ssize_t smk_read_syslog(struct file *filp, char __user *buf, > if (*ppos != 0) > return 0; > > - if (smack_syslog_label != NULL) > - smack = smack_syslog_label->smk_known; > + if (smack_syslog_label != NULL) { > + smack = smk_find_label_name(smack_syslog_label); > + if (smack == NULL) > + smack = smack_known_huh.smk_known; > + } > > asize = strlen(smack) + 1; > > @@ -2689,7 +2706,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, > * > * But do so only on invalid label, not on system errors. > */ > - skp = smk_import_entry(data, count); > + skp = smk_get_label(data, count, true); > if (PTR_ERR(skp) == -EINVAL) > skp = NULL; > else if (IS_ERR(skp)) { From casey at schaufler-ca.com Thu Oct 29 22:51:30 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:51:30 -0700 Subject: [PATCH v4 08/11] smack: misc cleanups in preparation for a namespace patch In-Reply-To: <1444826525-9758-9-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-9-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A2F2.1090000@schaufler-ca.com> On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > This patch does some small miscellaneous cleanups and additions that > should not change the code behaviour in any way. Its only purpose is to > shape the code in a way that the smack namespace patches would be > smaller and easier to understand. > > Changes: > - four small helper functions added > - minor code reformatting in several places for readability > - unnecessarily increasing string size has been fixed > > This patch should not change the behaviour of the Smack in any way. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler Acked-by: Casey Schaufler > --- > security/smack/smack.h | 47 ++++++++++++++++++++++++++++++++++- > security/smack/smack_access.c | 18 +++++++++----- > security/smack/smack_lsm.c | 58 ++++++++++++++++--------------------------- > security/smack/smackfs.c | 4 +-- > 4 files changed, 81 insertions(+), 46 deletions(-) > > diff --git a/security/smack/smack.h b/security/smack/smack.h > index 091efc2..98bb676 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -291,7 +291,7 @@ struct smk_audit_info { > int smk_access_entry(char *, char *, struct list_head *); > int smk_access(struct smack_known *, struct smack_known *, > int, struct smk_audit_info *); > -int smk_tskacc(struct task_smack *, struct smack_known *, > +int smk_tskacc(struct task_struct *, struct smack_known *, > u32, struct smk_audit_info *); > int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); > struct smack_known *smack_from_secid(const u32); > @@ -348,6 +348,7 @@ extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; > static inline int smk_inode_transmutable(const struct inode *isp) > { > struct inode_smack *sip = isp->i_security; > + > return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0; > } > > @@ -357,10 +358,31 @@ static inline int smk_inode_transmutable(const struct inode *isp) > static inline struct smack_known *smk_of_inode(const struct inode *isp) > { > struct inode_smack *sip = isp->i_security; > + > return sip->smk_inode; > } > > /* > + * Present a pointer to the smack label entry in an inode blob for an exec. > + */ > +static inline struct smack_known *smk_of_exec(const struct inode *isp) > +{ > + struct inode_smack *sip = isp->i_security; > + > + return sip->smk_task; > +} > + > +/* > + * Present a pointer to the smack label entry in an inode blob for an mmap. > + */ > +static inline struct smack_known *smk_of_mmap(const struct inode *isp) > +{ > + struct inode_smack *sip = isp->i_security; > + > + return sip->smk_mmap; > +} > + > +/* > * Present a pointer to the smack label entry in an task blob. > */ > static inline struct smack_known *smk_of_task(const struct task_smack *tsp) > @@ -395,6 +417,29 @@ static inline struct smack_known *smk_of_current(void) > } > > /* > + * Present a pointer to the user namespace entry in an task blob. > + */ > +static inline > +struct user_namespace *ns_of_task_struct(const struct task_struct *t) > +{ > + struct user_namespace *ns; > + > + rcu_read_lock(); > + ns = __task_cred(t)->user_ns; > + rcu_read_unlock(); > + > + return ns; > +} > + > +/* > + * Present a pointer to the user namespace entry in the current task blob. > + */ > +static inline struct user_namespace *ns_of_current(void) > +{ > + return current_user_ns(); > +} > + > +/* > * logging functions > */ > #define SMACK_AUDIT_DENIED 0x1 > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index 131c742..750aa9c 100644 > --- a/security/smack/smack_access.c > +++ b/security/smack/smack_access.c > @@ -167,6 +167,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, > if (subject == &smack_known_hat) > goto out_audit; > } > + > /* > * Beyond here an explicit relationship is required. > * If the requested access is contained in the available > @@ -183,6 +184,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, > rc = -EACCES; > goto out_audit; > } > + > #ifdef CONFIG_SECURITY_SMACK_BRINGUP > /* > * Return a positive value if using bringup mode. > @@ -225,10 +227,10 @@ out_audit: > * non zero otherwise. It allows that the task may have the capability > * to override the rules. > */ > -int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known, > +int smk_tskacc(struct task_struct *task, struct smack_known *obj_known, > u32 mode, struct smk_audit_info *a) > { > - struct smack_known *sbj_known = smk_of_task(tsp); > + struct smack_known *sbj_known = smk_of_task_struct(task); > int may; > int rc; > > @@ -237,13 +239,19 @@ int smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known, > */ > rc = smk_access(sbj_known, obj_known, mode, NULL); > if (rc >= 0) { > + struct task_smack *tsp; > + > /* > * If there is an entry in the task's rule list > * it can further restrict access. > */ > + rcu_read_lock(); > + tsp = __task_cred(task)->security; > may = smk_access_entry(sbj_known->smk_known, > obj_known->smk_known, > &tsp->smk_rules); > + rcu_read_unlock(); > + > if (may < 0) > goto out_audit; > if ((mode & may) == mode) > @@ -280,9 +288,7 @@ out_audit: > int smk_curacc(struct smack_known *obj_known, > u32 mode, struct smk_audit_info *a) > { > - struct task_smack *tsp = current_security(); > - > - return smk_tskacc(tsp, obj_known, mode, a); > + return smk_tskacc(current, obj_known, mode, a); > } > > #ifdef CONFIG_AUDIT > @@ -456,7 +462,7 @@ char *smk_parse_smack(const char *string, int len) > int i; > > if (len <= 0) > - len = strlen(string) + 1; > + len = strlen(string); > > /* > * Reserve a leading '-' as an indicator that > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 7303c37..5a59836 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -393,8 +393,6 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > { > int rc; > struct smk_audit_info ad, *saip = NULL; > - struct task_smack *tsp; > - struct smack_known *tracer_known; > > if ((mode & PTRACE_MODE_NOAUDIT) == 0) { > smk_ad_init(&ad, func, LSM_AUDIT_DATA_TASK); > @@ -402,13 +400,12 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > saip = &ad; > } > > - rcu_read_lock(); > - tsp = __task_cred(tracer)->security; > - tracer_known = smk_of_task(tsp); > > if ((mode & PTRACE_MODE_ATTACH) && > (smack_ptrace_rule == SMACK_PTRACE_EXACT || > smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { > + struct smack_known *tracer_known = smk_of_task_struct(tracer); > + > if (tracer_known->smk_known == tracee_known->smk_known) > rc = 0; > else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) > @@ -416,22 +413,18 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > else if (smack_has_privilege(tracer, CAP_SYS_PTRACE)) > rc = 0; > else > - rc = -EACCES; > + rc = -EPERM; > > if (saip) > smack_log(tracer_known->smk_known, > tracee_known->smk_known, > 0, rc, saip); > > - rcu_read_unlock(); > return rc; > } > > /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ > - rc = smk_tskacc(tsp, tracee_known, smk_ptrace_mode(mode), saip); > - > - rcu_read_unlock(); > - return rc; > + return smk_tskacc(tracer, tracee_known, smk_ptrace_mode(mode), saip); > } > > /* > @@ -450,9 +443,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > */ > static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) > { > - struct smack_known *skp; > - > - skp = smk_of_task_struct(ctp); > + struct smack_known *skp = smk_of_task_struct(ctp); > > return smk_ptrace_rule_check(current, skp, mode, __func__); > } > @@ -467,13 +458,9 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) > */ > static int smack_ptrace_traceme(struct task_struct *ptp) > { > - int rc; > - struct smack_known *skp; > - > - skp = smk_of_task(current_security()); > + struct smack_known *skp = smk_of_current(); > > - rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); > - return rc; > + return smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); > } > > /** > @@ -1692,13 +1679,14 @@ static int smack_mmap_file(struct file *file, > if (file == NULL) > return 0; > > + tsp = current_security(); > + skp = smk_of_task(tsp); > isp = file_inode(file)->i_security; > - if (isp->smk_mmap == NULL) > - return 0; > mkp = isp->smk_mmap; > > - tsp = current_security(); > - skp = smk_of_current(); > + if (mkp == NULL) > + return 0; > + > rc = 0; > > rcu_read_lock(); > @@ -3606,11 +3594,13 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > static int smack_unix_stream_connect(struct sock *sock, > struct sock *other, struct sock *newsk) > { > - struct smack_known *skp; > - struct smack_known *okp; > struct socket_smack *ssp = sock->sk_security; > struct socket_smack *osp = other->sk_security; > struct socket_smack *nsp = newsk->sk_security; > + struct smack_known *skp_out = ssp->smk_out; > + struct smack_known *okp_out = osp->smk_out; > + struct smack_known *skp_in = ssp->smk_in; > + struct smack_known *okp_in = osp->smk_in; > struct smk_audit_info ad; > int rc = 0; > #ifdef CONFIG_AUDIT > @@ -3618,19 +3608,15 @@ static int smack_unix_stream_connect(struct sock *sock, > #endif > > if (!smack_privileged(CAP_MAC_OVERRIDE)) { > - skp = ssp->smk_out; > - okp = osp->smk_in; > #ifdef CONFIG_AUDIT > smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); > smk_ad_setfield_u_net_sk(&ad, other); > #endif > - rc = smk_access(skp, okp, MAY_WRITE, &ad); > - rc = smk_bu_note("UDS connect", skp, okp, MAY_WRITE, rc); > + rc = smk_access(skp_out, okp_in, MAY_WRITE, &ad); > + rc = smk_bu_note("UDS connect", skp_out, okp_in, MAY_WRITE, rc); > if (rc == 0) { > - okp = osp->smk_out; > - skp = ssp->smk_in; > - rc = smk_access(okp, skp, MAY_WRITE, &ad); > - rc = smk_bu_note("UDS connect", okp, skp, > + rc = smk_access(okp_out, skp_in, MAY_WRITE, &ad); > + rc = smk_bu_note("UDS connect", okp_out, skp_in, > MAY_WRITE, rc); > } > } > @@ -3639,8 +3625,8 @@ static int smack_unix_stream_connect(struct sock *sock, > * Cross reference the peer labels for SO_PEERSEC. > */ > if (rc == 0) { > - nsp->smk_packet = ssp->smk_out; > - ssp->smk_packet = osp->smk_out; > + nsp->smk_packet = skp_out; > + ssp->smk_packet = okp_out; > } > > return rc; > diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c > index e5fb555..3149ec0 100644 > --- a/security/smack/smackfs.c > +++ b/security/smack/smackfs.c > @@ -1827,7 +1827,7 @@ static const struct file_operations smk_mapped_ops = { > static ssize_t smk_read_ambient(struct file *filp, char __user *buf, > size_t cn, loff_t *ppos) > { > - ssize_t rc; > + ssize_t rc = -EINVAL; > char *cp; > int asize; > > @@ -1847,8 +1847,6 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, > > if (cn >= asize) > rc = simple_read_from_buffer(buf, cn, ppos, cp, asize); > - else > - rc = -EINVAL; > > mutex_unlock(&smack_ambient_lock); > From casey at schaufler-ca.com Thu Oct 29 22:51:55 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:51:55 -0700 Subject: [PATCH v4 09/11] smack: namespace groundwork In-Reply-To: <1444826525-9758-10-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-10-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A30B.9030703@schaufler-ca.com> On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > This commit introduces several changes to Smack to prepare it for > namespace implementation. All the changes are related to namespaces. > > Overview of the changes: > - Adds required data structures for mapped labels and functions to > operate on them. > - Implements the proc interface /proc/$PID/attr/label_map that can be > used for remapping of labels for a specific namespace. Also for > checking the map. > - Modifies handling of special built-in labels. Detects them on import > and assigns the same char* pointer regardless whether it's used in a > normal or a mapped label. This way we can always compare them by == > instead of strcmp(). > - Adds User namespace hooks implementation > > This patch introduces both internal and user-space visible APIs to > handle namespaced labels and Smack namespaces but the behaviour of Smack > should not be changed. The APIs are there, but they have no impact yet. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler Acked-by: Casey Schaufler > --- > security/smack/Kconfig | 10 ++ > security/smack/Makefile | 1 + > security/smack/smack.h | 45 ++++- > security/smack/smack_access.c | 47 ++++- > security/smack/smack_lsm.c | 134 +++++++++++++- > security/smack/smack_ns.c | 404 ++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 626 insertions(+), 15 deletions(-) > create mode 100644 security/smack/smack_ns.c > > diff --git a/security/smack/Kconfig b/security/smack/Kconfig > index 271adae..b19a7fb 100644 > --- a/security/smack/Kconfig > +++ b/security/smack/Kconfig > @@ -40,3 +40,13 @@ config SECURITY_SMACK_NETFILTER > This enables security marking of network packets using > Smack labels. > If you are unsure how to answer this question, answer N. > + > +config SECURITY_SMACK_NS > + bool "Smack namespace" > + depends on SECURITY_SMACK > + depends on USER_NS > + help > + This enables Smack namespace that makes it possible to map > + specific labels within user namespace (analogously to mapping > + UIDs) and to gain MAC capabilities over them. > + If you are unsure how to answer this question, answer N. > diff --git a/security/smack/Makefile b/security/smack/Makefile > index ee2ebd5..5faebd7 100644 > --- a/security/smack/Makefile > +++ b/security/smack/Makefile > @@ -6,3 +6,4 @@ obj-$(CONFIG_SECURITY_SMACK) := smack.o > > smack-y := smack_lsm.o smack_access.o smackfs.o > smack-$(CONFIG_SECURITY_SMACK_NETFILTER) += smack_netfilter.o > +smack-$(CONFIG_SECURITY_SMACK_NS) += smack_ns.o > diff --git a/security/smack/smack.h b/security/smack/smack.h > index 98bb676..4b7489f 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -24,6 +24,7 @@ > #include > #include > #include > +#include > > /* > * Use IPv6 port labeling if IPv6 is enabled and secmarks > @@ -74,8 +75,36 @@ struct smack_known { > struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ > struct list_head smk_rules; /* access rules */ > struct mutex smk_rules_lock; /* lock for rules */ > +#ifdef CONFIG_SECURITY_SMACK_NS > + struct list_head smk_mapped; /* namespaced labels */ > + struct mutex smk_mapped_lock; > +#endif /* CONFIG_SECURITY_SMACK_NS */ > }; > > +#ifdef CONFIG_SECURITY_SMACK_NS > + > +/* > + * User namespace security pointer content. > + */ > +struct smack_ns { > + struct list_head smk_mapped; /* namespaced labels */ > + struct mutex smk_mapped_lock; > +}; > + > +/* > + * A single entry for a namespaced/mapped label. > + */ > +struct smack_known_ns { > + struct list_head smk_list_known; > + struct list_head smk_list_ns; > + struct user_namespace *smk_ns; > + char *smk_mapped; > + struct smack_known *smk_unmapped; > + bool smk_allocated; > +}; > + > +#endif /* CONFIG_SECURITY_SMACK_NS */ > + > /* > * Maximum number of bytes for the levels in a CIPSO IP option. > * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is > @@ -295,7 +324,7 @@ int smk_tskacc(struct task_struct *, struct smack_known *, > u32, struct smk_audit_info *); > int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); > struct smack_known *smack_from_secid(const u32); > -char *smk_parse_smack(const char *string, int len); > +char *smk_parse_smack(const char *string, int len, bool *allocated); > int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); > struct smack_known *smk_import_entry(const char *, int); > void smk_insert_entry(struct smack_known *skp); > @@ -310,6 +339,20 @@ char *smk_find_label_name(struct smack_known *skp); > struct smack_known *smk_get_label(const char *string, int len, bool import); > > /* > + * These functions are in smack_ns.c > + */ > +#ifdef CONFIG_SECURITY_SMACK_NS > +struct user_namespace *smk_find_mapped_ns(struct user_namespace *ns); > +struct smack_known_ns *smk_find_mapped(struct smack_known *skp, > + struct user_namespace *ns); > +struct smack_known *smk_find_unmapped(const char *string, int len, > + struct user_namespace *ns); > +extern const struct seq_operations proc_label_map_seq_operations; > +ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred, > + void *value, size_t size); > +#endif /* CONFIG_SECURITY_SMACK_NS */ > + > +/* > * Shared data. > */ > extern int smack_enabled; > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index 750aa9c..17b7e2c 100644 > --- a/security/smack/smack_access.c > +++ b/security/smack/smack_access.c > @@ -452,13 +452,16 @@ struct smack_known *smk_find_entry(const char *string) > /** > * smk_parse_smack - parse smack label from a text string > * @string: a text string that might contain a Smack label > - * @len: the maximum size, or zero if it is NULL terminated. > + * @len: the maximum size, or zero if it is NULL terminated > + * @allocated: (out) indicates whether the return string has been > + * allocated and has to be freed with kfree() later > + * (built-in labels returned are not allocated) > * > * Returns a pointer to the clean label or an error code. > */ > -char *smk_parse_smack(const char *string, int len) > +char *smk_parse_smack(const char *string, int len, bool *allocated) > { > - char *smack; > + char *smack = NULL; > int i; > > if (len <= 0) > @@ -480,11 +483,33 @@ char *smk_parse_smack(const char *string, int len) > if (i == 0 || i >= SMK_LONGLABEL) > return ERR_PTR(-EINVAL); > > + /* > + * Look for special labels. This way we guarantee that we can compare > + * special labels in mapped entries by ==, without strcmp(). > + */ > + if (len == 1 && !strcmp(string, smack_known_huh.smk_known)) > + smack = smack_known_huh.smk_known; > + else if (len == 1 && !strcmp(string, smack_known_hat.smk_known)) > + smack = smack_known_hat.smk_known; > + else if (len == 1 && !strcmp(string, smack_known_star.smk_known)) > + smack = smack_known_star.smk_known; > + else if (len == 1 && !strcmp(string, smack_known_floor.smk_known)) > + smack = smack_known_floor.smk_known; > + else if (len == 1 && !strcmp(string, smack_known_web.smk_known)) > + smack = smack_known_web.smk_known; > + > + if (smack) { > + *allocated = false; > + > + return smack; > + } > + > smack = kzalloc(i + 1, GFP_KERNEL); > if (smack == NULL) > return ERR_PTR(-ENOMEM); > > strncpy(smack, string, i); > + *allocated = true; > > return smack; > } > @@ -540,8 +565,9 @@ struct smack_known *smk_import_entry(const char *string, int len) > char *smack; > int slen; > int rc; > + bool allocated; > > - smack = smk_parse_smack(string, len); > + smack = smk_parse_smack(string, len, &allocated); > if (IS_ERR(smack)) > return ERR_CAST(smack); > > @@ -577,6 +603,10 @@ struct smack_known *smk_import_entry(const char *string, int len) > if (rc >= 0) { > INIT_LIST_HEAD(&skp->smk_rules); > mutex_init(&skp->smk_rules_lock); > +#ifdef CONFIG_SECURITY_SMACK_NS > + INIT_LIST_HEAD(&skp->smk_mapped); > + mutex_init(&skp->smk_mapped_lock); > +#endif /* CONFIG_SECURITY_SMACK_NS */ > /* > * Make sure that the entry is actually > * filled before putting it on the list. > @@ -590,7 +620,8 @@ struct smack_known *smk_import_entry(const char *string, int len) > kfree(skp); > skp = ERR_PTR(rc); > freeout: > - kfree(smack); > + if (allocated) > + kfree(smack); > unlockout: > mutex_unlock(&smack_known_lock); > > @@ -748,17 +779,19 @@ char *smk_find_label_name(struct smack_known *skp) > struct smack_known *smk_get_label(const char *string, int len, bool import) > { > struct smack_known *skp; > + bool allocated; > char *cp; > > if (import) { > skp = smk_import_entry(string, len); > } else { > - cp = smk_parse_smack(string, len); > + cp = smk_parse_smack(string, len, &allocated); > if (IS_ERR(cp)) > return ERR_CAST(cp); > > skp = smk_find_entry(cp); > - kfree(cp); > + if (allocated) > + kfree(cp); > } > > return skp; > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 5a59836..206e0ce 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -42,6 +42,7 @@ > #include > #include > #include > +#include > #include "smack.h" > > #define TRANS_TRUE "TRUE" > @@ -3496,6 +3497,27 @@ unlockandout: > } > > /** > + * smack_getprocattr_seq - Smack process attribute access through seq > + * @p: the object task > + * @name: the name of the attribute in /proc/.../attr/ > + * @ops: out, seq_operations to handle @name > + * > + * Returns 0 if @name is to be handled by seq, error otherwise. > + */ > +int smack_getprocattr_seq(struct task_struct *p, const char *name, > + const struct seq_operations **ops) > +{ > +#ifdef CONFIG_SECURITY_SMACK_NS > + if (strcmp(name, "label_map") == 0) { > + *ops = &proc_label_map_seq_operations; > + return 0; > + } > +#endif > + > + return -EOPNOTSUPP; > +} > + > +/** > * smack_getprocattr - Smack process attribute access > * @p: the object task > * @name: the name of the attribute in /proc/.../attr > @@ -3527,9 +3549,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) > } > > /** > - * smack_setprocattr - Smack process attribute setting > + * proc_current_write - Smack "current" process attribute setting > * @p: the object task > - * @name: the name of the attribute in /proc/.../attr > * @value: the value to set > * @size: the size of the value > * > @@ -3538,8 +3559,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) > * > * Returns the length of the smack label or an error code > */ > -static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > - char *name, void *value, size_t size) > +static int proc_current_write(struct task_struct *p, void *value, size_t size) > { > struct task_smack *tsp; > struct cred *new; > @@ -3558,9 +3578,6 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > if (value == NULL || size == 0 || size >= SMK_LONGLABEL) > return -EINVAL; > > - if (strcmp(name, "current") != 0) > - return -EINVAL; > - > skp = smk_get_label(value, size, true); > if (IS_ERR(skp)) > return PTR_ERR(skp); > @@ -3583,6 +3600,33 @@ static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > } > > /** > + * smack_setprocattr - Smack process attribute setting > + * @p: the object task > + * @cred: the credentials of the file's opener > + * @name: the name of the attribute in /proc/.../attr > + * @value: the value to set > + * @size: the size of the value > + * > + * Sets the proc attribute > + * > + * Returns the length of the written data or an error code > + */ > +static int smack_setprocattr(struct task_struct *p, const struct cred *f_cred, > + char *name, void *value, size_t size) > +{ > +#ifdef CONFIG_SECURITY_SMACK_NS > + if (strcmp(name, "label_map") == 0) > + return proc_label_map_write(p, f_cred, value, size); > +#endif > + > + if (strcmp(name, "current") == 0) > + return proc_current_write(p, value, size); > + > + return -EINVAL; > + > +} > + > +/** > * smack_unix_stream_connect - Smack access on UDS > * @sock: one sock > * @other: the other sock > @@ -4434,6 +4478,53 @@ static void smack_audit_rule_free(void *vrule) > > #endif /* CONFIG_AUDIT */ > > +#ifdef CONFIG_SECURITY_SMACK_NS > + > +static inline int smack_userns_create(struct user_namespace *ns) > +{ > + struct smack_ns *snsp; > + > + snsp = kzalloc(sizeof(*snsp), GFP_KERNEL); > + if (snsp == NULL) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&snsp->smk_mapped); > + mutex_init(&snsp->smk_mapped_lock); > + > + ns->security = snsp; > + return 0; > +} > + > +static inline void smack_userns_free(struct user_namespace *ns) > +{ > + struct smack_ns *snsp = ns->security; > + struct smack_known *skp; > + struct smack_known_ns *sknp, *n; > + > + list_for_each_entry_safe(sknp, n, &snsp->smk_mapped, smk_list_ns) { > + skp = sknp->smk_unmapped; > + > + mutex_lock(&skp->smk_mapped_lock); > + list_del_rcu(&sknp->smk_list_known); > + if (sknp->smk_allocated) > + kfree(sknp->smk_mapped); > + kfree(sknp); > + mutex_unlock(&skp->smk_mapped_lock); > + > + list_del(&sknp->smk_list_ns); > + } > + > + kfree(snsp); > +} > + > +static inline int smack_userns_setns(struct nsproxy *nsproxy, > + struct user_namespace *ns) > +{ > + return 0; > +} > + > +#endif /* CONFIG_SECURITY_SMACK_NS */ > + > /** > * smack_ismaclabel - check if xattr @name references a smack MAC label > * @name: Full xattr name to check. > @@ -4610,6 +4701,7 @@ static struct security_hook_list smack_hooks[] = { > > LSM_HOOK_INIT(d_instantiate, smack_d_instantiate), > > + LSM_HOOK_INIT(getprocattr_seq, smack_getprocattr_seq), > LSM_HOOK_INIT(getprocattr, smack_getprocattr), > LSM_HOOK_INIT(setprocattr, smack_setprocattr), > > @@ -4647,6 +4739,13 @@ static struct security_hook_list smack_hooks[] = { > LSM_HOOK_INIT(audit_rule_free, smack_audit_rule_free), > #endif /* CONFIG_AUDIT */ > > + /* Namespace hooks */ > +#ifdef CONFIG_SECURITY_SMACK_NS > + LSM_HOOK_INIT(userns_create, smack_userns_create), > + LSM_HOOK_INIT(userns_free, smack_userns_free), > + LSM_HOOK_INIT(userns_setns, smack_userns_setns), > +#endif /* CONFIG_SECURITY_SMACK_NS */ > + > LSM_HOOK_INIT(ismaclabel, smack_ismaclabel), > LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx), > LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid), > @@ -4659,6 +4758,27 @@ static struct security_hook_list smack_hooks[] = { > > static __init void init_smack_known_list(void) > { > +#ifdef CONFIG_SECURITY_SMACK_NS > + /* > + * Initialize mapped list locks > + */ > + mutex_init(&smack_known_huh.smk_mapped_lock); > + mutex_init(&smack_known_hat.smk_mapped_lock); > + mutex_init(&smack_known_floor.smk_mapped_lock); > + mutex_init(&smack_known_star.smk_mapped_lock); > + mutex_init(&smack_known_invalid.smk_mapped_lock); > + mutex_init(&smack_known_web.smk_mapped_lock); > + /* > + * Initialize mapped lists > + */ > + INIT_LIST_HEAD(&smack_known_huh.smk_mapped); > + INIT_LIST_HEAD(&smack_known_hat.smk_mapped); > + INIT_LIST_HEAD(&smack_known_star.smk_mapped); > + INIT_LIST_HEAD(&smack_known_floor.smk_mapped); > + INIT_LIST_HEAD(&smack_known_invalid.smk_mapped); > + INIT_LIST_HEAD(&smack_known_web.smk_mapped); > +#endif /* CONFIG_SECURITY_SMACK_NS */ > + > /* > * Initialize rule list locks > */ > diff --git a/security/smack/smack_ns.c b/security/smack/smack_ns.c > new file mode 100644 > index 0000000..49223c4 > --- /dev/null > +++ b/security/smack/smack_ns.c > @@ -0,0 +1,404 @@ > +/* > + * Copyright (C) 2014 Samsung Electronics. > + * > + * Smack namespaces > + * > + * Author(s): > + * Lukasz Pawelczyk > + * > + * This program is free software, you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include "smack.h" > + > +/** > + * smk_find_mapped_ns - Finds a first namespace from this one through > + * its parrents that has a map. This map is the effective map in this > + * namespace. > + * @ns: a user namespace for which we search for a mapped ns > + * > + * Returns a namespace that has a non-NULL map, or NULL if there is > + * no mapped namespace. > + * > + * Can be effectively used to answer a question: "is there a Smack > + * map for this namespace?" > + */ > +struct user_namespace *smk_find_mapped_ns(struct user_namespace *ns) > +{ > + struct user_namespace *user_ns = ns; > + > + do { > + struct smack_ns *sns = user_ns->security; > + > + if (sns && !list_empty(&sns->smk_mapped)) > + break; > + > + user_ns = user_ns->parent; > + } while (user_ns); > + > + return user_ns; > +} > + > +/** > + * __smk_find_mapped - an internal version of smk_find_mapped > + * that doesn't use smk_find_mapped_ns, but > + * operates directly on the passed one. > + */ > +static struct smack_known_ns *__smk_find_mapped(struct smack_known *skp, > + struct user_namespace *ns) > +{ > + struct smack_known_ns *sknp; > + > + if (ns == NULL) > + return NULL; > + > + list_for_each_entry_rcu(sknp, &skp->smk_mapped, smk_list_known) > + if (sknp->smk_ns == ns) > + return sknp; > + > + return NULL; > +} > + > +/** > + * smk_find_mapped - Finds a mapped label on the smack_known's mapped list > + * @skp: a label which mapped label we look for > + * @ns: a user namespace the label we search for is assigned to > + * > + * Returns a pointer to the mapped label if one exists that is > + * assigned to the specified user namespace or NULL if not found. > + */ > +struct smack_known_ns *smk_find_mapped(struct smack_known *skp, > + struct user_namespace *ns) > +{ > + struct user_namespace *user_ns = smk_find_mapped_ns(ns); > + > + return __smk_find_mapped(skp, user_ns); > +} > + > +/** > + * __smk_find_unmapped - an internal version of smk_find_unmapped > + * that doesn't use smk_find_mapped_ns, but > + * operates directly on the passed one. > + */ > +static struct smack_known *__smk_find_unmapped(const char *string, int len, > + struct user_namespace *ns) > +{ > + struct smack_ns *snsp; > + struct smack_known *skp = NULL; > + struct smack_known_ns *sknp; > + char *smack; > + bool allocated = false; > + > + if (ns == NULL) > + return NULL; > + > + snsp = ns->security; > + > + smack = smk_parse_smack(string, len, &allocated); > + if (IS_ERR(smack)) > + return ERR_CAST(smack); > + > + list_for_each_entry_rcu(sknp, &snsp->smk_mapped, smk_list_ns) { > + if (strcmp(smack, sknp->smk_mapped) == 0) { > + skp = sknp->smk_unmapped; > + break; > + } > + } > + > + if (allocated) > + kfree(smack); > + return skp; > +} > + > +/** > + * smk_find_unmapped - Finds an original label by a mapped label string > + * and the namespace it could be mapped in > + * @string: a name of a mapped label we look for > + * @len: the string size, or zero if it is NULL terminated. > + * @ns: a namespace the looked for label should be mapped in > + * > + * Returns a smack_known label that is mapped as 'string' in 'ns', > + * NULL if not found or an error code. > + */ > +struct smack_known *smk_find_unmapped(const char *string, int len, > + struct user_namespace *ns) > +{ > + struct user_namespace *user_ns = smk_find_mapped_ns(ns); > + > + return __smk_find_unmapped(string, len, user_ns); > +} > + > +/** > + * smk_import_mapped - Imports a mapped label effectively creating a mapping. > + * @skp: a label we map > + * @ns: a user namespace this label will be mapped in > + * @string: a text string of the mapped label > + * @len: the maximum size, or zero if it is NULL terminanted > + * > + * Returns a pointer to the mapped label entry or an error code. > + * > + * The mapped label will be added to 2 lists: > + * - a list of mapped labels of skp > + * - a list of labels mapped in ns > + */ > +static struct smack_known_ns *smk_import_mapped(struct smack_known *skp, > + struct user_namespace *ns, > + const char *string, int len) > +{ > + struct smack_ns *snsp = ns->security; > + struct smack_known_ns *sknp; > + char *mapped; > + bool allocated; > + > + /* Mapping init_user_ns is against the design and pointless */ > + if (ns == &init_user_ns) > + return ERR_PTR(-EBADR); > + > + mapped = smk_parse_smack(string, len, &allocated); > + if (IS_ERR(mapped)) > + return ERR_CAST(mapped); > + > + mutex_lock(&skp->smk_mapped_lock); > + > + /* > + * Don't allow one<->many mappings in namespace, rename. > + * This code won't get triggered for now as trying to assign > + * a duplicate is forbidden in proc_label_map_write(). > + * Leaving this as this function might be also used elsewhere. > + */ > + sknp = smk_find_mapped(skp, ns); > + if (sknp != NULL) { > + if (sknp->smk_allocated) > + kfree(sknp->smk_mapped); > + sknp->smk_mapped = mapped; > + sknp->smk_allocated = allocated; > + goto unlockout; > + } > + > + sknp = kzalloc(sizeof(*sknp), GFP_KERNEL); > + if (sknp == NULL) { > + sknp = ERR_PTR(-ENOMEM); > + if (allocated) > + kfree(mapped); > + goto unlockout; > + } > + > + sknp->smk_ns = ns; > + sknp->smk_mapped = mapped; > + sknp->smk_allocated = allocated; > + sknp->smk_unmapped = skp; > + list_add_rcu(&sknp->smk_list_known, &skp->smk_mapped); > + > + mutex_lock(&snsp->smk_mapped_lock); > + list_add_rcu(&sknp->smk_list_ns, &snsp->smk_mapped); > + mutex_unlock(&snsp->smk_mapped_lock); > + > +unlockout: > + mutex_unlock(&skp->smk_mapped_lock); > + > + return sknp; > +} > + > +static void *proc_label_map_seq_start(struct seq_file *seq, loff_t *pos) > +{ > + struct smack_known *skp; > + struct task_struct *task = seq->private; > + struct user_namespace *ns = ns_of_task_struct(task); > + loff_t counter = *pos; > + > + rcu_read_lock(); > + list_for_each_entry_rcu(skp, &smack_known_list, list) > + if (smk_find_mapped(skp, ns) && counter-- == 0) > + return skp; > + > + return NULL; > +} > + > +static void proc_label_map_seq_stop(struct seq_file *seq, void *v) > +{ > + rcu_read_unlock(); > +} > + > +static void *proc_label_map_seq_next(struct seq_file *seq, void *v, loff_t *pos) > +{ > + struct smack_known *skp = v; > + struct task_struct *task = seq->private; > + struct user_namespace *ns = ns_of_task_struct(task); > + > + list_for_each_entry_continue_rcu(skp, &smack_known_list, list) { > + if (smk_find_mapped(skp, ns)) { > + (*pos)++; > + return skp; > + } > + } > + > + return NULL; > +} > + > +static int proc_label_map_seq_show(struct seq_file *seq, void *v) > +{ > + struct smack_known *skp = v; > + struct task_struct *task = seq->private; > + struct user_namespace *ns = ns_of_task_struct(task); > + struct smack_known_ns *sknp; > + > + sknp = smk_find_mapped(skp, ns); > + if (sknp) > + seq_printf(seq, "%s -> %s\n", skp->smk_known, sknp->smk_mapped); > + > + return 0; > +} > + > +const struct seq_operations proc_label_map_seq_operations = { > + .start = proc_label_map_seq_start, > + .stop = proc_label_map_seq_stop, > + .next = proc_label_map_seq_next, > + .show = proc_label_map_seq_show, > +}; > + > +static DEFINE_MUTEX(smk_map_mutex); > + > +static bool mapping_permitted(const struct cred *f_cred, > + struct user_namespace *user_ns) > +{ > + /* > + * Do not allow mapping own label. This is in contrast to user > + * namespace where you can always map your own UID. In Smack having > + * administrative privileges over your own label (which Smack > + * namespace would effectively give you) is not equivalent to user > + * namespace. E.g. things like setting exec/transmute labels that > + * otherwise would be denied. Hence no own_label param here. > + */ > + > + /* > + * Adjusting namespace settings requires capabilities on the target. > + */ > + if (security_capable(f_cred, user_ns, CAP_MAC_ADMIN) != 0) > + return false; > + > + /* > + * And it requires capabilities in the parent. > + * > + * If the Smack namespace was properly hierarchical the user_ns to > + * check against could be 'user_ns->parent'. Right now because of > + * security concerns only privileged initial namespace is allowed > + * to fill the map. For a hierarchical namespaces one would > + * implement mapping (in the child namespaces) of only mapped labels > + * (in parent namespace) and change '&init_user_ns' to > + * 'user_ns->parent'. This will be added in the future. > + */ > + if (smack_ns_privileged(&init_user_ns, CAP_MAC_ADMIN) && > + security_capable(f_cred, &init_user_ns, CAP_MAC_ADMIN) == 0) > + return true; > + > + return false; > +} > + > +ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred, > + void *value, size_t size) > +{ > + struct user_namespace *ns = ns_of_task_struct(p); > + struct user_namespace *cur_ns = ns_of_current(); > + struct smack_known *skp; > + struct smack_known_ns *sknp; > + char *pos, *next_line, *tok[2]; > + ssize_t ret; > + int i; > + > + /* Mapping labels for the init ns makes no sense */ > + if (ns == &init_user_ns) > + return -EBADR; > + > + if (cur_ns != ns->parent) > + return -EPERM; > + > + if (!mapping_permitted(f_cred, ns)) > + return -EPERM; > + > + if (value == NULL || size == 0 || size >= PAGE_SIZE) > + return -EINVAL; > + > + mutex_lock(&smk_map_mutex); > + > + /* Parse the user data */ > + pos = value; > + pos[size] = '\0'; > + > + for (; pos; pos = next_line) { > + ret = -EINVAL; > + > + /* Find the end of line and ensure I don't look past it */ > + next_line = strchr(pos, '\n'); > + if (next_line) { > + *next_line = '\0'; > + next_line++; > + if (*next_line == '\0') > + next_line = NULL; > + } > + > + /* Find tokens in line */ > + for (i = 0; i < 2; ++i) { > + while (isspace(*pos)) > + *(pos++) = '\0'; > + > + /* unexpected end of file */ > + if (*pos == '\0') > + goto out; > + > + tok[i] = pos; > + > + /* find the end of the token */ > + while (*pos != '\0' && !isspace(*pos)) > + ++pos; > + } > + > + /* NUL terminate the last token if not EOL */ > + while (isspace(*pos)) > + *(pos++) = '\0'; > + > + /* there should not be any trailing data */ > + if (*pos != '\0') > + goto out; > + > + ret = -EEXIST; > + > + /* do not allow to map 2 different labels to one name */ > + skp = __smk_find_unmapped(tok[1], 0, ns); > + if (IS_ERR(skp)) { > + ret = PTR_ERR(skp); > + goto out; > + } > + if (skp != NULL) > + goto out; > + > + skp = smk_import_entry(tok[0], 0); > + if (IS_ERR(skp)) { > + ret = PTR_ERR(skp); > + goto out; > + } > + > + /* do not allow remapping */ > + if (__smk_find_mapped(skp, ns)) > + goto out; > + > + sknp = smk_import_mapped(skp, ns, tok[1], 0); > + if (IS_ERR(sknp)) { > + ret = PTR_ERR(sknp); > + goto out; > + } > + } > + > + ret = size; > + > +out: > + mutex_unlock(&smk_map_mutex); > + > + return ret; > +} From casey at schaufler-ca.com Thu Oct 29 22:52:08 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:52:08 -0700 Subject: [PATCH v4 10/11] smack: namespace implementation In-Reply-To: <1444826525-9758-11-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-11-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A318.8090104@schaufler-ca.com> On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > This commit uses all the changes introduced in "namespace groundwork" > and previous preparation patches and makes smack aware of its namespace > and mapped labels. > > It modifies the following functions to be namespace aware: > - smk_access > - smk_find_label_name > - smk_get_label > > And all functions that use them (e.g. smk_tskacc). > > It also adds another function that is used throughout Smack LSM hooks: > - smk_labels_valid - it checks whether both, subject and object labels > are properly mapped in a namespace where they are to be used. This > function is used mostly together with a capability check when there is > no proper access check that usually checks for that. > > All the Smack LSM hooks have been adapted to be namespace aware. > > The capabilities (CAP_MAC_ADMIN, CAP_MAC_OVERRIDE) has been allowed in > the namespace for few cases. Check the documentation for the details. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler Acked-by: Casey Schaufler > --- > security/smack/smack.h | 29 +++- > security/smack/smack_access.c | 109 ++++++++++-- > security/smack/smack_lsm.c | 390 ++++++++++++++++++++++++++++++------------ > security/smack/smack_ns.c | 39 +++++ > security/smack/smackfs.c | 63 ++++--- > 5 files changed, 483 insertions(+), 147 deletions(-) > > diff --git a/security/smack/smack.h b/security/smack/smack.h > index 4b7489f..3d432f4 100644 > --- a/security/smack/smack.h > +++ b/security/smack/smack.h > @@ -119,6 +119,7 @@ struct superblock_smack { > struct smack_known *smk_floor; > struct smack_known *smk_hat; > struct smack_known *smk_default; > + struct user_namespace *smk_ns; > int smk_initialized; > }; > > @@ -126,6 +127,7 @@ struct socket_smack { > struct smack_known *smk_out; /* outbound label */ > struct smack_known *smk_in; /* inbound label */ > struct smack_known *smk_packet; /* TCP peer label */ > + struct user_namespace *smk_ns; /* user namespace */ > }; > > /* > @@ -146,6 +148,14 @@ struct task_smack { > struct mutex smk_rules_lock; /* lock for the rules */ > }; > > +/* > + * Used for IPC objects (sem, shm, etc) > + */ > +struct ipc_smack { > + struct smack_known *smk_known; /* label for access control */ > + struct user_namespace *smk_ns; /* user namespace */ > +}; > + > #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ > #define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */ > #define SMK_INODE_CHANGED 0x04 /* smack was transmuted */ > @@ -319,10 +329,11 @@ struct smk_audit_info { > */ > int smk_access_entry(char *, char *, struct list_head *); > int smk_access(struct smack_known *, struct smack_known *, > - int, struct smk_audit_info *); > + struct user_namespace *, int, struct smk_audit_info *); > int smk_tskacc(struct task_struct *, struct smack_known *, > + struct user_namespace *, u32, struct smk_audit_info *); > +int smk_curacc(struct smack_known *, struct user_namespace *, > u32, struct smk_audit_info *); > -int smk_curacc(struct smack_known *, u32, struct smk_audit_info *); > struct smack_known *smack_from_secid(const u32); > char *smk_parse_smack(const char *string, int len, bool *allocated); > int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); > @@ -335,8 +346,9 @@ int smack_has_ns_privilege(struct task_struct *task, > int smack_has_privilege(struct task_struct *task, int cap); > int smack_ns_privileged(struct user_namespace *user_ns, int cap); > int smack_privileged(int cap); > -char *smk_find_label_name(struct smack_known *skp); > -struct smack_known *smk_get_label(const char *string, int len, bool import); > +char *smk_find_label_name(struct smack_known *skp, struct user_namespace *ns); > +struct smack_known *smk_get_label(const char *string, int len, bool import, > + struct user_namespace *ns); > > /* > * These functions are in smack_ns.c > @@ -350,6 +362,15 @@ struct smack_known *smk_find_unmapped(const char *string, int len, > extern const struct seq_operations proc_label_map_seq_operations; > ssize_t proc_label_map_write(struct task_struct *p, const struct cred *f_cred, > void *value, size_t size); > +bool smk_labels_valid(struct smack_known *sbj, struct smack_known *obj, > + struct user_namespace *ns); > +#else > +static inline bool smk_labels_valid(struct smack_known *sbj, > + struct smack_known *obj, > + struct user_namespace *ns) > +{ > + return true; > +} > #endif /* CONFIG_SECURITY_SMACK_NS */ > > /* > diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c > index 17b7e2c..e230948 100644 > --- a/security/smack/smack_access.c > +++ b/security/smack/smack_access.c > @@ -14,6 +14,7 @@ > #include > #include > #include > +#include > #include "smack.h" > > struct smack_known smack_known_huh = { > @@ -113,6 +114,7 @@ int smk_access_entry(char *subject_label, char *object_label, > * smk_access - determine if a subject has a specific access to an object > * @subject: a pointer to the subject's Smack label entry > * @object: a pointer to the object's Smack label entry > + * @ns: user namespace to check against (usually subject's) > * @request: the access requested, in "MAY" format > * @a : a pointer to the audit data > * > @@ -123,10 +125,34 @@ int smk_access_entry(char *subject_label, char *object_label, > * Smack labels are shared on smack_list > */ > int smk_access(struct smack_known *subject, struct smack_known *object, > - int request, struct smk_audit_info *a) > + struct user_namespace *ns, int request, struct smk_audit_info *a) > { > int may = MAY_NOT; > int rc = 0; > + char *subject_label = subject->smk_known; > + char *object_label = object->smk_known; > +#ifdef CONFIG_SECURITY_SMACK_NS > + struct smack_known_ns *sknp; > + struct smack_known_ns *oknp; > + > + /* > + * For the namespaced case we need to check whether the labels > + * are mapped. If not, refuse. If yes check the builtin rules > + * on the mapped label strings so the builtin labels can > + * work properly inside the namespace. > + */ > + if (smk_find_mapped_ns(ns)) { > + sknp = smk_find_mapped(subject, ns); > + oknp = smk_find_mapped(object, ns); > + if (!sknp || !oknp) { > + rc = -EACCES; > + goto out_audit; > + } > + > + subject_label = sknp->smk_mapped; > + object_label = oknp->smk_mapped; > + } > +#endif > > /* > * Hardcoded comparisons. > @@ -134,7 +160,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, > /* > * A star subject can't access any object. > */ > - if (subject == &smack_known_star) { > + if (subject_label == smack_known_star.smk_known) { > rc = -EACCES; > goto out_audit; > } > @@ -143,18 +169,19 @@ int smk_access(struct smack_known *subject, struct smack_known *object, > * Tasks cannot be assigned the internet label. > * An internet subject can access any object. > */ > - if (object == &smack_known_web || subject == &smack_known_web) > + if (object_label == smack_known_web.smk_known || > + subject_label == smack_known_web.smk_known) > goto out_audit; > /* > * A star object can be accessed by any subject. > */ > - if (object == &smack_known_star) > + if (object_label == smack_known_star.smk_known) > goto out_audit; > /* > * An object can be accessed in any way by a subject > * with the same label. > */ > - if (subject->smk_known == object->smk_known) > + if (subject_label == object_label) > goto out_audit; > /* > * A hat subject can read or lock any object. > @@ -162,9 +189,9 @@ int smk_access(struct smack_known *subject, struct smack_known *object, > */ > if ((request & MAY_ANYREAD) == request || > (request & MAY_LOCK) == request) { > - if (object == &smack_known_floor) > + if (object_label == smack_known_floor.smk_known) > goto out_audit; > - if (subject == &smack_known_hat) > + if (subject_label == smack_known_hat.smk_known) > goto out_audit; > } > > @@ -174,6 +201,7 @@ int smk_access(struct smack_known *subject, struct smack_known *object, > * access (e.g. read is included in readwrite) it's > * good. A negative response from smk_access_entry() > * indicates there is no entry for this pair. > + * For this check we need real, not mapped labels. > */ > rcu_read_lock(); > may = smk_access_entry(subject->smk_known, object->smk_known, > @@ -219,6 +247,7 @@ out_audit: > * smk_tskacc - determine if a task has a specific access to an object > * @tsp: a pointer to the subject's task > * @obj_known: a pointer to the object's label entry > + * @obj_ns: an object's namespace to check the caps against > * @mode: the access requested, in "MAY" format > * @a : common audit data > * > @@ -228,16 +257,18 @@ out_audit: > * to override the rules. > */ > int smk_tskacc(struct task_struct *task, struct smack_known *obj_known, > - u32 mode, struct smk_audit_info *a) > + struct user_namespace *obj_ns, u32 mode, > + struct smk_audit_info *a) > { > struct smack_known *sbj_known = smk_of_task_struct(task); > + struct user_namespace *sbj_ns = ns_of_task_struct(task); > int may; > int rc; > > /* > * Check the global rule list > */ > - rc = smk_access(sbj_known, obj_known, mode, NULL); > + rc = smk_access(sbj_known, obj_known, sbj_ns, mode, NULL); > if (rc >= 0) { > struct task_smack *tsp; > > @@ -261,8 +292,10 @@ int smk_tskacc(struct task_struct *task, struct smack_known *obj_known, > > /* > * Allow for priviliged to override policy. > + * Either in init_ns or when both labels are mapped. > */ > - if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE)) > + if (rc != 0 && smk_labels_valid(sbj_known, obj_known, sbj_ns) > + && smack_has_ns_privilege(task, obj_ns, CAP_MAC_OVERRIDE)) > rc = 0; > > out_audit: > @@ -277,6 +310,7 @@ out_audit: > /** > * smk_curacc - determine if current has a specific access to an object > * @obj_known: a pointer to the object's Smack label entry > + * @obj_ns: an object's namespace to check the caps against > * @mode: the access requested, in "MAY" format > * @a : common audit data > * > @@ -285,10 +319,10 @@ out_audit: > * non zero otherwise. It allows that current may have the capability > * to override the rules. > */ > -int smk_curacc(struct smack_known *obj_known, > +int smk_curacc(struct smack_known *obj_known, struct user_namespace *obj_ns, > u32 mode, struct smk_audit_info *a) > { > - return smk_tskacc(current, obj_known, mode, a); > + return smk_tskacc(current, obj_known, obj_ns, mode, a); > } > > #ifdef CONFIG_AUDIT > @@ -671,12 +705,13 @@ DEFINE_MUTEX(smack_onlycap_lock); > * > * For a capability in smack related checks to be effective it needs to: > * - be allowed to be privileged by the onlycap rule. > - * - be in the initial user ns > + * - be in the initial user ns or have a filled map in the child ns > */ > static int smack_capability_allowed(struct smack_known *skp, > struct user_namespace *user_ns) > { > struct smack_onlycap *sop; > + struct smack_ns *sns; > > /* > * All kernel tasks are privileged > @@ -684,8 +719,15 @@ static int smack_capability_allowed(struct smack_known *skp, > if (unlikely(current->flags & PF_KTHREAD)) > return 1; > > +#ifdef CONFIG_SECURITY_SMACK_NS > + sns = user_ns->security; > + > + if (user_ns != &init_user_ns && list_empty(&sns->smk_mapped)) > + return 0; > +#else > if (user_ns != &init_user_ns) > return 0; > +#endif /* CONFIG_SECURITY_SMACK_NS */ > > rcu_read_lock(); > if (list_empty(&smack_onlycap_list)) { > @@ -755,14 +797,32 @@ int smack_privileged(int cap) > } > > /** > - * smk_find_label_name - A helper to get a string value of a label > + * smk_find_label_name - A helper to get a string value of either a label or a > + * mapped label when inside a namespace > * @skp: a label we want a string value from > + * @ns: namespace against which we want to get the value > * > * Returns a pointer to a label name or NULL if label name not found. > */ > -char *smk_find_label_name(struct smack_known *skp) > +char *smk_find_label_name(struct smack_known *skp, struct user_namespace *ns) > { > - return skp->smk_known; > + char *name = NULL; > + > +#ifdef CONFIG_SECURITY_SMACK_NS > + struct smack_known_ns *sknp; > + > + if (smk_find_mapped_ns(ns)) { > + sknp = smk_find_mapped(skp, ns); > + if (sknp != NULL) > + name = sknp->smk_mapped; > + } else { > + name = skp->smk_known; > + } > +#else > + name = skp->smk_known; > +#endif > + > + return name; > } > > /** > @@ -771,17 +831,32 @@ char *smk_find_label_name(struct smack_known *skp) > * @string: a name of a label we look for or want to import > * @len: the string size, or zero if it is NULL terminated > * @import: whether we should import the label if not found > + * @ns: a namespace the looked for label should be in > * > * Returns a smack_known label that is either imported or found. > * NULL if label not found (only when import == false). > * Error code otherwise. > */ > -struct smack_known *smk_get_label(const char *string, int len, bool import) > +struct smack_known *smk_get_label(const char *string, int len, bool import, > + struct user_namespace *ns) > { > struct smack_known *skp; > bool allocated; > char *cp; > > +#ifdef CONFIG_SECURITY_SMACK_NS > + if (smk_find_mapped_ns(ns)) { > + skp = smk_find_unmapped(string, len, ns); > + > + /* Label not found but we can't import in namespaces */ > + if (skp == NULL && import) > + skp = ERR_PTR(-EBADR); > + > + /* will also return error codes from smk_find_unmapped() */ > + return skp; > + } > +#endif > + > if (import) { > skp = smk_import_entry(string, len); > } else { > diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c > index 206e0ce..8e0da67 100644 > --- a/security/smack/smack_lsm.c > +++ b/security/smack/smack_lsm.c > @@ -383,6 +383,7 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) > * smk_ptrace_rule_check - helper for ptrace access > * @tracer: tracer process > * @tracee_known: label entry of the process that's about to be traced > + * @tracee_ns: a tracee's namespace to check the caps against > * @mode: ptrace attachment mode (PTRACE_MODE_*) > * @func: name of the function that called us, used for audit > * > @@ -390,6 +391,7 @@ static inline unsigned int smk_ptrace_mode(unsigned int mode) > */ > static int smk_ptrace_rule_check(struct task_struct *tracer, > struct smack_known *tracee_known, > + struct user_namespace *tracee_ns, > unsigned int mode, const char *func) > { > int rc; > @@ -401,21 +403,28 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > saip = &ad; > } > > - > if ((mode & PTRACE_MODE_ATTACH) && > (smack_ptrace_rule == SMACK_PTRACE_EXACT || > smack_ptrace_rule == SMACK_PTRACE_DRACONIAN)) { > struct smack_known *tracer_known = smk_of_task_struct(tracer); > + struct user_namespace *tracer_ns = ns_of_task_struct(tracer); > + > + if (!smk_labels_valid(tracer_known, tracee_known, tracer_ns)) { > + rc = -EACCES; > + goto out; > + } > > if (tracer_known->smk_known == tracee_known->smk_known) > rc = 0; > else if (smack_ptrace_rule == SMACK_PTRACE_DRACONIAN) > rc = -EACCES; > - else if (smack_has_privilege(tracer, CAP_SYS_PTRACE)) > + else if (smack_has_ns_privilege(tracer, tracee_ns, > + CAP_SYS_PTRACE)) > rc = 0; > else > rc = -EPERM; > > +out: > if (saip) > smack_log(tracer_known->smk_known, > tracee_known->smk_known, > @@ -425,7 +434,8 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > } > > /* In case of rule==SMACK_PTRACE_DEFAULT or mode==PTRACE_MODE_READ */ > - return smk_tskacc(tracer, tracee_known, smk_ptrace_mode(mode), saip); > + return smk_tskacc(tracer, tracee_known, tracee_ns, > + smk_ptrace_mode(mode), saip); > } > > /* > @@ -445,8 +455,9 @@ static int smk_ptrace_rule_check(struct task_struct *tracer, > static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) > { > struct smack_known *skp = smk_of_task_struct(ctp); > + struct user_namespace *ns = ns_of_task_struct(ctp); > > - return smk_ptrace_rule_check(current, skp, mode, __func__); > + return smk_ptrace_rule_check(current, skp, ns, mode, __func__); > } > > /** > @@ -460,8 +471,10 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) > static int smack_ptrace_traceme(struct task_struct *ptp) > { > struct smack_known *skp = smk_of_current(); > + struct user_namespace *ns = ns_of_current(); > > - return smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__); > + return smk_ptrace_rule_check(ptp, skp, ns, PTRACE_MODE_ATTACH, > + __func__); > } > > /** > @@ -508,6 +521,7 @@ static int smack_sb_alloc_security(struct super_block *sb) > sbsp->smk_default = &smack_known_floor; > sbsp->smk_floor = &smack_known_floor; > sbsp->smk_hat = &smack_known_hat; > + sbsp->smk_ns = get_user_ns(&init_user_ns); > /* > * smk_initialized will be zero from kzalloc. > */ > @@ -523,6 +537,9 @@ static int smack_sb_alloc_security(struct super_block *sb) > */ > static void smack_sb_free_security(struct super_block *sb) > { > + struct superblock_smack *sbsp = sb->s_security; > + > + put_user_ns(sbsp->smk_ns); > kfree(sb->s_security); > sb->s_security = NULL; > } > @@ -724,6 +741,7 @@ static int smack_set_mnt_opts(struct super_block *sb, > struct smack_known *skp; > int i; > int num_opts = opts->num_mnt_opts; > + struct user_namespace *ns = ns_of_current(); > int transmute = 0; > > if (sp->smk_initialized) > @@ -734,31 +752,31 @@ static int smack_set_mnt_opts(struct super_block *sb, > for (i = 0; i < num_opts; i++) { > switch (opts->mnt_opts_flags[i]) { > case FSDEFAULT_MNT: > - skp = smk_get_label(opts->mnt_opts[i], 0, true); > + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_default = skp; > break; > case FSFLOOR_MNT: > - skp = smk_get_label(opts->mnt_opts[i], 0, true); > + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_floor = skp; > break; > case FSHAT_MNT: > - skp = smk_get_label(opts->mnt_opts[i], 0, true); > + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_hat = skp; > break; > case FSROOT_MNT: > - skp = smk_get_label(opts->mnt_opts[i], 0, true); > + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_root = skp; > break; > case FSTRANS_MNT: > - skp = smk_get_label(opts->mnt_opts[i], 0, true); > + skp = smk_get_label(opts->mnt_opts[i], 0, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > sp->smk_root = skp; > @@ -769,7 +787,12 @@ static int smack_set_mnt_opts(struct super_block *sb, > } > } > > - if (!smack_privileged(CAP_MAC_ADMIN)) { > + /* > + * Check for non-privileged case. If current is inside the namespace > + * and the it has privileges the validity of labels has already been > + * checked during smk_get_label() > + */ > + if (!smack_ns_privileged(ns, CAP_MAC_ADMIN)) { > /* > * Unprivileged mounts don't get to specify Smack values. > */ > @@ -798,6 +821,12 @@ static int smack_set_mnt_opts(struct super_block *sb, > if (transmute) > isp->smk_flags |= SMK_INODE_TRANSMUTE; > > + /* > + * Set the superblock namespace from a mounting process > + */ > + put_user_ns(sp->smk_ns); > + sp->smk_ns = get_user_ns(ns); > + > return 0; > } > > @@ -848,7 +877,7 @@ static int smack_sb_statfs(struct dentry *dentry) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); > smk_ad_setfield_u_fs_path_dentry(&ad, dentry); > > - rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad); > + rc = smk_curacc(sbp->smk_floor, sbp->smk_ns, MAY_READ, &ad); > rc = smk_bu_current("statfs", sbp->smk_floor, MAY_READ, rc); > return rc; > } > @@ -868,6 +897,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) > struct inode *inode = file_inode(bprm->file); > struct task_smack *bsp = bprm->cred->security; > struct inode_smack *isp; > + struct user_namespace *ns = ns_of_current(); > int rc; > > if (bprm->cred_prepared) > @@ -877,6 +907,13 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) > if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) > return 0; > > +#ifdef CONFIG_SECURITY_SMACK_NS > + /* one label version of smk_labels_valid() */ > + if (smk_find_mapped_ns(ns) && > + smk_find_mapped(isp->smk_task, ns) == NULL) > + return -EACCES; > +#endif > + > if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { > struct task_struct *tracer; > rc = 0; > @@ -884,9 +921,8 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) > rcu_read_lock(); > tracer = ptrace_parent(current); > if (likely(tracer != NULL)) > - rc = smk_ptrace_rule_check(tracer, > - isp->smk_task, > - PTRACE_MODE_ATTACH, > + rc = smk_ptrace_rule_check(tracer, isp->smk_task, > + ns, PTRACE_MODE_ATTACH, > __func__); > rcu_read_unlock(); > > @@ -1027,6 +1063,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, > struct dentry *new_dentry) > { > struct smack_known *isp; > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int rc; > > @@ -1034,13 +1071,13 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, > smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); > > isp = smk_of_inode(d_backing_inode(old_dentry)); > - rc = smk_curacc(isp, MAY_WRITE, &ad); > + rc = smk_curacc(isp, ns, MAY_WRITE, &ad); > rc = smk_bu_inode(d_backing_inode(old_dentry), MAY_WRITE, rc); > > if (rc == 0 && d_is_positive(new_dentry)) { > isp = smk_of_inode(d_backing_inode(new_dentry)); > smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); > - rc = smk_curacc(isp, MAY_WRITE, &ad); > + rc = smk_curacc(isp, ns, MAY_WRITE, &ad); > rc = smk_bu_inode(d_backing_inode(new_dentry), MAY_WRITE, rc); > } > > @@ -1058,6 +1095,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, > static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) > { > struct inode *ip = d_backing_inode(dentry); > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int rc; > > @@ -1067,7 +1105,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) > /* > * You need write access to the thing you're unlinking > */ > - rc = smk_curacc(smk_of_inode(ip), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(ip), ns, MAY_WRITE, &ad); > rc = smk_bu_inode(ip, MAY_WRITE, rc); > if (rc == 0) { > /* > @@ -1075,7 +1113,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) > */ > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); > smk_ad_setfield_u_fs_inode(&ad, dir); > - rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(dir), ns, MAY_WRITE, &ad); > rc = smk_bu_inode(dir, MAY_WRITE, rc); > } > return rc; > @@ -1091,6 +1129,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) > */ > static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) > { > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int rc; > > @@ -1100,7 +1139,8 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) > /* > * You need write access to the thing you're removing > */ > - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), ns, > + MAY_WRITE, &ad); > rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); > if (rc == 0) { > /* > @@ -1108,7 +1148,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) > */ > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); > smk_ad_setfield_u_fs_inode(&ad, dir); > - rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(dir), ns, MAY_WRITE, &ad); > rc = smk_bu_inode(dir, MAY_WRITE, rc); > } > > @@ -1132,21 +1172,22 @@ static int smack_inode_rename(struct inode *old_inode, > struct inode *new_inode, > struct dentry *new_dentry) > { > - int rc; > struct smack_known *isp; > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > + int rc; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); > smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); > > isp = smk_of_inode(d_backing_inode(old_dentry)); > - rc = smk_curacc(isp, MAY_READWRITE, &ad); > + rc = smk_curacc(isp, ns, MAY_READWRITE, &ad); > rc = smk_bu_inode(d_backing_inode(old_dentry), MAY_READWRITE, rc); > > if (rc == 0 && d_is_positive(new_dentry)) { > isp = smk_of_inode(d_backing_inode(new_dentry)); > smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry); > - rc = smk_curacc(isp, MAY_READWRITE, &ad); > + rc = smk_curacc(isp, ns, MAY_READWRITE, &ad); > rc = smk_bu_inode(d_backing_inode(new_dentry), MAY_READWRITE, rc); > } > return rc; > @@ -1163,6 +1204,7 @@ static int smack_inode_rename(struct inode *old_inode, > */ > static int smack_inode_permission(struct inode *inode, int mask) > { > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int no_block = mask & MAY_NOT_BLOCK; > int rc; > @@ -1179,7 +1221,7 @@ static int smack_inode_permission(struct inode *inode, int mask) > return -ECHILD; > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); > smk_ad_setfield_u_fs_inode(&ad, inode); > - rc = smk_curacc(smk_of_inode(inode), mask, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, mask, &ad); > rc = smk_bu_inode(inode, mask, rc); > return rc; > } > @@ -1193,6 +1235,7 @@ static int smack_inode_permission(struct inode *inode, int mask) > */ > static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) > { > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int rc; > > @@ -1204,7 +1247,8 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); > smk_ad_setfield_u_fs_path_dentry(&ad, dentry); > > - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), ns, > + MAY_WRITE, &ad); > rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); > return rc; > } > @@ -1218,13 +1262,14 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) > */ > static int smack_inode_getattr(const struct path *path) > { > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > struct inode *inode = d_backing_inode(path->dentry); > int rc; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); > smk_ad_setfield_u_fs_path(&ad, *path); > - rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, MAY_READ, &ad); > rc = smk_bu_inode(inode, MAY_READ, rc); > return rc; > } > @@ -1246,6 +1291,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, > { > struct smk_audit_info ad; > struct smack_known *skp; > + struct smack_known *sbj = smk_of_current(); > + struct smack_known *obj = smk_of_inode(d_backing_inode(dentry)); > + struct user_namespace *ns = ns_of_current(); > int check_priv = 0; > int check_import = 0; > int check_star = 0; > @@ -1272,11 +1320,12 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, > } else > rc = cap_inode_setxattr(dentry, name, value, size, flags); > > - if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) > + if (check_priv && !(smk_labels_valid(sbj, obj, ns) && > + smack_ns_privileged(ns, CAP_MAC_ADMIN))) > rc = -EPERM; > > if (rc == 0 && check_import) { > - skp = size ? smk_get_label(value, size, true) : NULL; > + skp = size ? smk_get_label(value, size, true, ns) : NULL; > if (IS_ERR(skp)) > rc = PTR_ERR(skp); > else if (skp == NULL || (check_star && > @@ -1288,7 +1337,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, > smk_ad_setfield_u_fs_path_dentry(&ad, dentry); > > if (rc == 0) { > - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); > + rc = smk_curacc(obj, ns, MAY_WRITE, &ad); > rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); > } > > @@ -1296,6 +1345,40 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, > } > > /** > + * smack_inode_pre_setxattr - Unmap the namespaced label > + * @dentry: object > + * @name: attribute name > + * @value: attribute value > + * @size: attribute size > + * @flags: unused > + * @alloc: unused > + * > + * Guarantees that the real label value will be written to the filesystem. > + */ > +static int smack_inode_pre_setxattr(struct dentry *dentry, const char *name, > + const void **value, size_t *size, int flags, > + bool *alloc) > +{ > + struct smack_known *skp; > + struct user_namespace *ns = ns_of_current(); > + > + if (strcmp(name, XATTR_NAME_SMACK) != 0 && > + strcmp(name, XATTR_NAME_SMACKEXEC) != 0 && > + strcmp(name, XATTR_NAME_SMACKMMAP) != 0) > + return 0; > + > + /* Convert value to non namespaced label */ > + skp = smk_get_label(*value, *size, true, ns); > + if (IS_ERR(skp)) > + return PTR_ERR(skp); > + > + *value = skp->smk_known; > + *size = strlen(skp->smk_known); > + > + return 0; > +} > + > +/** > * smack_inode_post_setxattr - Apply the Smack update approved above > * @dentry: object > * @name: attribute name > @@ -1326,7 +1409,8 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, > skpp = &isp->smk_mmap; > > if (skpp) { > - skp = smk_get_label(value, size, true); > + /* value has been un-namespaced in inode_pre_setxattr() */ > + skp = smk_get_label(value, size, true, &init_user_ns); > > if (!IS_ERR(skp)) > *skpp = skp; > @@ -1344,13 +1428,15 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, > */ > static int smack_inode_getxattr(struct dentry *dentry, const char *name) > { > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int rc; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); > smk_ad_setfield_u_fs_path_dentry(&ad, dentry); > > - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_READ, &ad); > + rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), ns, > + MAY_READ, &ad); > rc = smk_bu_inode(d_backing_inode(dentry), MAY_READ, rc); > return rc; > } > @@ -1367,6 +1453,9 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name) > static int smack_inode_removexattr(struct dentry *dentry, const char *name) > { > struct inode_smack *isp; > + struct smack_known *sbj = smk_of_current(); > + struct smack_known *obj = smk_of_inode(d_backing_inode(dentry)); > + struct user_namespace *ns = ns_of_current(); > struct smk_audit_info ad; > int rc = 0; > > @@ -1376,7 +1465,8 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) > strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || > strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0 || > strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { > - if (!smack_privileged(CAP_MAC_ADMIN)) > + if (!smk_labels_valid(sbj, obj, ns) || > + !smack_ns_privileged(ns, CAP_MAC_ADMIN)) > rc = -EPERM; > } else > rc = cap_inode_removexattr(dentry, name); > @@ -1387,7 +1477,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); > smk_ad_setfield_u_fs_path_dentry(&ad, dentry); > > - rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad); > + rc = smk_curacc(obj, ns, MAY_WRITE, &ad); > rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc); > if (rc != 0) > return rc; > @@ -1427,13 +1517,18 @@ static int smack_inode_getsecurity(const struct inode *inode, > struct super_block *sbp; > struct inode *ip = (struct inode *)inode; > struct smack_known *isp = NULL; > + struct user_namespace *ns = ns_of_current(); > int rc = 0; > > if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) > isp = smk_of_inode(inode); > + else if (strcmp(name, XATTR_SMACK_EXEC) == 0) > + isp = smk_of_exec(inode); > + else if (strcmp(name, XATTR_SMACK_MMAP) == 0) > + isp = smk_of_mmap(inode); > > if (isp) { > - *buffer = smk_find_label_name(isp); > + *buffer = smk_find_label_name(isp, ns); > if (*buffer == NULL) > *buffer = smack_known_huh.smk_known; > return strlen(*buffer); > @@ -1460,7 +1555,7 @@ static int smack_inode_getsecurity(const struct inode *inode, > return -EOPNOTSUPP; > > if (rc == 0) { > - *buffer = smk_find_label_name(isp); > + *buffer = smk_find_label_name(isp, ns); > if (*buffer == NULL) > *buffer = smack_known_huh.smk_known; > rc = strlen(*buffer); > @@ -1571,18 +1666,19 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd, > { > int rc = 0; > struct smk_audit_info ad; > + struct user_namespace *ns = ns_of_current(); > struct inode *inode = file_inode(file); > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); > smk_ad_setfield_u_fs_path(&ad, file->f_path); > > if (_IOC_DIR(cmd) & _IOC_WRITE) { > - rc = smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, MAY_WRITE, &ad); > rc = smk_bu_file(file, MAY_WRITE, rc); > } > > if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ)) { > - rc = smk_curacc(smk_of_inode(inode), MAY_READ, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, MAY_READ, &ad); > rc = smk_bu_file(file, MAY_READ, rc); > } > > @@ -1600,11 +1696,12 @@ static int smack_file_lock(struct file *file, unsigned int cmd) > { > struct smk_audit_info ad; > int rc; > + struct user_namespace *ns = ns_of_current(); > struct inode *inode = file_inode(file); > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); > smk_ad_setfield_u_fs_path(&ad, file->f_path); > - rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, MAY_LOCK, &ad); > rc = smk_bu_file(file, MAY_LOCK, rc); > return rc; > } > @@ -1626,6 +1723,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, > { > struct smk_audit_info ad; > int rc = 0; > + struct user_namespace *ns = ns_of_current(); > struct inode *inode = file_inode(file); > > switch (cmd) { > @@ -1635,14 +1733,14 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, > case F_SETLKW: > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); > smk_ad_setfield_u_fs_path(&ad, file->f_path); > - rc = smk_curacc(smk_of_inode(inode), MAY_LOCK, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, MAY_LOCK, &ad); > rc = smk_bu_file(file, MAY_LOCK, rc); > break; > case F_SETOWN: > case F_SETSIG: > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); > smk_ad_setfield_u_fs_path(&ad, file->f_path); > - rc = smk_curacc(smk_of_inode(inode), MAY_WRITE, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, MAY_WRITE, &ad); > rc = smk_bu_file(file, MAY_WRITE, rc); > break; > default: > @@ -1672,6 +1770,7 @@ static int smack_mmap_file(struct file *file, > struct task_smack *tsp; > struct smack_known *okp; > struct inode_smack *isp; > + struct user_namespace *sns; > int may; > int mmay; > int tmay; > @@ -1682,12 +1781,16 @@ static int smack_mmap_file(struct file *file, > > tsp = current_security(); > skp = smk_of_task(tsp); > + sns = ns_of_current(); > isp = file_inode(file)->i_security; > mkp = isp->smk_mmap; > > if (mkp == NULL) > return 0; > > + if (!smk_labels_valid(skp, mkp, sns)) > + return -EACCES; > + > rc = 0; > > rcu_read_lock(); > @@ -1703,6 +1806,7 @@ static int smack_mmap_file(struct file *file, > */ > if (mkp->smk_known == okp->smk_known) > continue; > + > /* > * If there is a matching local rule take > * that into account as well. > @@ -1782,8 +1886,10 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, > struct fown_struct *fown, int signum) > { > struct smack_known *skp; > - struct smack_known *tkp = smk_of_task(tsk->cred->security); > + struct smack_known *tkp; > struct file *file; > + struct user_namespace *sns; > + struct user_namespace *tns; > int rc; > struct smk_audit_info ad; > > @@ -1791,12 +1897,17 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, > * struct fown_struct is never outside the context of a struct file > */ > file = container_of(fown, struct file, f_owner); > + skp = file->f_security; > + sns = file->f_cred->user_ns; > + > + tkp = smk_of_task_struct(tsk); > + tns = ns_of_task_struct(tsk); > > /* we don't log here as rc can be overriden */ > - skp = file->f_security; > - rc = smk_access(skp, tkp, MAY_WRITE, NULL); > + rc = smk_access(skp, tkp, sns, MAY_WRITE, NULL); > rc = smk_bu_note("sigiotask", skp, tkp, MAY_WRITE, rc); > - if (rc != 0 && smack_has_privilege(tsk, CAP_MAC_OVERRIDE)) > + if (rc != 0 && smk_labels_valid(skp, tkp, sns) > + && smack_has_ns_privilege(tsk, tns, CAP_MAC_OVERRIDE)) > rc = 0; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); > @@ -1816,6 +1927,7 @@ static int smack_file_receive(struct file *file) > int rc; > int may = 0; > struct smk_audit_info ad; > + struct user_namespace *ns = ns_of_current(); > struct inode *inode = file_inode(file); > > if (unlikely(IS_PRIVATE(inode))) > @@ -1831,7 +1943,7 @@ static int smack_file_receive(struct file *file) > if (file->f_mode & FMODE_WRITE) > may |= MAY_WRITE; > > - rc = smk_curacc(smk_of_inode(inode), may, &ad); > + rc = smk_curacc(smk_of_inode(inode), ns, may, &ad); > rc = smk_bu_file(file, may, rc); > return rc; > } > @@ -1851,16 +1963,19 @@ static int smack_file_receive(struct file *file) > static int smack_file_open(struct file *file, const struct cred *cred) > { > struct task_smack *tsp = cred->security; > + struct user_namespace *ns = cred->user_ns; > struct inode *inode = file_inode(file); > + struct inode_smack *isp = file_inode(file)->i_security; > struct smk_audit_info ad; > int rc; > > - if (smack_privileged(CAP_MAC_OVERRIDE)) > + if (smk_labels_valid(tsp->smk_task, isp->smk_inode, ns) && > + smack_ns_privileged(ns, CAP_MAC_OVERRIDE)) > return 0; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); > smk_ad_setfield_u_fs_path(&ad, file->f_path); > - rc = smk_access(tsp->smk_task, smk_of_inode(inode), MAY_READ, &ad); > + rc = smk_access(tsp->smk_task, smk_of_inode(inode), ns, MAY_READ, &ad); > rc = smk_bu_credfile(cred, file, MAY_READ, rc); > > return rc; > @@ -2015,12 +2130,13 @@ static int smk_curacc_on_task(struct task_struct *p, int access, > const char *caller) > { > struct smk_audit_info ad; > - struct smack_known *skp = smk_of_task_struct(p); > + struct smack_known *tkp = smk_of_task_struct(p); > + struct user_namespace *tns = ns_of_task_struct(p); > int rc; > > smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); > smk_ad_setfield_u_tsk(&ad, p); > - rc = smk_curacc(skp, access, &ad); > + rc = smk_curacc(tkp, tns, access, &ad); > rc = smk_bu_task(p, access, rc); > return rc; > } > @@ -2161,6 +2277,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, > struct smk_audit_info ad; > struct smack_known *skp; > struct smack_known *tkp = smk_of_task_struct(p); > + struct user_namespace *tns = ns_of_task_struct(p); > int rc; > > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); > @@ -2170,7 +2287,7 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, > * can write the receiver. > */ > if (secid == 0) { > - rc = smk_curacc(tkp, MAY_WRITE, &ad); > + rc = smk_curacc(tkp, tns, MAY_WRITE, &ad); > rc = smk_bu_task(p, MAY_WRITE, rc); > return rc; > } > @@ -2180,8 +2297,9 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, > * we can't take privilege into account. > */ > skp = smack_from_secid(secid); > - rc = smk_access(skp, tkp, MAY_WRITE, &ad); > + rc = smk_access(skp, tkp, tns, MAY_WRITE, &ad); > rc = smk_bu_note("USB signal", skp, tkp, MAY_WRITE, rc); > + > return rc; > } > > @@ -2236,6 +2354,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) > static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) > { > struct smack_known *skp = smk_of_current(); > + struct user_namespace *ns = ns_of_current(); > struct socket_smack *ssp; > > ssp = kzalloc(sizeof(struct socket_smack), gfp_flags); > @@ -2245,6 +2364,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) > ssp->smk_in = skp; > ssp->smk_out = skp; > ssp->smk_packet = NULL; > + ssp->smk_ns = get_user_ns(ns); > > sk->sk_security = ssp; > > @@ -2259,7 +2379,11 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) > */ > static void smack_sk_free_security(struct sock *sk) > { > + struct socket_smack *ssp = sk->sk_security; > + > + put_user_ns(ssp->smk_ns); > kfree(sk->sk_security); > + sk->sk_security = NULL; > } > > /** > @@ -2420,6 +2544,7 @@ static int smack_netlabel(struct sock *sk, int labeled) > static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) > { > struct smack_known *skp; > + struct user_namespace *sns; > int rc; > int sk_lbl; > struct smack_known *hkp; > @@ -2439,7 +2564,8 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) > #endif > sk_lbl = SMACK_UNLABELED_SOCKET; > skp = ssp->smk_out; > - rc = smk_access(skp, hkp, MAY_WRITE, &ad); > + sns = ssp->smk_ns; > + rc = smk_access(skp, hkp, sns, MAY_WRITE, &ad); > rc = smk_bu_note("IPv4 host check", skp, hkp, MAY_WRITE, rc); > } else { > sk_lbl = SMACK_CIPSO_SOCKET; > @@ -2464,6 +2590,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) > */ > static int smk_ipv6_check(struct smack_known *subject, > struct smack_known *object, > + struct user_namespace *ns, > struct sockaddr_in6 *address, int act) > { > #ifdef CONFIG_AUDIT > @@ -2481,7 +2608,7 @@ static int smk_ipv6_check(struct smack_known *subject, > else > ad.a.u.net->v6info.daddr = address->sin6_addr; > #endif > - rc = smk_access(subject, object, MAY_WRITE, &ad); > + rc = smk_access(subject, object, ns, MAY_WRITE, &ad); > rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc); > return rc; > } > @@ -2574,6 +2701,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, > struct smk_port_label *spp; > struct socket_smack *ssp = sk->sk_security; > struct smack_known *skp = NULL; > + struct user_namespace *sns = ssp->smk_ns; > unsigned short port; > struct smack_known *object; > > @@ -2617,7 +2745,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, > break; > } > > - return smk_ipv6_check(skp, object, address, act); > + return smk_ipv6_check(skp, object, sns, address, act); > } > #endif /* SMACK_IPV6_PORT_LABELING */ > > @@ -2640,12 +2768,13 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, > struct inode_smack *nsp = inode->i_security; > struct socket_smack *ssp; > struct socket *sock; > + struct user_namespace *ns = ns_of_current(); > int rc = 0; > > if (value == NULL || size > SMK_LONGLABEL || size == 0) > return -EINVAL; > > - skp = smk_get_label(value, size, true); > + skp = smk_get_label(value, size, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > > @@ -2765,6 +2894,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, > #ifdef SMACK_IPV6_SECMARK_LABELING > struct smack_known *rsp; > struct socket_smack *ssp = sock->sk->sk_security; > + struct user_namespace *sns = ssp->smk_ns; > #endif > > if (sock->sk == NULL) > @@ -2782,7 +2912,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, > #ifdef SMACK_IPV6_SECMARK_LABELING > rsp = smack_ipv6host_label(sip); > if (rsp != NULL) > - rc = smk_ipv6_check(ssp->smk_out, rsp, sip, > + rc = smk_ipv6_check(ssp->smk_out, rsp, sns, sip, > SMK_CONNECTING); > #endif > #ifdef SMACK_IPV6_PORT_LABELING > @@ -2839,14 +2969,14 @@ static void smack_msg_msg_free_security(struct msg_msg *msg) > } > > /** > - * smack_of_shm - the smack pointer for the shm > + * security_of_shm - the smack pointer for the shm > * @shp: the object > * > - * Returns a pointer to the smack value > + * Returns a pointer to the security_smack struct > */ > -static struct smack_known *smack_of_shm(struct shmid_kernel *shp) > +static struct ipc_smack *security_of_shm(struct shmid_kernel *shp) > { > - return (struct smack_known *)shp->shm_perm.security; > + return (struct ipc_smack *)shp->shm_perm.security; > } > > /** > @@ -2858,9 +2988,16 @@ static struct smack_known *smack_of_shm(struct shmid_kernel *shp) > static int smack_shm_alloc_security(struct shmid_kernel *shp) > { > struct kern_ipc_perm *isp = &shp->shm_perm; > - struct smack_known *skp = smk_of_current(); > + struct ipc_smack *ssp; > + > + ssp = kzalloc(sizeof(struct ipc_smack), GFP_KERNEL); > + if (ssp == NULL) > + return -ENOMEM; > + > + ssp->smk_known = smk_of_current(); > + ssp->smk_ns = get_user_ns(ns_of_current()); > > - isp->security = skp; > + isp->security = ssp; > return 0; > } > > @@ -2873,7 +3010,10 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp) > static void smack_shm_free_security(struct shmid_kernel *shp) > { > struct kern_ipc_perm *isp = &shp->shm_perm; > + struct ipc_smack *ssp = isp->security; > > + put_user_ns(ssp->smk_ns); > + kfree(isp->security); > isp->security = NULL; > } > > @@ -2886,7 +3026,7 @@ static void smack_shm_free_security(struct shmid_kernel *shp) > */ > static int smk_curacc_shm(struct shmid_kernel *shp, int access) > { > - struct smack_known *ssp = smack_of_shm(shp); > + struct ipc_smack *ssp = security_of_shm(shp); > struct smk_audit_info ad; > int rc; > > @@ -2894,8 +3034,8 @@ static int smk_curacc_shm(struct shmid_kernel *shp, int access) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); > ad.a.u.ipc_id = shp->shm_perm.id; > #endif > - rc = smk_curacc(ssp, access, &ad); > - rc = smk_bu_current("shm", ssp, access, rc); > + rc = smk_curacc(ssp->smk_known, ssp->smk_ns, access, &ad); > + rc = smk_bu_current("shm", ssp->smk_known, access, rc); > return rc; > } > > @@ -2966,14 +3106,14 @@ static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, > } > > /** > - * smack_of_sem - the smack pointer for the sem > + * security_of_sem - the smack pointer for the sem > * @sma: the object > * > - * Returns a pointer to the smack value > + * Returns a pointer to the ipc_smack struct > */ > -static struct smack_known *smack_of_sem(struct sem_array *sma) > +static struct ipc_smack *security_of_sem(struct sem_array *sma) > { > - return (struct smack_known *)sma->sem_perm.security; > + return (struct ipc_smack *)sma->sem_perm.security; > } > > /** > @@ -2985,9 +3125,16 @@ static struct smack_known *smack_of_sem(struct sem_array *sma) > static int smack_sem_alloc_security(struct sem_array *sma) > { > struct kern_ipc_perm *isp = &sma->sem_perm; > - struct smack_known *skp = smk_of_current(); > + struct ipc_smack *ssp; > + > + ssp = kzalloc(sizeof(struct ipc_smack), GFP_KERNEL); > + if (ssp == NULL) > + return -ENOMEM; > + > + ssp->smk_known = smk_of_current(); > + ssp->smk_ns = get_user_ns(ns_of_current()); > > - isp->security = skp; > + isp->security = ssp; > return 0; > } > > @@ -3000,7 +3147,10 @@ static int smack_sem_alloc_security(struct sem_array *sma) > static void smack_sem_free_security(struct sem_array *sma) > { > struct kern_ipc_perm *isp = &sma->sem_perm; > + struct ipc_smack *ssp = isp->security; > > + put_user_ns(ssp->smk_ns); > + kfree(isp->security); > isp->security = NULL; > } > > @@ -3013,7 +3163,7 @@ static void smack_sem_free_security(struct sem_array *sma) > */ > static int smk_curacc_sem(struct sem_array *sma, int access) > { > - struct smack_known *ssp = smack_of_sem(sma); > + struct ipc_smack *ssp = security_of_sem(sma); > struct smk_audit_info ad; > int rc; > > @@ -3021,8 +3171,8 @@ static int smk_curacc_sem(struct sem_array *sma, int access) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); > ad.a.u.ipc_id = sma->sem_perm.id; > #endif > - rc = smk_curacc(ssp, access, &ad); > - rc = smk_bu_current("sem", ssp, access, rc); > + rc = smk_curacc(ssp->smk_known, ssp->smk_ns, access, &ad); > + rc = smk_bu_current("sem", ssp->smk_known, access, rc); > return rc; > } > > @@ -3107,9 +3257,16 @@ static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, > static int smack_msg_queue_alloc_security(struct msg_queue *msq) > { > struct kern_ipc_perm *kisp = &msq->q_perm; > - struct smack_known *skp = smk_of_current(); > + struct ipc_smack *ssp; > + > + ssp = kzalloc(sizeof(struct ipc_smack), GFP_KERNEL); > + if (ssp == NULL) > + return -ENOMEM; > > - kisp->security = skp; > + ssp->smk_known = smk_of_current(); > + ssp->smk_ns = get_user_ns(ns_of_current()); > + > + kisp->security = ssp; > return 0; > } > > @@ -3122,19 +3279,22 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq) > static void smack_msg_queue_free_security(struct msg_queue *msq) > { > struct kern_ipc_perm *kisp = &msq->q_perm; > + struct ipc_smack *ssp = kisp->security; > > + put_user_ns(ssp->smk_ns); > + kfree(kisp->security); > kisp->security = NULL; > } > > /** > - * smack_of_msq - the smack pointer for the msq > + * security_of_msq - the smack pointer for the msq > * @msq: the object > * > - * Returns a pointer to the smack label entry > + * Returns a pointer to the ipc_smack struct > */ > -static struct smack_known *smack_of_msq(struct msg_queue *msq) > +static struct ipc_smack *security_of_msq(struct msg_queue *msq) > { > - return (struct smack_known *)msq->q_perm.security; > + return (struct ipc_smack *)msq->q_perm.security; > } > > /** > @@ -3146,7 +3306,7 @@ static struct smack_known *smack_of_msq(struct msg_queue *msq) > */ > static int smk_curacc_msq(struct msg_queue *msq, int access) > { > - struct smack_known *msp = smack_of_msq(msq); > + struct ipc_smack *msp = security_of_msq(msq); > struct smk_audit_info ad; > int rc; > > @@ -3154,8 +3314,8 @@ static int smk_curacc_msq(struct msg_queue *msq, int access) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); > ad.a.u.ipc_id = msq->q_perm.id; > #endif > - rc = smk_curacc(msp, access, &ad); > - rc = smk_bu_current("msq", msp, access, rc); > + rc = smk_curacc(msp->smk_known, msp->smk_ns, access, &ad); > + rc = smk_bu_current("msq", msp->smk_known, access, rc); > return rc; > } > > @@ -3249,7 +3409,7 @@ static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, > */ > static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) > { > - struct smack_known *iskp = ipp->security; > + struct ipc_smack *isp = ipp->security; > int may = smack_flags_to_may(flag); > struct smk_audit_info ad; > int rc; > @@ -3258,8 +3418,8 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) > smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); > ad.a.u.ipc_id = ipp->id; > #endif > - rc = smk_curacc(iskp, may, &ad); > - rc = smk_bu_current("svipc", iskp, may, rc); > + rc = smk_curacc(isp->smk_known, isp->smk_ns, may, &ad); > + rc = smk_bu_current("svipc", isp->smk_known, may, rc); > return rc; > } > > @@ -3270,9 +3430,9 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag) > */ > static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) > { > - struct smack_known *iskp = ipp->security; > + struct ipc_smack *iskp = ipp->security; > > - *secid = iskp->smk_secid; > + *secid = iskp->smk_known->smk_secid; > } > > /** > @@ -3530,13 +3690,14 @@ int smack_getprocattr_seq(struct task_struct *p, const char *name, > static int smack_getprocattr(struct task_struct *p, char *name, char **value) > { > struct smack_known *skp = smk_of_task_struct(p); > + struct user_namespace *ns = ns_of_current(); > char *cp; > int slen; > > if (strcmp(name, "current") != 0) > return -EINVAL; > > - cp = smk_find_label_name(skp); > + cp = smk_find_label_name(skp, ns); > if (cp == NULL) > cp = smack_known_huh.smk_known; > cp = kstrdup(cp, GFP_KERNEL); > @@ -3564,6 +3725,7 @@ static int proc_current_write(struct task_struct *p, void *value, size_t size) > struct task_smack *tsp; > struct cred *new; > struct smack_known *skp; > + struct user_namespace *ns; > > /* > * Changing another process' Smack value is too dangerous > @@ -3572,13 +3734,15 @@ static int proc_current_write(struct task_struct *p, void *value, size_t size) > if (p != current) > return -EPERM; > > - if (!smack_privileged(CAP_MAC_ADMIN)) > + ns = ns_of_current(); > + > + if (!smack_ns_privileged(ns, CAP_MAC_ADMIN)) > return -EPERM; > > if (value == NULL || size == 0 || size >= SMK_LONGLABEL) > return -EINVAL; > > - skp = smk_get_label(value, size, true); > + skp = smk_get_label(value, size, true, ns); > if (IS_ERR(skp)) > return PTR_ERR(skp); > > @@ -3645,23 +3809,27 @@ static int smack_unix_stream_connect(struct sock *sock, > struct smack_known *okp_out = osp->smk_out; > struct smack_known *skp_in = ssp->smk_in; > struct smack_known *okp_in = osp->smk_in; > + struct user_namespace *sns = ssp->smk_ns; > + struct user_namespace *ons = osp->smk_ns; > struct smk_audit_info ad; > int rc = 0; > #ifdef CONFIG_AUDIT > struct lsm_network_audit net; > #endif > > - if (!smack_privileged(CAP_MAC_OVERRIDE)) { > + if (!smack_ns_privileged(ons, CAP_MAC_OVERRIDE) || > + !smk_labels_valid(skp_out, okp_in, sns) || > + !smk_labels_valid(okp_out, skp_in, ons)) { > #ifdef CONFIG_AUDIT > smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); > smk_ad_setfield_u_net_sk(&ad, other); > #endif > - rc = smk_access(skp_out, okp_in, MAY_WRITE, &ad); > + rc = smk_access(skp_out, okp_in, sns, MAY_WRITE, &ad); > rc = smk_bu_note("UDS connect", skp_out, okp_in, MAY_WRITE, rc); > if (rc == 0) { > - rc = smk_access(okp_out, skp_in, MAY_WRITE, &ad); > + rc = smk_access(okp_out, skp_in, ons, MAY_WRITE, &ad); > rc = smk_bu_note("UDS connect", okp_out, skp_in, > - MAY_WRITE, rc); > + MAY_WRITE, rc); > } > } > > @@ -3688,6 +3856,8 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) > { > struct socket_smack *ssp = sock->sk->sk_security; > struct socket_smack *osp = other->sk->sk_security; > + struct user_namespace *sns = ssp->smk_ns; > + struct user_namespace *ons = osp->smk_ns; > struct smk_audit_info ad; > int rc; > > @@ -3698,10 +3868,11 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) > smk_ad_setfield_u_net_sk(&ad, other->sk); > #endif > > - if (smack_privileged(CAP_MAC_OVERRIDE)) > + if (smk_labels_valid(ssp->smk_out, osp->smk_in, sns) && > + smack_ns_privileged(ons, CAP_MAC_OVERRIDE)) > return 0; > > - rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); > + rc = smk_access(ssp->smk_out, osp->smk_in, sns, MAY_WRITE, &ad); > rc = smk_bu_note("UDS send", ssp->smk_out, osp->smk_in, MAY_WRITE, rc); > return rc; > } > @@ -3724,8 +3895,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, > struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name; > #endif > #ifdef SMACK_IPV6_SECMARK_LABELING > - struct socket_smack *ssp = sock->sk->sk_security; > struct smack_known *rsp; > + struct socket_smack *ssp = sock->sk->sk_security; > + struct user_namespace *sns = ssp->smk_ns; > #endif > int rc = 0; > > @@ -3743,7 +3915,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, > #ifdef SMACK_IPV6_SECMARK_LABELING > rsp = smack_ipv6host_label(sap); > if (rsp != NULL) > - rc = smk_ipv6_check(ssp->smk_out, rsp, sap, > + rc = smk_ipv6_check(ssp->smk_out, rsp, sns, sap, > SMK_CONNECTING); > #endif > #ifdef SMACK_IPV6_PORT_LABELING > @@ -3951,7 +4123,7 @@ access_check: > * This is the simplist possible security model > * for networking. > */ > - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); > + rc = smk_access(skp, ssp->smk_in, ssp->smk_ns, MAY_WRITE, &ad); > rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in, > MAY_WRITE, rc); > if (rc != 0) > @@ -3975,7 +4147,7 @@ access_check: > ad.a.u.net->netif = skb->skb_iif; > ipv6_skb_to_auditdata(skb, &ad.a, NULL); > #endif /* CONFIG_AUDIT */ > - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); > + rc = smk_access(skp, ssp->smk_in, ssp->smk_ns, MAY_WRITE, &ad); > rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in, > MAY_WRITE, rc); > #endif /* SMACK_IPV6_SECMARK_LABELING */ > @@ -4187,7 +4359,7 @@ access_check: > * Receiving a packet requires that the other end be able to write > * here. Read access is not required. > */ > - rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); > + rc = smk_access(skp, ssp->smk_in, ssp->smk_ns, MAY_WRITE, &ad); > rc = smk_bu_note("IPv4 connect", skp, ssp->smk_in, MAY_WRITE, rc); > if (rc != 0) > return rc; > @@ -4291,6 +4463,7 @@ static int smack_key_permission(key_ref_t key_ref, > struct key *keyp; > struct smk_audit_info ad; > struct smack_known *tkp = smk_of_task(cred->security); > + struct user_namespace *tns = cred->user_ns; > int request = 0; > int rc; > > @@ -4317,7 +4490,7 @@ static int smack_key_permission(key_ref_t key_ref, > request = MAY_READ; > if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) > request = MAY_WRITE; > - rc = smk_access(tkp, keyp->security, request, &ad); > + rc = smk_access(tkp, keyp->security, tns, request, &ad); > rc = smk_bu_note("key access", tkp, keyp->security, request, rc); > return rc; > } > @@ -4334,6 +4507,7 @@ static int smack_key_permission(key_ref_t key_ref, > static int smack_key_getsecurity(struct key *key, char **_buffer) > { > struct smack_known *skp = key->security; > + struct user_namespace *ns = ns_of_current(); > size_t length; > char *copy; > > @@ -4342,7 +4516,7 @@ static int smack_key_getsecurity(struct key *key, char **_buffer) > return 0; > } > > - copy = smk_find_label_name(skp); > + copy = smk_find_label_name(skp, ns); > if (copy == NULL) > copy = smack_known_huh.smk_known; > copy = kstrdup(copy, GFP_KERNEL); > @@ -4520,6 +4694,11 @@ static inline void smack_userns_free(struct user_namespace *ns) > static inline int smack_userns_setns(struct nsproxy *nsproxy, > struct user_namespace *ns) > { > + struct smack_known *skp = smk_of_current(); > + > + if (smk_find_mapped(skp, ns) == NULL) > + return -EACCES; > + > return 0; > } > > @@ -4632,6 +4811,7 @@ static struct security_hook_list smack_hooks[] = { > LSM_HOOK_INIT(inode_setattr, smack_inode_setattr), > LSM_HOOK_INIT(inode_getattr, smack_inode_getattr), > LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr), > + LSM_HOOK_INIT(inode_pre_setxattr, smack_inode_pre_setxattr), > LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr), > LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr), > LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr), > diff --git a/security/smack/smack_ns.c b/security/smack/smack_ns.c > index 49223c4..dc2a666 100644 > --- a/security/smack/smack_ns.c > +++ b/security/smack/smack_ns.c > @@ -206,6 +206,45 @@ unlockout: > return sknp; > } > > +/** > + * smk_labels_valid - A helper to check whether labels are valid/mapped > + * in the namespace and can be used there > + * @sbj: a subject label to be checked > + * @obj: an object label to be checked > + * @ns: user namespace to check against (usually subject's) > + * > + * Returns true if both valid/mapped, false otherwise. > + * This helper is mostly used while checking capabilities. > + * The access functions check the validity of labels by themselves. > + */ > +bool smk_labels_valid(struct smack_known *sbj, struct smack_known *obj, > + struct user_namespace *ns) > +{ > + struct user_namespace *user_ns; > + > + /* > + * labels are always valid if there is no map > + * (init_user_ns or unmapped descendants) > + */ > + user_ns = smk_find_mapped_ns(ns); > + if (user_ns == NULL) > + return true; > + > + /* > + * If we have a map though, both labels need to be mapped. > + */ > + if (__smk_find_mapped(sbj, user_ns) == NULL) > + return false; > + if (__smk_find_mapped(obj, user_ns) == NULL) > + return false; > + > + return true; > +} > + > +/* > + * proc mapping operations > + */ > + > static void *proc_label_map_seq_start(struct seq_file *seq, loff_t *pos) > { > struct smack_known *skp; > diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c > index 3149ec0..fe4ad24 100644 > --- a/security/smack/smackfs.c > +++ b/security/smack/smackfs.c > @@ -340,13 +340,15 @@ static int smk_fill_rule(const char *subject, const char *object, > struct smack_parsed_rule *rule, int import, > int len) > { > - rule->smk_subject = smk_get_label(subject, len, import); > + struct user_namespace *ns = ns_of_current(); > + > + rule->smk_subject = smk_get_label(subject, len, import, ns); > if (IS_ERR(rule->smk_subject)) > return PTR_ERR(rule->smk_subject); > if (rule->smk_subject == NULL) > return -ENOENT; > > - rule->smk_object = smk_get_label(object, len, import); > + rule->smk_object = smk_get_label(object, len, import, ns); > if (IS_ERR(rule->smk_object)) > return PTR_ERR(rule->smk_object); > if (rule->smk_object == NULL) > @@ -573,6 +575,7 @@ static void smk_seq_stop(struct seq_file *s, void *v) > > static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) > { > + struct user_namespace *ns = ns_of_current(); > char *sbj; > char *obj; > > @@ -581,6 +584,7 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) > * interface file (/smack/load or /smack/load2) > * because you should expect to be able to write > * anything you read back. > + * Show only fully mapped rules in a namespace (both labels mapped). > */ > if (strlen(srp->smk_subject->smk_known) >= max || > strlen(srp->smk_object->smk_known) >= max) > @@ -589,8 +593,8 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) > if (srp->smk_access == 0) > return; > > - sbj = smk_find_label_name(srp->smk_subject); > - obj = smk_find_label_name(srp->smk_object); > + sbj = smk_find_label_name(srp->smk_subject, ns); > + obj = smk_find_label_name(srp->smk_object, ns); > > if (sbj == NULL || obj == NULL) > return; > @@ -785,6 +789,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) > struct smack_known *skp = > list_entry_rcu(list, struct smack_known, list); > struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; > + struct user_namespace *ns = ns_of_current(); > char sep = '/'; > char *cp; > int i; > @@ -800,7 +805,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) > if (strlen(skp->smk_known) >= SMK_LABELLEN) > return 0; > > - cp = smk_find_label_name(skp); > + cp = smk_find_label_name(skp, ns); > if (cp == NULL) > return 0; > > @@ -853,6 +858,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, > { > struct smack_known *skp; > struct netlbl_lsm_secattr ncats; > + struct user_namespace *ns = ns_of_current(); > char mapcatset[SMK_CIPSOLEN]; > int maplevel; > unsigned int cat; > @@ -893,7 +899,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, > */ > mutex_lock(&smack_cipso_lock); > > - skp = smk_get_label(rule, 0, true); > + skp = smk_get_label(rule, 0, true, ns); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto out; > @@ -981,11 +987,12 @@ static int cipso2_seq_show(struct seq_file *s, void *v) > struct smack_known *skp = > list_entry_rcu(list, struct smack_known, list); > struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; > + struct user_namespace *ns = ns_of_current(); > char sep = '/'; > char *cp; > int i; > > - cp = smk_find_label_name(skp); > + cp = smk_find_label_name(skp, ns); > if (cp == NULL) > return 0; > > @@ -1066,12 +1073,13 @@ static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos) > static int net4addr_seq_show(struct seq_file *s, void *v) > { > struct list_head *list = v; > + struct user_namespace *ns = ns_of_current(); > struct smk_net4addr *skp = > list_entry_rcu(list, struct smk_net4addr, list); > char *kp = SMACK_CIPSO_OPTION; > > if (skp->smk_label != NULL) { > - kp = smk_find_label_name(skp->smk_label); > + kp = smk_find_label_name(skp->smk_label, ns); > if (kp == NULL) > kp = smack_known_huh.smk_known; > } > @@ -1167,6 +1175,7 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, > int rc; > struct netlbl_audit audit_info; > struct in_addr mask; > + struct user_namespace *ns = ns_of_current(); > unsigned int m; > unsigned int masks; > int found; > @@ -1226,7 +1235,7 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf, > * If smack begins with '-', it is an option, don't import it > */ > if (smack[0] != '-') { > - skp = smk_get_label(smack, 0, true); > + skp = smk_get_label(smack, 0, true, ns); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto free_out; > @@ -1345,10 +1354,11 @@ static int net6addr_seq_show(struct seq_file *s, void *v) > struct list_head *list = v; > struct smk_net6addr *skp = > list_entry(list, struct smk_net6addr, list); > + struct user_namespace *ns = ns_of_current(); > char *kp; > > if (skp->smk_label != NULL) { > - kp = smk_find_label_name(skp->smk_label); > + kp = smk_find_label_name(skp->smk_label, ns); > if (kp == NULL) > kp = smack_known_huh.smk_known; > > @@ -1438,6 +1448,7 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, > struct in6_addr newname; > struct in6_addr fullmask; > struct smack_known *skp = NULL; > + struct user_namespace *ns = ns_of_current(); > char *smack; > char *data; > int rc = 0; > @@ -1508,7 +1519,7 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, > * If smack begins with '-', it is an option, don't import it > */ > if (smack[0] != '-') { > - skp = smk_get_label(smack, 0, true); > + skp = smk_get_label(smack, 0, true, ns); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto free_out; > @@ -1827,6 +1838,7 @@ static const struct file_operations smk_mapped_ops = { > static ssize_t smk_read_ambient(struct file *filp, char __user *buf, > size_t cn, loff_t *ppos) > { > + struct user_namespace *ns = ns_of_current(); > ssize_t rc = -EINVAL; > char *cp; > int asize; > @@ -1839,7 +1851,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, > */ > mutex_lock(&smack_ambient_lock); > > - cp = smk_find_label_name(smack_net_ambient); > + cp = smk_find_label_name(smack_net_ambient, ns); > if (cp == NULL) > cp = smack_known_huh.smk_known; > > @@ -1866,6 +1878,7 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, > size_t count, loff_t *ppos) > { > struct smack_known *skp; > + struct user_namespace *ns = ns_of_current(); > char *oldambient; > char *data; > int rc = count; > @@ -1882,7 +1895,7 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, > goto out; > } > > - skp = smk_get_label(data, count, true); > + skp = smk_get_label(data, count, true, ns); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto out; > @@ -1923,11 +1936,12 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) > static int onlycap_seq_show(struct seq_file *s, void *v) > { > char *smack; > + struct user_namespace *ns = ns_of_current(); > struct list_head *list = v; > struct smack_onlycap *sop = > list_entry_rcu(list, struct smack_onlycap, list); > > - smack = smk_find_label_name(sop->smk_label); > + smack = smk_find_label_name(sop->smk_label, ns); > if (smack == NULL) > smack = smack_known_huh.smk_known; > > @@ -2006,6 +2020,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, > struct smack_onlycap *sop; > struct smack_onlycap *sop2; > LIST_HEAD(list_tmp); > + struct user_namespace *ns = ns_of_current(); > int rc = count; > > if (!smack_privileged(CAP_MAC_ADMIN)) > @@ -2025,7 +2040,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, > if (!*tok) > continue; > > - skp = smk_get_label(tok, 0, true); > + skp = smk_get_label(tok, 0, true, ns); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > break; > @@ -2091,12 +2106,13 @@ static ssize_t smk_read_unconfined(struct file *filp, char __user *buf, > char *smack = ""; > ssize_t rc = -EINVAL; > int asize; > + struct user_namespace *ns = ns_of_current(); > > if (*ppos != 0) > return 0; > > if (smack_unconfined != NULL) { > - smack = smk_find_label_name(smack_unconfined); > + smack = smk_find_label_name(smack_unconfined, ns); > if (smack == NULL) > smack = smack_known_huh.smk_known; > } > @@ -2123,6 +2139,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, > { > char *data; > struct smack_known *skp; > + struct user_namespace *ns = ns_of_current(); > int rc = count; > > if (!smack_privileged(CAP_MAC_ADMIN)) > @@ -2146,7 +2163,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, > * > * But do so only on invalid label, not on system errors. > */ > - skp = smk_get_label(data, count, true); > + skp = smk_get_label(data, count, true, ns); > if (PTR_ERR(skp) == -EINVAL) > skp = NULL; > else if (IS_ERR(skp)) { > @@ -2318,6 +2335,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, > size_t count, loff_t *ppos, int format) > { > struct smack_parsed_rule rule; > + struct user_namespace *ns = ns_of_current(); > char *data; > int res; > > @@ -2337,7 +2355,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, > } > > if (res >= 0) > - res = smk_access(rule.smk_subject, rule.smk_object, > + res = smk_access(rule.smk_subject, rule.smk_object, ns, > rule.smk_access1, NULL); > else if (res != -ENOENT) > return res; > @@ -2547,6 +2565,7 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, > struct smack_rule *sp; > struct list_head *rule_list; > struct mutex *rule_lock; > + struct user_namespace *ns = ns_of_current(); > int rc = count; > > if (*ppos != 0) > @@ -2567,7 +2586,7 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, > goto out_data; > } > > - skp = smk_get_label(data, count, false); > + skp = smk_get_label(data, count, false, ns); > if (IS_ERR(skp)) { > rc = PTR_ERR(skp); > goto out_data; > @@ -2649,12 +2668,13 @@ static ssize_t smk_read_syslog(struct file *filp, char __user *buf, > char *smack = ""; > ssize_t rc = -EINVAL; > int asize; > + struct user_namespace *ns = ns_of_current(); > > if (*ppos != 0) > return 0; > > if (smack_syslog_label != NULL) { > - smack = smk_find_label_name(smack_syslog_label); > + smack = smk_find_label_name(smack_syslog_label, ns); > if (smack == NULL) > smack = smack_known_huh.smk_known; > } > @@ -2681,6 +2701,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, > { > char *data; > struct smack_known *skp; > + struct user_namespace *ns = ns_of_current(); > int rc = count; > > if (!smack_privileged(CAP_MAC_ADMIN)) > @@ -2704,7 +2725,7 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf, > * > * But do so only on invalid label, not on system errors. > */ > - skp = smk_get_label(data, count, true); > + skp = smk_get_label(data, count, true, ns); > if (PTR_ERR(skp) == -EINVAL) > skp = NULL; > else if (IS_ERR(skp)) { From casey at schaufler-ca.com Thu Oct 29 22:52:20 2015 From: casey at schaufler-ca.com (Casey Schaufler) Date: Thu, 29 Oct 2015 15:52:20 -0700 Subject: [PATCH v4 11/11] smack: documentation for the Smack namespace In-Reply-To: <1444826525-9758-12-git-send-email-l.pawelczyk@samsung.com> References: <1444826525-9758-1-git-send-email-l.pawelczyk@samsung.com> <1444826525-9758-12-git-send-email-l.pawelczyk@samsung.com> Message-ID: <5632A324.7000802@schaufler-ca.com> On 10/14/2015 5:42 AM, Lukasz Pawelczyk wrote: > Adds Documentation/smack-namespace.txt. > > Signed-off-by: Lukasz Pawelczyk > Reviewed-by: Casey Schaufler Acked-by: Casey Schaufler > --- > Documentation/security/00-INDEX | 2 + > Documentation/security/Smack-namespace.txt | 231 +++++++++++++++++++++++++++++ > MAINTAINERS | 1 + > security/smack/Kconfig | 2 + > 4 files changed, 236 insertions(+) > create mode 100644 Documentation/security/Smack-namespace.txt > > diff --git a/Documentation/security/00-INDEX b/Documentation/security/00-INDEX > index 45c82fd..c03a220 100644 > --- a/Documentation/security/00-INDEX > +++ b/Documentation/security/00-INDEX > @@ -6,6 +6,8 @@ SELinux.txt > - how to get started with the SELinux security enhancement. > Smack.txt > - documentation on the Smack Linux Security Module. > +Smack-namespace.txt > + - documentation on the Smack namespace implementation. > Yama.txt > - documentation on the Yama Linux Security Module. > apparmor.txt > diff --git a/Documentation/security/Smack-namespace.txt b/Documentation/security/Smack-namespace.txt > new file mode 100644 > index 0000000..5304355 > --- /dev/null > +++ b/Documentation/security/Smack-namespace.txt > @@ -0,0 +1,231 @@ > + > + "Quis custodiet ipsos custodes?" > + - Satires of Juvenal > + > + > +--- What is a Smack namespace --- > + > +Smack namespace was developed to make it possible for Smack to work > +nicely with Linux containers where there is a full operating system > +with its own init inside the namespace. Such a system working with > +Smack expects to have at least partially working SMACK_MAC_ADMIN to be > +able to change labels of processes and files. This is required to be > +able to securely start applications under the control of Smack and > +manage their access rights. > + > +It was implemented using new LSM hooks added to the user namespace > +that were developed together with Smack namespace. > + > + > +--- Design ideas --- > + > +"Smack namespace" is rather "Smack labels namespace" as not the whole > +MAC is namespaced, only the labels. There is a great analogy between > +Smack labels namespace and the user namespace part that remaps UIDs. > + > +The idea is to create a map of labels for a namespace so the namespace > +is only allowed to use those labels. Smack rules are always the same > +as in the init namespace (limited only by what labels are mapped) and > +cannot be manipulated from the child namespace. The map is actually > +only for labels' names. The underlying structures for labels remain > +the same. The filesystem also stores the "unmapped" labels from the > +init namespace. > + > +Let's say we have those labels in the init namespace: > +label1 > +label2 > +label3 > + > +and those rules: > +label1 label2 rwx > +label1 label3 rwx > +label2 label3 rwx > + > +We create a map for a namespace: > +label1 -> mapped1 > +label2 -> mapped2 > + > +This means that 'label3' is completely invisible in the namespace. As if > +it didn't exist. All the rules that include it are ignored. > + > +Effectively in the namespace we have only one rule: > +mapped1 mapped2 rwx > + > +Which in reality is: > +label1 label2 rwx > + > +All requests to access an object with a 'label3' will be denied. If it > +ever comes to a situation where 'label3' would have to be printed > +(e.g. reading an exec or mmap label from a file to which we have > +access) then huh sign '?' will be printed instead. > + > +All the operations in the namespace on the remaining labels will have > +to be performed using their mapped names. Things like changing own > +process's label, changing filesystem label. Labels will also be > +printed with their mapped names. > + > +You cannot import new labels in a namespace. Every operation that > +would do so in an init namespace will return an error in the child > +namespace. You cannot assign an unmapped or not existing label to an > +object. You can only operate on labels that have been explicitly > +mapped. > + > + > +--- Capabilities --- > + > +Enabling Smack related capabilities (CAP_MAC_ADMIN and > +CAP_MAC_OVERRIDE) is main goal of Smack namespace, so it can work > +properly in the container. And those capabilities do work to some > +extent. In several places where capabilities are checked compatibility > +with Smack namespace has been introduced. Capabilities are of course > +limited to operate only on mapped labels. > + > +CAP_MAC_OVERRIDE works fully, will allow you to ignore Smack access > +rules, but only between objects that have labels mapped. So in the > +example above having this CAP will allow e.g. label2 to write to > +label1, but will not allow any access to label3. > + > +With CAP_MAC_ADMIN the following operations has been allowed inside > +the namespace: > +- setting and removing xattr on files, including the security.* ones > +- setting process's own label (/proc/self/attr/current) > +- mounting in a privileged Smack mode, which means one can specify > + additional mount options like: smackfsdef, smackfsfloor etc. > + > +Again this is also allowed only on the mapped labels. Labels on the > +filesystem will be stored in unmapped form so they are preserved > +through reboots. > + > +Such a namespace construct allows e.g. systemd (with Smack support) > +working in a container to assign labels properly to daemons and other > +processes. > + > + > +--- Usage --- > + > +Smack namespace is written using LSM hooks inside user namespace. That > +means it's connected to it. > + > +To create a new Smack namespace you need to unshare() user namespace > +as usual. If that is all you do though, than there is no difference to > +what is now. To activate the Smack namespace you need to fill the > +labels' map. It is in a file /proc/$PID/attr/label_map. > + > +By default the map is empty and Smack namespaces are inactive (labels > +are taken directly from a parent namespace). It also means that the > +Smack capabilities will be inactive. After you fill the map it starts > +to take effect in the namespace and Smack capabilities (only on mapped > +labels) start to work. > + > +Due to the way Smack works only CAP_MAC_ADMIN from the parent > +namespace (init_user_ns for now, see the "Current limitations" below) > +is allowed to fill the map. That means that an unprivileged user is > +still allowed to create the user namespace but it will not be able to > +fill the labels' map (activate Smack namespace). An administrator > +intervention is required. > + > +The attr_map write format is: > +unmapped_label mapped_label > + > +When reading the file it shows an active map for a namespace the > +process in question is in in the format: > +unmapped_label -> mapped_label > + > +If the label_map file is empty it means the namespace is not mapped > +and Smack namespace is inactive (no mappings, MAC related capabilities > +behave as they did before, meaning they are active only in > +init_user_ns). For init_user_ns the map will always be empty. > + > +Writing to the map file is not disabled after the first write as it is > +in uid_map. For Smack we have no means to map ranges of labels, hence > +it can really be advantageous to be able to expand the map later > +on. But you can only add to the map. You cannot remove already mapped > +labels. You cannot change the already existing mappings. Also mappings > +has to be 1-1. All requests to create a map where either the unmapped > +or the mapped label already exists in the map will be denied. > + > +setns() with Smack namespace active has an additional check that the > +label of a process that is calling setns() has to be already mapped in > +the target Smack namespace for the call to succeed. > + > + > +--- Special labels --- > + > +Smack is using some special labels that have built-in rules. Things > +like floor '_', dash '^', star '*', etc. Those labels are not > +automatically mapped to the namespace. Moreover, you can choose to map > +a different label from the init namespace to behave e.g. like floor > +inside the namespace. > + > +Let's say we have no rules and those labels in the init namespace: > +_ > +floor_to_be > +label > + > +Both 'label' and 'floor_to_be' can read objects with '_'. But they > +have no access rights to each other. > + > +Now let's create a map like this: > +_ ordinary_label > +floor_to_be _ > +label mapped > + > +Right now label 'mapped' can read label '_' which means that > +effectively inside this namespace label 'label' has gained read access > +to the 'floor_to_be'. The label 'ordinary_label' is exactly it, an > +ordinary label that the built-in rules no longer apply to inside the > +namespace. > + > +To sum up, special labels in the namespace behave the same as in the > +init namespace. Not the original special labels though, but the ones > +we map to specials. This is the only case where a namespace can have > +access rights the init namespace does not have (like the 'label' to > +'floor_to_be' in the example above). > + > +Of course mappings like these are perfectly legal: > +_ _ > +* * > +^ ^ > + > + > +--- Current limitations --- > + > +The Smack namespace is not hierarchical yet. It is currently not > +possible to fill a label_map of a nested user namespace (you can still > +create nested user namespace, it will just inherit its parent's map > +and won't have active Smack capabilities). When hierarchy will be > +implemented the process creating another namespace will be allowed to > +map only labels that it has permission to itself (those that it has in > +its own map). > + > +Special files inside the virtual smackfs needs to be reviewed whether > +it's beneficial to have some of their functionality namespaced as well > +(e.g. onlycap, syslog. ambient, etc). This would increase > +CAP_MAC_ADMIN privileges inside the namespace. > + > + > +--- Error codes --- > + > +While working in the namespace patches the error codes has been made > +to propagate properly from a place they occurred. New error codes has > +also been introduced for Smack in the context of namespace usage. This > +is a complete summary of error codes used throughout the Smack now: > + > +ENOMEM and other system errors that might come from low level > + kernel functions like memory allocations > +EOPNOTSUPP means the underlying system operation is not > + supported (eg. getxattr) > +EINVAL means invalid syntax (e.g. empty label or one starting > + with '-') > +EEXIST when creating map means that a label is already mapped > +EBADR is used for wrong namespace usage: > + - trying to import a label inside a namespace (like trying > + to use an unmapped label that would otherwise be imported) > + - trying to create a Smack label map in the init namespace > +ENOENT when failed to find a label we expected to exist (will not > + be propagated to user-space) > +EPERM means no permission to operate on an object, e.g. due to > + insufficient capabilities or simply because the object > + cannot be operated on in the current context > +EACCESS when access has been denied due to Smack access checks > + (including object being outside of a namespace) > diff --git a/MAINTAINERS b/MAINTAINERS > index 797236b..c77be5a 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -9590,6 +9590,7 @@ W: http://schaufler-ca.com > T: git git://git.gitorious.org/smack-next/kernel.git > S: Maintained > F: Documentation/security/Smack.txt > +F: Documentation/security/Smack-namespace.txt > F: security/smack/ > > DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS) > diff --git a/security/smack/Kconfig b/security/smack/Kconfig > index b19a7fb..a6e0f3f 100644 > --- a/security/smack/Kconfig > +++ b/security/smack/Kconfig > @@ -49,4 +49,6 @@ config SECURITY_SMACK_NS > This enables Smack namespace that makes it possible to map > specific labels within user namespace (analogously to mapping > UIDs) and to gain MAC capabilities over them. > + Documentation is availabile here: > + Documentation/security/Smack-namespace.txt > If you are unsure how to answer this question, answer N.