From patchwork Thu Mar 23 16:48:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Carter X-Patchwork-Id: 9641623 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D9DAD602CA for ; Thu, 23 Mar 2017 16:52:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C4B0E1FF60 for ; Thu, 23 Mar 2017 16:52:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B8B6A283FD; Thu, 23 Mar 2017 16:52:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from emsm-gh1-uea11.nsa.gov (emsm-gh1-uea11.nsa.gov [8.44.101.9]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 178FD27F90 for ; Thu, 23 Mar 2017 16:52:14 +0000 (UTC) X-IronPort-AV: E=Sophos;i="5.36,210,1486425600"; d="scan'208";a="4246730" IronPort-PHdr: =?us-ascii?q?9a23=3Ao38N1BebBN2gZW7wiMNxDQoblGMj4u6mDksu8pMi?= =?us-ascii?q?zoh2WeGdxcq4YxeN2/xhgRfzUJnB7Loc0qyN4v2mBjdLsMzJmUtBWaQEbwUCh8?= =?us-ascii?q?QSkl5oK+++Imq/EsTXaTcnFt9JTl5v8iLzG0FUHMHjew+a+SXqvnYdFRrlKAV6?= =?us-ascii?q?OPn+FJLMgMSrzeCy/IDYbxlViDanb75/KBW7oR/Tu8QYjodvLrg9wQbVr3VVfO?= =?us-ascii?q?hb2XlmLk+JkRbm4cew8p9j8yBOtP8k6sVNT6b0cbkmQLJBFDgpPHw768PttRnY?= =?us-ascii?q?UAuA/WAcXXkMkhpJGAfK8hf3VYrsvyTgt+p93C6aPdDqTb0xRD+v4btnRAPuhS?= =?us-ascii?q?waOTE56mXXgdFugqxdrhyquhhzz5fKbI2JMfZzeL7Wc9EHSmpbRstfVzJPDJ6y?= =?us-ascii?q?YYUMCOQOP+hYr5H/qlcToxaxChWjCuzxxT9TnXL2wa833v49HQzcwAAsAdQDu2?= =?us-ascii?q?nUotXvM6cSVPi4wq3SwjXGcfxWwir25ZbSfRA7vfGDQ6hwetfWxEktGQLKkk+f?= =?us-ascii?q?qZb5Pz+LzekNr2ia4PZgVeKokWInrRpxrSK1yscjjYnJmJsYx1bZ/it3x4Y1IM?= =?us-ascii?q?e3SE99YdO8FZtfrTmaO5F3QsMkRWxjpSU0yqUetJKmcyUHx44ryh7CZ/CdbYSF?= =?us-ascii?q?7QzvWPyMLTp+mXlrYqiwhwyo/kil0uD8U86030tUoSddidnMs2wN1wTU6siaVv?= =?us-ascii?q?tx5keh1iiL1wDU8uxEPVo7lbDaK5482b48jIYTsETfES/2n0X6lqmWeVk/+ue0?= =?us-ascii?q?8ejnZ7TmppuEO491jAHxLLgul9SiDek3PQUCRWiW9fmm2LH98kD1Xq9GguAunq?= =?us-ascii?q?ncqp/aJMAbpqCjAw9S14Yu8w2/ACmi0NQZm3kHMV1EdAucgIjuJlHOJuz3Aumk?= =?us-ascii?q?g1m3jDdqx+zJPr3mApnXKHjDi63ucaxy605b1go/1cpf6I5MCrEdPPLzXVf8tN?= =?us-ascii?q?7fDh8/KQO03+bnB8971o4FW2KPBamZMKbIvl+T+uIgPfOMZJQPtDb6Mfgl6Obk?= =?us-ascii?q?jWUlll8FYampwZwXZWi6HvRkOUqWfXnsgswGEWcMvwo+UvfniFOGUT5OaHe9RL?= =?us-ascii?q?885jcnCIK7F4vDQZqtgLOZ1iehApJWfnxGCkyLEXrwb4WLRewDZzyUIs9gjjwE?= =?us-ascii?q?UqOsS44/2h6wqgD20b1nLvDb+icAr5LsyMB15/HPlRE17TF7EsOd02eLT25pgG?= =?us-ascii?q?wIQz420btlrkNj0FeD1rJ0g/tCFdxc//lJSBs1NYbAz+xmDND/Qh/OfsuTR1a8?= =?us-ascii?q?WdipHTYxTtctzNAUeEZ9B8+ujhbd0Cq2G7UVjaCEBIQo8qLA2Hj8P8d9y3Da1K?= =?us-ascii?q?Y/i1kpXM5POnO6hq5l9wneHInJk12HmKascKQTxi/N9GOYwmqJs0BUSglwUb/K?= =?us-ascii?q?XXoHfEvZscz55l/eT7+pEbkmMgpAycmEKqZRb93piUxKRPblONjEZWK+gX2wBR?= =?us-ascii?q?WJxrOScoXmYWMd0zvBCEIciQAc4W6GNRQiBiemu2/REiRuGkj1Y0Pt6ulzs2u7?= =?us-ascii?q?QVUuzwGLdE1hz7619QQJivOCUf8T2agEuCg5oTVuAFm9x87WC8aHpwd5cqRcYN?= =?us-ascii?q?c94FJZ2mLDrAF9MIKvIL1lhl4EaQt3p1nu1xJ1CopejccqsGkmzA1oKaKXyFlB?= =?us-ascii?q?bS+X3YjsOr3LLWn/5B6va7TM1VHYzNmW+7wP5e89q1X7vQGlD1Ei8nt809VP1H?= =?us-ascii?q?uc/JrKBhIIUZ3tSkY36wR6p7bCbyk+/YzU1npsMbSvvz/Bwd0pB+wlygu6c9dY?= =?us-ascii?q?KqOEGxf4E9cGCMi2NOwqh1+pYwoeM+9P7q40Pt2pd+Gd1aGxIOlgmSiqjWNd4I?= =?us-ascii?q?ByykiM7TZzSvbU35YZxPGVxheIWC34jFi/qcD3hYBFZTYJE2qk1SfkBZBeZqtu?= =?us-ascii?q?fYYKE2iiOcu3yctih5T1QX5X6EajB08a2M+uYReSb0Ly3QlU1UQQunGnnTa4zy?= =?us-ascii?q?BzkzEstKWTxivOw/7tdBAfIG5EWHFijUvwIYizl90aR0aobxIulBu++Ub33LNb?= =?us-ascii?q?pKJ4L2jdREdHZS72I3tkUqSuqrqIe9RP548wsSVLTOS8ZkiXS7DnrBsezyPsAX?= =?us-ascii?q?FexCsgejG0pJr5hQd1iH+YLHppq3rZY85wzw/F5NPAXf5RwiYGRC5ghDnLG1ez?= =?us-ascii?q?Itip8s+Ql5rYru++UGehWYFJfinw0YOMri276nN2ARenhfC8hsXnERQm0S/8z9?= =?us-ascii?q?RqUyTIrBL4Yob1zKS3KvhocVdyBF/78cZ6HZt+koQojpEKxXcanomV/WYAkWrr?= =?us-ascii?q?L9Vb2L7xbGYKRT4R2dLa/Ajl2EplLnKG3I/5UnSdws17aNmgZWMawCU979pFCK?= =?us-ascii?q?2M9rxLgTN1okakrQLWefV9mjYdyeAy6HIDmOEGpg4tziSbArAPEkhVJjfslwyJ?= =?us-ascii?q?79C4raVYeH2vfaK21EVggdCrFKuCrR1EWHblZpciGjd97t94MFLI1H389Jvkd8?= =?us-ascii?q?XXbdMdrRCUiArAgPROKJ4rkfoKnyVnM3rnvXI50+47kQBu3ZaisYifMWpt4aa5?= =?us-ascii?q?DgRCOTHvfMwT/TXtjbpZnsaQxI2vG4ltGjMMXJT0V/6oFiwdtej/PQaUDDI8sm?= =?us-ascii?q?ubGabYHQKH60dpsXTPE4uwOnGUOnYZy85iRB+DK0xDmgwUWy86npEhHACw2MPh?= =?us-ascii?q?aFt55iwW5lPgshtD0P9oNx3hXWfHvgiobCs0R4aFLBdN8wFC/FnaMdaF7u5pBC?= =?us-ascii?q?FY+YChrBaRJWOFaQRICnsJWkOaCFz5OLmh+8XA+fCCBuWiN/vOfamOqetGWveW?= =?us-ascii?q?w5Kgz5Br/yiKNsqSPnluFfs71VRfXXphAcTWhy0PSzALlyLKd8ObvA2z+jBzrs?= =?us-ascii?q?2k7PvrWR7v5IuWB7tcK9pv/Qq6gaCZN+6fnCx5MypX1osQxX/Uz7gSxF0SiiFq?= =?us-ascii?q?dzazCrkAsCnNTKzemqBJEx4bdyNzNMxN760ixAVNP9DUis/t3L5ikvE1E0tFVU?= =?us-ascii?q?Dmmsyxa8wFOWe9NE/ABEaOL7mGKyPEzNzwYaOmU7JQlP9UtxqyuTmFD0DjIi6P?= =?us-ascii?q?lz7zVxCgKetMlj2UPARCuIGhdRZgEXXsTNT8ZRCgLdB3iyc5zaczhnPQOm4WKS?= =?us-ascii?q?J8fF9Vrr2M8SNYhe1yG3Zb4ntrN+WEnjyW7+3fKpcWqvZkGDh7l+dA7HQ90btV?= =?us-ascii?q?4zlOROZplyvKst5uv1amn/GByjp9ThVBty1Li5iNvUV+PqXZ7YJNVmzY8xII92?= =?us-ascii?q?mQDA4Kp9R9AN31p69Q0sTPlL70KDpa9dLU59ETB9DMKM+cPnohMADpGCXIDAsL?= =?us-ascii?q?TD6rM3vQh0pBn/GT7HKVqoI2qp/2mJoBUrVbTkA6FusGCkR5G9wPOIx4USgjnb?= =?us-ascii?q?6fjc4I/ma+rALLSMVBoJ/ISuieAe7xJzadjLlEYQYIwLzjIIsPNoz7wVFiakJg?= =?us-ascii?q?kIXEAUXQQctNoipnbgMup0VN6nd+RHUp2035cgOt/GMTFfmskxEtiwt+buMt9D?= =?us-ascii?q?H37lgqOFXFvyown1M3mdn/hjCRajHxJr+qXY5KEyr0q1QxMpTjTglvYg29h1Jk?= =?us-ascii?q?OynfSrJWl7tva2ZriArGtptOHf5TVqpEYRAKyfGQffUo3kxWqj+7yk9f+evFFZ?= =?us-ascii?q?ximRM2cZ6rqnJMwRhjbNk0JKPOIqpG1UJQhqWQsS+rze0+3AgeJ0MR/2yIYy4I?= =?us-ascii?q?pFYENr84KCq05uZs8xCNmyNfeGgQUPonuvRq+V0nNOSE1C/g0LlDKlutN+OFKa?= =?us-ascii?q?OZoGvAlc+STlM2yEwIi1FP/aJq3sc7b0qUS0cvwaOLFxsXLsrNNxtab9BO+3jT?= =?us-ascii?q?ZiuBquTNwYhoMIW6GODoVfWOu7gUg06+GgYmAZgM5NwbHpawyEHYMdvnLLkdxB?= =?us-ascii?q?ow/gvrOlSFA+lSeB+QijgHudu/zJh53IlGOD4SHXh9MT+25rbLpg4mmvyDU8kq?= =?us-ascii?q?Yn0CRIsLKmo2WNGmmy5ep3lPESK33fwDxwiN9T/zvDrQASLnb9V5Z/eUeAlsB8?= =?us-ascii?q?us9TU77qi2j0TY8o/HK2HmMtRiud/O6eQAq5aBFfxUUaF3s13AlIlAW3yqT2nP?= =?us-ascii?q?HMaxJ5j3cYkjc9r0C3e9UlGkhDI1SdrxMMyqLqiSjgHiXZxUv5WD3DA/Kc+9ES?= =?us-ascii?q?kTGwxuqOEH5aJ8YxADYpUgbBH0sAQ+MrCwLx2D0tWyWWqtLyVZT+VHx+Wge7NX?= =?us-ascii?q?1zYsbvO9yHY4U5410+y38UkLRJEWkx3T3uijaJdEUSjyG31dfAPPqTA/l2d/Ku?= =?us-ascii?q?Y42v0/zw/QsVkALzCLc/RkaG9ev9A8H1ySJ2l2CmojSl+TiorD+hKj06oU/yRD?= =?us-ascii?q?g9ZeyfdFv2Tmvp/DfDKsX7SmqYnUsyolatgmv61xMIz4L8SbtZPfnibQQ4LKvg?= =?us-ascii?q?2DTiG6C+JQmsJMLyJAXPlIhWYlNNQJuYpb80UxUNs+KqdOCKkrqLGqbyZkDS4M?= =?us-ascii?q?wi8YUYOAwCINgv2m17vciBiQbIwoMAYYv5VantsdTyl2bzsEq6+nVoXZi3GLRX?= =?us-ascii?q?AOIAYT6wRM4wQAmZVrc+/+4YrHVplMxydYo/JpSCfLEIdn90fjQGGMnVf4UOmh?= =?us-ascii?q?k/Cu3Q9K1/3jzN8bVQVlBkdA2eZZiFEoKKptJKkKvo/GqDiIdVn1vGj10uupOE?= =?us-ascii?q?FRydHId13/FIfFr2z8UiwA9n0UQo9PzGrfG4wLnQRkcqgmuFJCLZu6dUb54jwr?= =?us-ascii?q?255mH6KkVcyxwVYqs2oGTT+wE9VdE+FmrE7XWDp9bp+3qpXqIYldT3RM952ar1?= =?us-ascii?q?dZlERtPDCjyZpANc5C/j4MUCJIoTWHs9u4UNdD1tNuD58QPtd/vG/wGLleN5iL?= =?us-ascii?q?pH05oafiymPD+z8mrle12jGzG66+T+JW+20eGwopJ2SCqkkhFeQs6GLS/krLsl?= =?us-ascii?q?9q4+dRHqKPgllpoDZhApBOAS5E1H6/I1tvTHlGsuBaKKXPfMxfWPQyZAGgNAIg?= =?us-ascii?q?GvI82EyJ5050l2/jYyNurgta5zzdXw4sWCkNhbftnScRqtupOD8dT5JIYy4sby?= =?us-ascii?q?LCKwKcgiBYogpQa1ttW5ACGdZF+rcb3YRO8srFU0asJjkPXAZ+OQIgzfpfiUlD?= =?us-ascii?q?vV2CdiDHEAqoc+zDsh5scMeQrc6pLej58xxdhoPhru84670JR2e6lg21XdDes4?= =?us-ascii?q?j8u8WWtkuJcaf1KPG8bWPETDfXlxywg6krD4XS9SjJLAVbM4V6yWYjYZX5CW7E?= =?us-ascii?q?Jw5GKLgfJ0VGTq11dMtJovhCZ8B6fKYG57VtCgiARhP1F4yltONGIUrLRTTCMy?= =?us-ascii?q?WB9fSyoYDU7bPDUuXve9KDx23ZTKJtIpd19yT0F63s0YBA5kr8wu1t+V9iSVja?= =?us-ascii?q?LyCBq8zsKRgF5Ma+akfvvIAlED3NDZhsl3rtw11PeNAMQy226pgYzo1W6GroQ+?= =?us-ascii?q?JizkfzqPFS96Vj6YQv+bBm19q7JafIJvRGqkJnBh+UBht0+ZkzHGd/QH1ebfEL?= =?us-ascii?q?JPfXY6sZkdjkq/rrGKwP9B2V5+tZZMPfJ0HahsmwECyTSQBfkQcarj4aMxGc2O?= =?us-ascii?q?KblK9yVcalv/L22kY371i5NB4Gw6hn5Z2Y9aqQuO/XcxzRwKAYWqjwWMz8sKkj?= =?us-ascii?q?tliP5f0giL4OYHZ6YxOmEOgaUs4S2HvgwbotzSI2HMPJB6jg9+JbV3IlgjLgnI?= =?us-ascii?q?hwH1AMFfMQGbqE45hennsilOPDLdAWdL5NmnqUFRG4CLAC0WCk5zGRIGZ7nhHE?= =?us-ascii?q?ywvwTn+r7F/qsS94RjPBz9X5nUpPUbm3HkNSUDC3OUJjtTOPIQ7otMb2uasp7U?= =?us-ascii?q?E5KGrktMmXmGumIrNXENX1JMaALiksuFIXkJoxS8St2Y8BH9q9Ls0R8HZjYfTA?= =?us-ascii?q?8G6rjzVOo6Fch4rZ+M2V/OvYHWW4ha2At7qN3CxYymQ/vVwn5dCgK/DO59mOQ/?= =?us-ascii?q?u20mYcVDpwtBXaUB60sLPbqUoYOUuR0EfEgIYKJM1W3WEk1kH64+guWNQz9AFF?= =?us-ascii?q?G4bBYfMCoCr/ODrywVaRedI4TDKe0ydNHlLoDVZ4BLU82Hjus87TknfQ4VIoTJ?= =?us-ascii?q?Fqd0P7nRx3E5k4KUU15VgSxSoDFxQNaR+FA7yzAETlLJELVU4dZhSb0rm6fb03?= =?us-ascii?q?3U5owr+14u/cc/B8DbIXNvlBlg6OgERbGpUOvK0EXb1zZkRQ9KHSpgntEYTnWe?= =?us-ascii?q?bplWYoP/2vXs9a6dwZt2cl4gunQxqg8pJD4rYAhZCGaKFLfJ/MvNpm4Ed//z4P?= =?us-ascii?q?cS5NjwZjgBOlS+wco/rj4tfDupqy9umuTLotR/kQ9xUsHGRxlYXwj0osod7JzO?= =?us-ascii?q?dRUZbVhp7h8A9XLH6Fpp3a3wN6KeUQN4KhZKxg+GkfJygCO3IOOsKba/s94y9o?= =?us-ascii?q?KzjT4kJNA8MSatMCPcrNnw9UhlfvWL5N6srRAkWYBJtrd8A082r3zyg48Zo9U+?= =?us-ascii?q?n68jC5OYvS70pTMPNfkipjiMjOpO8PwfrdECIX+2WWawBpwiOezJmAE+3//eSX?= =?us-ascii?q?yNDaTVwGHC82U5tFJDWY5AynQvG4m4nuUgyJ9MD8mo8+dFiXRnyrhqsJqKFMEf?= =?us-ascii?q?RPii/jxDhRCpj1h+6Js9qr8GZXrkdIEJpv4h3FFqRfOY97ORfhmsmwWEd8Air/?= =?us-ascii?q?eMfJdhsho+eW3v0D4/liOEv+a44bJQgEy7Hg5XpWUARuUrj2sUidXe4LY9tmUv?= =?us-ascii?q?zEpGhP6Y18M68PIESdpJvyozdUslA5HgspZ6U2rjNEbUTBgQxVW6n1uL4ckAQc?= =?us-ascii?q?SsR2uVVRGW6qPmIx+SbHX7xPjKaNEPwV7imTTqsWXkVnKCx+WQ262Ilue7aymf?= =?us-ascii?q?BHs39GkT19oPcwzjxmXwGwuSvyqKIRwTgg4q23tC0duXxZSeWTiyjIBktFzPQW?= =?us-ascii?q?iqccDHHu6Ueib3kHbYvy5qRnJMv7+Ikl/Xs/exUicDccUeSmFS7wk7uCApaTv9?= =?us-ascii?q?JEmB6NpMLOYKe1LSgTKrs9zhbjSGJm0gTEnRZo92wLQi677NI/IYWwNskkyjCv?= =?us-ascii?q?GWjBe1YG+rlJv9fptV4XUOs2bktswGp/3cicXS0BXdHBFWkzgAglb2VEbZ1C5A?= =?us-ascii?q?QVF6ktmDmIuLdJ8hsTYDfOHYSv4pPQktvQ2XkhUddqwXrbpq+ChpMq1n1pgc97?= =?us-ascii?q?7i6KuHQPauPZUs5sAn7o1odcyOzzfOutsuEZSIR8zLSuTf4CPtOl+WSoxJVgQl?= =?us-ascii?q?WlyagGH1qlLO8Dwa/WUya7SWKCX+SGaGyMnyskPU7z4hmoNUc7aMBQr08yKuHC?= =?us-ascii?q?nIJTlxX9UbNoQSWdvV3bw3ItMeMUcwI7o4mmdBYMTOEPe+iQP+4uz+MiCFEUdX?= =?us-ascii?q?/GATN2C/OqsV6qhIV7NHFg4UL0YOvz9gDmNMCfGgIDEY7cr55+5ea2RmSfNn98?= =?us-ascii?q?1BdyJlV79//DF1Qtse9Ra5SRndjQhtRj1u4Fc/ZtMTEjtdMIn4Jj6JWU0N2Ufh?= =?us-ascii?q?3LyZbyP97VqOCCA/LD10QqZn1aUr0BbAPp5oU6O8U0W6HSHbRFvhQTG6w6TIYn?= =?us-ascii?q?N2jv6KF+NBlzfRLJZLSonsnqofqGZp5Vp3/V7VIwKzzRux8Zxfy3Sgx7aY6lh3?= =?us-ascii?q?PoLJA2WD1Bs8VnCgF6E4tXB8MAsw2nDoaPl66ljd+x51h3u/QOsar3FvDKyMq5?= =?us-ascii?q?0JtsUJRA4kyLJjnRDrFxgkt5luSynuvA0p7pBMPsZ9wEUOl7QnbbZbLdH4S/Kz?= =?us-ascii?q?eOOsfidE5c976Tzqh1UhKLZCD2R6CGrjGrNO144UUnzYx1ZOTSzCQ377HaxNT9?= =?us-ascii?q?eXpWpiKirX6VLptQ8kbGBevEXxJbUfCF6npqHbULbYvo8+cDKdsiwNyC7ARo9D?= =?us-ascii?q?RC1NGKI7O9rk/Nxk17aYjbI1X31CojX4kFPgi/O1M2gWDFsnTdHWhcLs+8JMlp?= =?us-ascii?q?gdaVCAHt5kZolWEpZ25BAXboRcyKNGgawc2+YgqK+BhMD9YZkO69YVQ4ubGqSe?= =?us-ascii?q?l0JpVFnv2ntK4ZntZtKiHPQtVaPi7XILF5IjpRDvvApEI2bR4cqbc4QYQ1aoaU?= =?us-ascii?q?IEMBLkiAzTj9zRbN0UHucNyszqmJKj4M8npb17LFzSRMpw6htPacmMLsTLDZbJ?= =?us-ascii?q?DwXP7VKyclSDCaRTIsHkat+FekvecEvfWCLWcYuFAUbTqYCBQPqaB3sdjQEmjT?= =?us-ascii?q?lPVhfJIQh/CaXDv/RTZllKo0GiZLtUeMQ+cMFQnWc3/tmmxctxK+JvVU53Lqc6?= =?us-ascii?q?WYxrZJW+wRGoZMfeeWTMDCefBfOTgoki4UOPi4f9LGtLY500/IQXEfE6bW6F2U?= =?us-ascii?q?VFSWTeCExzL3QYUVuJA5uigt+tLXhC93CaXIP62Eqj6o6IG4iTiXuezEWmkqeU?= =?us-ascii?q?w1j/oIAHOdzxlYNGEEF9YVtVn2Qq6OYkZDyHMkhOF11h8SZgtzSHxu03xRnPa7?= =?us-ascii?q?AMFZT0AbjH+2Sv0ccF93FC8w/VON4gDqY9wAvsbTR2tF+7sJV4USMfco6I7NOK?= =?us-ascii?q?QM3vklxjRmrzIms3bVM1QIlQ+B8qzNDIpg17ZC4i8+5PgwUUaREB3FdW2S6IOj?= =?us-ascii?q?BtkH7i50vX3+n57WoetkLqcAj51oC1cNRyRtL5bS+i4XAjK84xe7oxvuRGfWGz?= =?us-ascii?q?ke+jteYHZdew=3D=3D?= X-IPAS-Result: =?us-ascii?q?A2HCCADj+9NY/wHyM5BdGgEBAQECAQEBAQgBAQEBFQEBAQE?= =?us-ascii?q?CAQEBAQgBAQEBgn4oYXkSjmSSYwGVTCIDgXeHIVcBAQEBAQEBAQIBAmgFI4IzI?= =?us-ascii?q?oEqWzwGAQIXAQwTFCAOAwkBARcpCAgDAS0VEQ4LBRgEh19CgTcNrDM6JgKKR4Q?= =?us-ascii?q?sizcRAYYBBYErAYdrhkeBTYsoAoZ7i08Cgk2IIYY/k2JYfAgbCQIUJVBngywBA?= =?us-ascii?q?QFCHYFiHVmHTg0XB4IQAQEB?= Received: from unknown (HELO tarius.tycho.ncsc.mil) ([144.51.242.1]) by emsm-gh1-uea11.nsa.gov with ESMTP; 23 Mar 2017 16:52:00 +0000 Received: from prometheus.infosec.tycho.ncsc.mil (prometheus [192.168.25.40]) by tarius.tycho.ncsc.mil (8.14.4/8.14.4) with ESMTP id v2NGpwJj003009; Thu, 23 Mar 2017 12:51:58 -0400 Received: from tarius.tycho.ncsc.mil (tarius.infosec.tycho.ncsc.mil [144.51.242.1]) by prometheus.infosec.tycho.ncsc.mil (8.15.2/8.15.2) with ESMTP id v2NGmYKR248221 for ; Thu, 23 Mar 2017 12:48:34 -0400 Received: from moss-lions.infosec.tycho.ncsc.mil (moss-lions [192.168.25.4]) by tarius.tycho.ncsc.mil (8.14.4/8.14.4) with ESMTP id v2NGmXYR001400 for ; Thu, 23 Mar 2017 12:48:33 -0400 From: James Carter To: selinux@tycho.nsa.gov Subject: [PATCH 1/3 v3] libsepol: Add ability to convert binary policy to CIL Date: Thu, 23 Mar 2017 12:48:55 -0400 Message-Id: <1490287737-31103-2-git-send-email-jwcart2@tycho.nsa.gov> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490287737-31103-1-git-send-email-jwcart2@tycho.nsa.gov> References: <1490287737-31103-1-git-send-email-jwcart2@tycho.nsa.gov> X-BeenThere: selinux@tycho.nsa.gov X-Mailman-Version: 2.1.20 Precedence: list List-Id: "Security-Enhanced Linux \(SELinux\) mailing list" List-Post: List-Help: MIME-Version: 1.0 Errors-To: selinux-bounces@tycho.nsa.gov Sender: "Selinux" X-Virus-Scanned: ClamAV using ClamSMTP It would sometimes be helpful for debugging or verification purposes to be able to convert a binary policy to a human-readable form. Create new function, sepol_kernel_policydb_to_cil(), that takes a policydb created from a binary policy and writes CIL policy to the provided FILE pointer. Signed-off-by: James Carter --- libsepol/include/sepol/kernel_to_cil.h | 5 + libsepol/src/kernel_to_cil.c | 3226 ++++++++++++++++++++++++++++++++ libsepol/src/kernel_to_common.c | 677 +++++++ libsepol/src/kernel_to_common.h | 114 ++ libsepol/src/libsepol.map.in | 1 + 5 files changed, 4023 insertions(+) create mode 100644 libsepol/include/sepol/kernel_to_cil.h create mode 100644 libsepol/src/kernel_to_cil.c create mode 100644 libsepol/src/kernel_to_common.c create mode 100644 libsepol/src/kernel_to_common.h diff --git a/libsepol/include/sepol/kernel_to_cil.h b/libsepol/include/sepol/kernel_to_cil.h new file mode 100644 index 0000000..60346ad --- /dev/null +++ b/libsepol/include/sepol/kernel_to_cil.h @@ -0,0 +1,5 @@ +#include + +#include + +int sepol_kernel_policydb_to_cil(FILE *fp, struct policydb *pdb); diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c new file mode 100644 index 0000000..e873ac3 --- /dev/null +++ b/libsepol/src/kernel_to_cil.c @@ -0,0 +1,3226 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_to_common.h" + + +static char *cond_expr_to_str(struct policydb *pdb, struct cond_expr *expr) +{ + struct cond_expr *curr; + struct strs *stack; + char *new_val; + char *str = NULL; + int rc; + + rc = stack_init(&stack); + if (rc != 0) { + goto exit; + } + + for (curr = expr; curr != NULL; curr = curr->next) { + if (curr->expr_type == COND_BOOL) { + char *val1 = pdb->p_bool_val_to_name[curr->bool - 1]; + new_val = create_str("%s", 1, val1); + } else { + const char *op; + uint32_t num_params; + char *val1 = NULL; + char *val2 = NULL; + + switch(curr->expr_type) { + case COND_NOT: op = "not"; num_params = 1; break; + case COND_OR: op = "or"; num_params = 2; break; + case COND_AND: op = "and"; num_params = 2; break; + case COND_XOR: op = "xor"; num_params = 2; break; + case COND_EQ: op = "eq"; num_params = 2; break; + case COND_NEQ: op = "neq"; num_params = 2; break; + default: + sepol_log_err("Unknown conditional operator: %i", + curr->expr_type); + goto exit; + } + + if (num_params == 2) { + val2 = stack_pop(stack); + if (!val2) { + sepol_log_err("Invalid conditional expression"); + goto exit; + } + } + val1 = stack_pop(stack); + if (!val1) { + sepol_log_err("Invalid conditional expression"); + free(val2); + goto exit; + } + if (num_params == 2) { + new_val = create_str("(%s %s %s)", 3, op, val1, val2); + free(val2); + } else { + new_val = create_str("(%s %s)", 2, op, val1); + } + free(val1); + } + if (!new_val) { + sepol_log_err("Invalid conditional expression"); + goto exit; + } + rc = stack_push(stack, new_val); + if (rc != 0) { + sepol_log_err("Out of memory"); + goto exit; + } + } + + new_val = stack_pop(stack); + if (!new_val || !stack_empty(stack)) { + sepol_log_err("Invalid conditional expression"); + goto exit; + } + + str = new_val; + + stack_destroy(&stack); + return str; + +exit: + while ((new_val = stack_pop(stack)) != NULL) { + free(new_val); + } + stack_destroy(&stack); + + return NULL; +} + +static char *constraint_expr_to_str(struct policydb *pdb, struct constraint_expr *expr, int *use_mls) +{ + struct constraint_expr *curr; + struct strs *stack = NULL; + char *new_val = NULL; + const char *op; + char *str = NULL; + int rc; + + *use_mls = 0; + + rc = stack_init(&stack); + if (rc != 0) { + goto exit; + } + + for (curr = expr; curr; curr = curr->next) { + if (curr->expr_type == CEXPR_ATTR || curr->expr_type == CEXPR_NAMES) { + const char *attr1 = NULL; + const char *attr2 = NULL; + + switch (curr->op) { + case CEXPR_EQ: op = "eq"; break; + case CEXPR_NEQ: op = "neq"; break; + case CEXPR_DOM: op = "dom"; break; + case CEXPR_DOMBY: op = "domby"; break; + case CEXPR_INCOMP: op = "incomp"; break; + default: + sepol_log_err("Unknown constraint operator: %i", curr->op); + goto exit; + } + + switch (curr->attr) { + case CEXPR_USER: attr1 ="u1"; attr2 ="u2"; break; + case CEXPR_USER | CEXPR_TARGET: attr1 ="u2"; attr2 =""; break; + case CEXPR_USER | CEXPR_XTARGET: attr1 ="u3"; attr2 =""; break; + case CEXPR_ROLE: attr1 ="r1"; attr2 ="r2"; break; + case CEXPR_ROLE | CEXPR_TARGET: attr1 ="r2"; attr2 =""; break; + case CEXPR_ROLE | CEXPR_XTARGET: attr1 ="r3"; attr2 =""; break; + case CEXPR_TYPE: attr1 ="t1"; attr2 ="t2"; break; + case CEXPR_TYPE | CEXPR_TARGET: attr1 ="t2"; attr2 =""; break; + case CEXPR_TYPE | CEXPR_XTARGET: attr1 ="t3"; attr2 =""; break; + case CEXPR_L1L2: attr1 ="l1"; attr2 ="l2"; break; + case CEXPR_L1H2: attr1 ="l1"; attr2 ="h2"; break; + case CEXPR_H1L2: attr1 ="h1"; attr2 ="l2"; break; + case CEXPR_H1H2: attr1 ="h1"; attr2 ="h2"; break; + case CEXPR_L1H1: attr1 ="l1"; attr2 ="h1"; break; + case CEXPR_L2H2: attr1 ="l2"; attr2 ="h2"; break; + default: + sepol_log_err("Unknown constraint attribute: %i", + curr->attr); + goto exit; + } + + if (curr->attr >= CEXPR_XTARGET) { + *use_mls = 1; + } + + if (curr->expr_type == CEXPR_ATTR) { + new_val = create_str("(%s %s %s)", 3, op, attr1, attr2); + } else { + char *names = NULL; + if (curr->attr & CEXPR_TYPE) { + struct type_set *ts = curr->type_names; + names = ebitmap_to_str(&ts->types, pdb->p_type_val_to_name, 1); + } else if (curr->attr & CEXPR_USER) { + names = ebitmap_to_str(&curr->names, pdb->p_user_val_to_name, 1); + } else if (curr->attr & CEXPR_ROLE) { + names = ebitmap_to_str(&curr->names, pdb->p_role_val_to_name, 1); + } + if (!names) { + goto exit; + } + new_val = create_str("(%s %s %s)", 3, op, attr1, names); + free(names); + } + } else { + uint32_t num_params; + char *val1 = NULL; + char *val2 = NULL; + + switch (curr->expr_type) { + case CEXPR_NOT: op = "not"; num_params = 1; break; + case CEXPR_AND: op = "and"; num_params = 2; break; + case CEXPR_OR: op = "or"; num_params = 2; break; + default: + sepol_log_err("Unknown constraint expression type: %i", + curr->expr_type); + goto exit; + } + + if (num_params == 2) { + val2 = stack_pop(stack); + if (!val2) { + sepol_log_err("Invalid constraint expression"); + goto exit; + } + } + val1 = stack_pop(stack); + if (!val1) { + sepol_log_err("Invalid constraint expression"); + goto exit; + } + + if (num_params == 2) { + new_val = create_str("(%s %s %s)", 3, op, val1, val2); + free(val2); + } else { + new_val = create_str("(%s %s)", 2, op, val1); + } + free(val1); + } + if (!new_val) { + goto exit; + } + rc = stack_push(stack, new_val); + if (rc != 0) { + sepol_log_err("Out of memory"); + goto exit; + } + } + + new_val = stack_pop(stack); + if (!new_val || !stack_empty(stack)) { + sepol_log_err("Invalid constraint expression"); + goto exit; + } + + str = new_val; + + stack_destroy(&stack); + + return str; + +exit: + while ((new_val = stack_pop(stack)) != NULL) { + free(new_val); + } + stack_destroy(&stack); + + return NULL; +} + +static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, + class_datum_t *class, + struct constraint_node *constraint_rules, + struct strs *mls_list, + struct strs *non_mls_list) +{ + int rc = 0; + struct constraint_node *curr; + char *expr = NULL; + int is_mls; + char *perms; + const char *format_str; + struct strs *strs; + + for (curr = constraint_rules; curr != NULL; curr = curr->next) { + expr = constraint_expr_to_str(pdb, curr->expr, &is_mls); + if (!expr) { + rc = -1; + goto exit; + } + + perms = sepol_av_to_string(pdb, class->s.value, curr->permissions); + + if (is_mls) { + format_str = "(mlsconstrain (%s (%s)) %s)"; + strs = mls_list; + } else { + format_str = "(constrain (%s (%s)) %s)"; + strs = non_mls_list; + } + + rc = strs_create_and_add(strs, format_str, 3, classkey, perms+1, expr); + free(expr); + if (rc != 0) { + goto exit; + } + } + + return 0; +exit: + sepol_log_err("Error gathering constraint rules\n"); + return rc; +} + +static int class_validatetrans_rules_to_strs(struct policydb *pdb, char *classkey, + struct constraint_node *validatetrans_rules, + struct strs *mls_list, + struct strs *non_mls_list) +{ + struct constraint_node *curr; + char *expr = NULL; + int is_mls; + const char *format_str; + struct strs *strs; + int rc = 0; + + for (curr = validatetrans_rules; curr != NULL; curr = curr->next) { + expr = constraint_expr_to_str(pdb, curr->expr, &is_mls); + if (!expr) { + rc = -1; + goto exit; + } + + if (is_mls) { + format_str = "(mlsvalidatetrans %s %s)"; + strs = mls_list; + } else { + format_str = "(validatetrans %s %s)"; + strs = non_mls_list; + } + + rc = strs_create_and_add(strs, format_str, 2, classkey, expr); + free(expr); + if (rc != 0) { + goto exit; + } + } + +exit: + return rc; +} + +static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs, struct strs *non_mls_strs) +{ + class_datum_t *class; + char *name; + unsigned i; + int rc = 0; + + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + if (class->constraints) { + name = pdb->p_class_val_to_name[i]; + rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(mls_strs); + strs_sort(non_mls_strs); + +exit: + return rc; +} + +static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_strs, struct strs *non_mls_strs) +{ + class_datum_t *class; + char *name; + unsigned i; + int rc = 0; + + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + if (class->validatetrans) { + name = pdb->p_class_val_to_name[i]; + rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(mls_strs); + strs_sort(non_mls_strs); + +exit: + return rc; +} + +static int write_handle_unknown_to_cil(FILE *out, struct policydb *pdb) +{ + const char *action; + + switch (pdb->handle_unknown) { + case SEPOL_DENY_UNKNOWN: + action = "deny"; + break; + case SEPOL_REJECT_UNKNOWN: + action = "reject"; + break; + case SEPOL_ALLOW_UNKNOWN: + action = "allow"; + break; + default: + sepol_log_err("Unknown value for handle-unknown: %i", pdb->handle_unknown); + return -1; + } + + sepol_printf(out, "(handleunknown %s)\n", action); + + return 0; +} + +static char *class_or_common_perms_to_str(symtab_t *permtab) +{ + struct strs *strs; + char *perms = NULL; + int rc; + + rc = strs_init(&strs, permtab->nprim); + if (rc != 0) { + goto exit; + } + + rc = hashtab_map(permtab->table, hashtab_ordered_to_strs, strs); + if (rc != 0) { + goto exit; + } + + if (strs_num_items(strs) > 0) { + perms = strs_to_str(strs); + } + +exit: + strs_destroy(&strs); + + return perms; +} + +static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb) +{ + class_datum_t *class; + common_datum_t *common; + int *used; + char *name, *perms; + unsigned i; + int rc = 0; + + /* class */ + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + name = pdb->p_class_val_to_name[i]; + perms = class_or_common_perms_to_str(&class->permissions); + if (perms) { + sepol_printf(out, "(class %s (%s))\n", name, perms); + free(perms); + } else { + sepol_printf(out, "(class %s ())\n", name); + } + } + + /* classorder */ + sepol_printf(out, "(classorder ("); + name = NULL; + for (i=0; i < pdb->p_classes.nprim; i++) { + if (name) { + sepol_printf(out, "%s ", name); + } + name = pdb->p_class_val_to_name[i]; + } + if (name) { + sepol_printf(out, "%s", name); + } + sepol_printf(out, "))\n"); + + /* classcommon */ + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + name = pdb->p_class_val_to_name[i]; + if (class->comkey != NULL) { + sepol_printf(out, "(classcommon %s %s)\n", name, class->comkey); + } + } + + /* common */ + used = calloc(pdb->p_commons.nprim, sizeof(*used)); + if (!used) { + sepol_log_err("Out of memory"); + rc = -1; + goto exit; + } + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + name = class->comkey; + if (name != NULL) { + common = hashtab_search(pdb->p_commons.table, name); + if (!common) { + rc = -1; + free(used); + goto exit; + } + /* Only write common rule once */ + if (!used[common->s.value-1]) { + perms = class_or_common_perms_to_str(&common->permissions); + if (!perms) { + rc = -1; + free(perms); + free(used); + goto exit; + } + + sepol_printf(out, "(common %s (%s))\n", name, perms); + free(perms); + used[common->s.value-1] = 1; + } + } + } + free(used); + +exit: + if (rc != 0) { + sepol_log_err("Error writing class rules to CIL\n"); + } + + return rc; +} + +static int write_sids_to_cil(FILE *out, const char *const *sid_to_str, struct ocontext *isids) +{ + struct ocontext *isid; + struct strs *strs; + char *sid; + char *prev; + unsigned i; + int rc; + + rc = strs_init(&strs, SECINITSID_NUM+1); + if (rc != 0) { + goto exit; + } + + for (isid = isids; isid != NULL; isid = isid->next) { + i = isid->sid[0]; + rc = strs_add_at_index(strs, (char *)sid_to_str[i], i); + if (rc != 0) { + goto exit; + } + } + + for (i=0; itarget_platform == SEPOL_TARGET_SELINUX) { + rc = write_sids_to_cil(out, selinux_sid_to_str, pdb->ocontexts[0]); + } else if (pdb->target_platform == SEPOL_TARGET_XEN) { + rc = write_sids_to_cil(out, xen_sid_to_str, pdb->ocontexts[0]); + } else { + sepol_log_err("Unknown target platform: %i", pdb->target_platform); + rc = -1; + } + + return rc; +} + +static int write_default_user_to_cil(FILE *out, char *class_name, class_datum_t *class) +{ + const char *dft; + + switch (class->default_user) { + case DEFAULT_SOURCE: + dft = "source"; + break; + case DEFAULT_TARGET: + dft = "target"; + break; + default: + sepol_log_err("Unknown default role value: %i", class->default_user); + return -1; + } + sepol_printf(out, "(defaultuser %s %s)\n", class_name, dft); + + return 0; +} + +static int write_default_role_to_cil(FILE *out, char *class_name, class_datum_t *class) +{ + const char *dft; + + switch (class->default_role) { + case DEFAULT_SOURCE: + dft = "source"; + break; + case DEFAULT_TARGET: + dft = "target"; + break; + default: + sepol_log_err("Unknown default role value: %i", class->default_role); + return -1; + } + sepol_printf(out, "(defaultrole %s %s)\n", class_name, dft); + + return 0; +} + +static int write_default_type_to_cil(FILE *out, char *class_name, class_datum_t *class) +{ + const char *dft; + + switch (class->default_type) { + case DEFAULT_SOURCE: + dft = "source"; + break; + case DEFAULT_TARGET: + dft = "target"; + break; + default: + sepol_log_err("Unknown default type value: %i", class->default_type); + return -1; + } + sepol_printf(out, "(defaulttype %s %s)\n", class_name, dft); + + return 0; +} + +static int write_default_range_to_cil(FILE *out, char *class_name, class_datum_t *class) +{ + const char *dft; + + switch (class->default_range) { + case DEFAULT_SOURCE_LOW: + dft = "source low"; + break; + case DEFAULT_SOURCE_HIGH: + dft = "source high"; + break; + case DEFAULT_SOURCE_LOW_HIGH: + dft = "source low-high"; + break; + case DEFAULT_TARGET_LOW: + dft = "target low"; + break; + case DEFAULT_TARGET_HIGH: + dft = "target high"; + break; + case DEFAULT_TARGET_LOW_HIGH: + dft = "target low-high"; + break; + default: + sepol_log_err("Unknown default type value: %i", class->default_range); + return -1; + } + sepol_printf(out, "(defaultrange %s %s)\n", class_name, dft); + + return 0; +} + +static int write_default_rules_to_cil(FILE *out, struct policydb *pdb) +{ + class_datum_t *class; + unsigned i; + int rc = 0; + + /* default_user */ + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + if (class->default_user != 0) { + rc = write_default_user_to_cil(out, pdb->p_class_val_to_name[i], class); + if (rc != 0) { + goto exit; + } + } + } + + /* default_role */ + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + if (class->default_role != 0) { + rc = write_default_role_to_cil(out, pdb->p_class_val_to_name[i], class); + if (rc != 0) { + goto exit; + } + } + } + + /* default_type */ + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + if (class->default_type != 0) { + rc = write_default_type_to_cil(out, pdb->p_class_val_to_name[i], class); + if (rc != 0) { + goto exit; + } + } + } + + if (!pdb->mls) { + return 0; + } + + /* default_range */ + for (i=0; i < pdb->p_classes.nprim; i++) { + class = pdb->class_val_to_struct[i]; + if (class->default_range) { + rc = write_default_range_to_cil(out, pdb->p_class_val_to_name[i], class); + if (rc != 0) { + goto exit; + } + } + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing default rules to CIL\n"); + } + + return rc; +} + +static void write_default_mls_level(FILE *out) +{ + sepol_printf(out, "(sensitivity s0)"); + sepol_printf(out, "(sensitivityorder (s0))"); + sepol_printf(out, "(level %s (s0))", DEFAULT_LEVEL); +} + +static int map_sensitivity_aliases_to_strs(char *key, void *data, void *args) +{ + level_datum_t *sens = data; + struct strs *strs = args; + int rc = 0; + + if (sens->isalias) { + rc = strs_add(strs, key); + } + + return rc; +} + +static int write_sensitivity_rules_to_cil(FILE *out, struct policydb *pdb) +{ + level_datum_t *level; + char *prev, *name, *actual; + struct strs *strs; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_levels.nprim); + if (rc != 0) { + goto exit; + } + + /* sensitivities */ + for (i=0; i < pdb->p_levels.nprim; i++) { + name = pdb->p_sens_val_to_name[i]; + if (!name) continue; + level = hashtab_search(pdb->p_levels.table, name); + if (!level) { + rc = -1; + goto exit; + } + if (level->isalias) continue; + + sepol_printf(out, "(sensitivity %s)\n", name); + } + + /* sensitivityorder */ + sepol_printf(out, "(sensitivityorder ("); + prev = NULL; + for (i=0; i < pdb->p_levels.nprim; i++) { + name = pdb->p_sens_val_to_name[i]; + if (!name) continue; + level = hashtab_search(pdb->p_levels.table, name); + if (!level) { + rc = -1; + goto exit; + } + if (level->isalias) continue; + + if (prev) { + sepol_printf(out, "%s ", prev); + } + prev = name; + } + if (prev) { + sepol_printf(out, "%s", prev); + } + sepol_printf(out, "))\n"); + + rc = hashtab_map(pdb->p_levels.table, map_sensitivity_aliases_to_strs, strs); + if (rc != 0) { + goto exit; + } + + strs_sort(strs); + + num = strs_num_items(strs); + + /* sensitivity aliases */ + for (i=0; i < num; i++) { + name = strs_read_at_index(strs, i); + level = hashtab_search(pdb->p_levels.table, name); + if (!level) { + rc = -1; + goto exit; + } + sepol_printf(out, "(sensitivityalias %s)\n", name); + } + + /* sensitivity aliases to actual */ + for (i=0; i < num; i++) { + name = strs_read_at_index(strs, i); + level = hashtab_search(pdb->p_levels.table, name); + if (!level) { + rc = -1; + goto exit; + } + actual = pdb->p_sens_val_to_name[level->level->sens - 1]; + sepol_printf(out, "(sensitivityaliasactual %s %s)\n", name, actual); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing sensitivity rules to CIL\n"); + } + + return rc; +} + +static int map_category_aliases_to_strs(char *key, void *data, void *args) +{ + cat_datum_t *cat = data; + struct strs *strs = args; + int rc = 0; + + if (cat->isalias) { + rc = strs_add(strs, key); + } + + return rc; +} + +static int write_category_rules_to_cil(FILE *out, struct policydb *pdb) +{ + cat_datum_t *cat; + char *prev, *name, *actual; + struct strs *strs; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_levels.nprim); + if (rc != 0) { + goto exit; + } + + /* categories */ + for (i=0; i < pdb->p_cats.nprim; i++) { + name = pdb->p_cat_val_to_name[i]; + if (!name) continue; + cat = hashtab_search(pdb->p_cats.table, name); + if (!cat) { + rc = -1; + goto exit; + } + if (cat->isalias) continue; + + sepol_printf(out, "(category %s)\n", name); + } + + /* categoryorder */ + sepol_printf(out, "(categoryorder ("); + prev = NULL; + for (i=0; i < pdb->p_cats.nprim; i++) { + name = pdb->p_cat_val_to_name[i]; + if (!name) continue; + cat = hashtab_search(pdb->p_cats.table, name); + if (!cat) { + rc = -1; + goto exit; + } + if (cat->isalias) continue; + + if (prev) { + sepol_printf(out, "%s ", prev); + } + prev = name; + } + if (prev) { + sepol_printf(out, "%s", prev); + } + sepol_printf(out, "))\n"); + + rc = hashtab_map(pdb->p_cats.table, map_category_aliases_to_strs, strs); + if (rc != 0) { + goto exit; + } + + strs_sort(strs); + + num = strs_num_items(strs); + + /* category aliases */ + for (i=0; i < num; i++) { + name = strs_read_at_index(strs, i); + cat = hashtab_search(pdb->p_cats.table, name); + if (!cat) { + rc = -1; + goto exit; + } + sepol_printf(out, "(categoryalias %s)\n", name); + } + + /* category aliases to actual */ + for (i=0; i < num; i++) { + name = strs_read_at_index(strs, i); + cat = hashtab_search(pdb->p_cats.table, name); + if (!cat) { + rc = -1; + goto exit; + } + actual = pdb->p_cat_val_to_name[cat->s.value - 1]; + sepol_printf(out, "(categoryaliasactual %s %s)\n", name, actual); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing category rules to CIL\n"); + } + + return rc; +} + +static size_t cats_ebitmap_len(struct ebitmap *cats, char **val_to_name) +{ + struct ebitmap_node *node; + uint32_t i, start, range; + size_t len = 0; + + range = 0; + ebitmap_for_each_bit(cats, node, i) { + if (!ebitmap_get_bit(cats, i)) + continue; + + if (range == 0) + start = i; + + range++; + + if (ebitmap_get_bit(cats, i+1)) + continue; + + len += strlen(val_to_name[start]); + if (range > 2) { + len += strlen(val_to_name[i-1]) + strlen("(range ) "); + } else if (range == 2) { + len += strlen(val_to_name[i-1]) + 2; + } else if (range == 1) { + len += 1; + } + + range = 0; + } + + if (len > 0) { + len += 2; /* For '(' and ')'. '\0' overwrites last ' ' */ + } + + return len; +} + +static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) +{ + struct ebitmap_node *node; + uint32_t i, start, range; + char *catsbuf, *p; + const char *fmt; + int len, remaining; + + remaining = (int)cats_ebitmap_len(cats, val_to_name); + catsbuf = malloc(remaining); + if (!catsbuf) { + goto exit; + } + + p = catsbuf; + + *p++ = '('; + remaining--;; + + range = 0; + ebitmap_for_each_bit(cats, node, i) { + if (!ebitmap_get_bit(cats, i)) + continue; + + if (range == 0) + start = i; + + range++; + + if (ebitmap_get_bit(cats, i+1)) + continue; + + if (range > 1) { + fmt = (range == 2) ? "%s %s " : "(range %s %s) "; + len = snprintf(p, remaining, fmt, + val_to_name[start], val_to_name[i]); + } else { + len = snprintf(p, remaining, "%s ", val_to_name[start]); + } + if (len < 0 || len >= remaining) { + goto exit; + } + p += len; + remaining -= len; + + range = 0; + } + + *(p-1) = ')'; /* Remove trailing ' ' */ + *p = '\0'; + + return catsbuf; + +exit: + free(catsbuf); + return NULL; +} + +static int write_sensitivitycategory_rules_to_cil(FILE *out, struct policydb *pdb) +{ + level_datum_t *level; + char *name, *cats; + unsigned i; + int rc = 0; + + /* sensitivities */ + for (i=0; i < pdb->p_levels.nprim; i++) { + name = pdb->p_sens_val_to_name[i]; + if (!name) continue; + level = hashtab_search(pdb->p_levels.table, name); + if (!level) { + rc = -1; + goto exit; + } + if (level->isalias) continue; + + if (ebitmap_cardinality(&level->level->cat) > 0) { + cats = cats_ebitmap_to_str(&level->level->cat, pdb->p_cat_val_to_name); + sepol_printf(out, "(sensitivitycategory %s %s)\n", name, cats); + free(cats); + } + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing sensitivitycategory rules to CIL\n"); + } + + return rc; +} + +static int write_mls_rules_to_cil(FILE *out, struct policydb *pdb) +{ + int rc = 0; + + if (!pdb->mls) { + sepol_printf(out, "(mls false)\n"); + /* CIL requires MLS, even if the kernel binary won't have it */ + write_default_mls_level(out); + return 0; + } + + sepol_printf(out, "(mls true)\n"); + + rc = write_sensitivity_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_category_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_sensitivitycategory_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing mls rules to CIL\n"); + } + + return rc; +} + +static int write_polcap_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct strs *strs; + struct ebitmap_node *node; + const char *name; + uint32_t i; + int rc = 0; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + ebitmap_for_each_bit(&pdb->policycaps, node, i) { + if (!ebitmap_get_bit(&pdb->policycaps, i)) continue; + + name = sepol_polcap_getname(i); + if (name == NULL) { + sepol_log_err("Unknown policy capability id: %i", i); + rc = -1; + goto exit; + } + + rc = strs_create_and_add(strs, "(policycap %s)", 1, name); + if (rc != 0) { + goto exit; + } + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing polcap rules to CIL\n"); + } + + return rc; +} + +static int write_type_attributes_to_cil(FILE *out, struct policydb *pdb) +{ + type_datum_t *type; + char *name; + struct strs *strs; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + type = pdb->type_val_to_struct[i]; + if (type->flavor == TYPE_ATTRIB) { + rc = strs_add(strs, pdb->p_type_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + for (i = 0; i < num; i++) { + name = strs_read_at_index(strs, i); + if (!name) { + rc = -1; + goto exit; + } + sepol_printf(out, "(typeattribute %s)\n", name); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing typeattribute rules to CIL\n"); + } + + return rc; +} + +static int write_role_attributes_to_cil(FILE *out, struct policydb *pdb) +{ + role_datum_t *role; + char *name; + struct strs *strs; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_roles.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_roles.nprim; i++) { + role = pdb->role_val_to_struct[i]; + if (role && role->flavor == ROLE_ATTRIB) { + rc = strs_add(strs, pdb->p_role_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + for (i=0; istate ? "true" : "false"; + + return strs_create_and_add(strs, "(boolean %s %s)", 2, key, value); +} + +static int write_boolean_decl_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct strs *strs; + int rc = 0; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + rc = hashtab_map(pdb->p_bools.table, map_boolean_to_strs, strs); + if (rc != 0) { + goto exit; + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing boolean declarations to CIL\n"); + } + + return rc; +} + +static int write_type_decl_rules_to_cil(FILE *out, struct policydb *pdb) +{ + type_datum_t *type; + struct strs *strs; + char *name; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + type = pdb->type_val_to_struct[i]; + if (type->flavor == TYPE_TYPE && type->primary) { + rc = strs_add(strs, pdb->p_type_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + for (i=0; ip_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + alias = pdb->type_val_to_struct[i]; + if (!alias->primary) { + rc = strs_add(strs, pdb->p_type_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + for (i=0; ip_types.table, name); + if (!alias) { + rc = -1; + goto exit; + } + type = pdb->p_type_val_to_name[alias->s.value - 1]; + sepol_printf(out, "(typealiasactual %s %s)\n", name, type); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing type alias rules to CIL\n"); + } + + return rc; +} + +static int write_type_bounds_rules_to_cil(FILE *out, struct policydb *pdb) +{ + type_datum_t *type; + struct strs *strs; + char *parent; + char *child; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + type = pdb->type_val_to_struct[i]; + if (type->flavor == TYPE_TYPE) { + if (type->bounds > 0) { + rc = strs_add(strs, pdb->p_type_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + for (i=0; ip_types.table, child); + if (!type) { + rc = -1; + goto exit; + } + parent = pdb->p_type_val_to_name[type->bounds - 1]; + sepol_printf(out, "(typebounds %s %s)\n", parent, child); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing type bounds rules to CIL\n"); + } + + return rc; +} + +static int write_type_attribute_sets_to_cil(FILE *out, struct policydb *pdb) +{ + type_datum_t *attr; + struct strs *strs; + ebitmap_t *typemap; + char *name, *types; + unsigned i; + int rc; + + rc = strs_init(&strs, pdb->p_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + attr = pdb->type_val_to_struct[i]; + if (attr->flavor != TYPE_ATTRIB) continue; + name = pdb->p_type_val_to_name[i]; + typemap = &pdb->attr_type_map[i]; + if (ebitmap_cardinality(typemap) == 0) continue; + types = ebitmap_to_str(typemap, pdb->p_type_val_to_name, 1); + if (!types) { + rc = -1; + goto exit; + } + + rc = strs_create_and_add(strs, "(typeattributeset %s (%s))", + 2, name, types); + free(types); + if (rc != 0) { + goto exit; + } + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing typeattributeset rules to CIL\n"); + } + + return rc; +} + +static int write_type_permissive_rules_to_cil(FILE *out, struct policydb *pdb) +{ + type_datum_t *type; + struct strs *strs; + char *name; + unsigned i, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + type = pdb->type_val_to_struct[i]; + if (type->flavor == TYPE_TYPE && (type->flags & TYPE_FLAGS_PERMISSIVE)) { + rc = strs_add(strs, pdb->p_type_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + for (i=0; ispecified != AVTAB_XPERMS_IOCTLFUNCTION) + && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER)) { + return NULL; + } + + for (bit = 0; bit < sizeof(xperms->perms)*8; bit++) { + len = 0; + + if (!xperm_test(bit, xperms->perms)) + continue; + + if (in_range && next_bit_in_range(bit, xperms->perms)) { + /* continue until high value found */ + continue; + } else if (next_bit_in_range(bit, xperms->perms)) { + /* low value */ + low_bit = bit; + in_range = 1; + continue; + } + + if (xperms->specified & AVTAB_XPERMS_IOCTLFUNCTION) { + value = xperms->driver<<8 | bit; + low_value = xperms->driver<<8 | low_bit; + if (in_range) { + len = snprintf(p, remaining, " (range 0x%hx 0x%hx)", low_value, value); + in_range = 0; + } else { + len = snprintf(p, remaining, " 0x%hx", value); + } + } else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) { + value = bit << 8; + low_value = low_bit << 8; + if (in_range) { + len = snprintf(p, remaining, " (range 0x%hx 0x%hx)", low_value, (uint16_t) (value|0xff)); + in_range = 0; + } else { + len = snprintf(p, remaining, " (range 0x%hx 0x%hx)", value, (uint16_t) (value|0xff)); + } + + } + if (len < 0 || len >= remaining) { + return NULL; + } + p += len; + remaining -= len; + } + + if (remaining < 2) { + return NULL; + } + + xpermsbuf[0] = '('; + *p++ = ')'; + *p = '\0'; + + return xpermsbuf; +} + +static char *avtab_node_to_str(struct policydb *pdb, avtab_key_t *key, avtab_datum_t *datum) +{ + uint32_t data = datum->data; + type_datum_t *type; + const char *flavor, *tgt; + char *src, *class, *perms, *new; + char *rule = NULL; + + switch (0xFFF & key->specified) { + case AVTAB_ALLOWED: + flavor = "allow"; + break; + case AVTAB_AUDITALLOW: + flavor = "auditallow"; + break; + case AVTAB_AUDITDENY: + flavor = "dontaudit"; + data = ~data; + break; + case AVTAB_XPERMS_ALLOWED: + flavor = "allowx"; + break; + case AVTAB_XPERMS_AUDITALLOW: + flavor = "auditallowx"; + break; + case AVTAB_XPERMS_DONTAUDIT: + flavor = "dontauditx"; + break; + case AVTAB_TRANSITION: + flavor = "typetransition"; + break; + case AVTAB_MEMBER: + flavor = "typemember"; + break; + case AVTAB_CHANGE: + flavor = "typechange"; + break; + default: + sepol_log_err("Unknown avtab type: %i", key->specified); + goto exit; + } + + src = pdb->p_type_val_to_name[key->source_type - 1]; + tgt = pdb->p_type_val_to_name[key->target_type - 1]; + if (key->source_type == key->target_type && !(key->specified & AVTAB_TYPE)) { + type = pdb->type_val_to_struct[key->source_type - 1]; + if (type->flavor != TYPE_ATTRIB) { + tgt = "self"; + } + } + class = pdb->p_class_val_to_name[key->target_class - 1]; + + if (key->specified & AVTAB_AV) { + perms = sepol_av_to_string(pdb, key->target_class, data); + if (perms == NULL) { + sepol_log_err("Failed to generate permission string"); + goto exit; + } + rule = create_str("(%s %s %s (%s (%s)))", 5, + flavor, src, tgt, class, perms+1); + } else if (key->specified & AVTAB_XPERMS) { + perms = xperms_to_str(datum->xperms); + if (perms == NULL) { + sepol_log_err("Failed to generate extended permission string"); + goto exit; + } + + rule = create_str("(%s %s %s (%s %s (%s)))", 6, + flavor, src, tgt, "ioctl", class, perms); + } else { + new = pdb->p_type_val_to_name[data - 1]; + + rule = create_str("(%s %s %s %s %s)", 5, flavor, src, tgt, class, new); + } + + if (!rule) { + goto exit; + } + + return rule; + +exit: + return NULL; +} + +struct map_avtab_args { + struct policydb *pdb; + uint32_t flavor; + struct strs *strs; +}; + +static int map_avtab_write_helper(avtab_key_t *key, avtab_datum_t *datum, void *args) +{ + struct map_avtab_args *map_args = args; + uint32_t flavor = map_args->flavor; + struct policydb *pdb = map_args->pdb; + struct strs *strs = map_args->strs; + char *rule; + int rc = 0; + + if (key->specified & flavor) { + rule = avtab_node_to_str(pdb, key, datum); + if (!rule) { + rc = -1; + goto exit; + } + rc = strs_add(strs, rule); + if (rc != 0) { + free(rule); + goto exit; + } + } + +exit: + return rc; +} + +static int write_avtab_flavor_to_cil(FILE *out, struct policydb *pdb, uint32_t flavor, int indent) +{ + struct map_avtab_args args; + struct strs *strs; + int rc = 0; + + rc = strs_init(&strs, 1000); + if (rc != 0) { + goto exit; + } + + args.pdb = pdb; + args.flavor = flavor; + args.strs = strs; + + rc = avtab_map(&pdb->te_avtab, map_avtab_write_helper, &args); + if (rc != 0) { + goto exit; + } + + strs_sort(strs); + strs_write_each_indented(strs, out, indent); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + return rc; +} + +static int write_avtab_to_cil(FILE *out, struct policydb *pdb, int indent) +{ + unsigned i; + int rc = 0; + + for (i = 0; i < AVTAB_FLAVORS_SZ; i++) { + rc = write_avtab_flavor_to_cil(out, pdb, avtab_flavors[i], indent); + if (rc != 0) { + goto exit; + } + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing avtab rules to CIL\n"); + } + + return rc; +} + +struct map_filename_trans_args { + struct policydb *pdb; + struct strs *strs; +}; + +static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) +{ + filename_trans_t *ft = (filename_trans_t *)key; + filename_trans_datum_t *datum = data; + struct map_filename_trans_args *map_args = arg; + struct policydb *pdb = map_args->pdb; + struct strs *strs = map_args->strs; + char *src, *tgt, *class, *filename, *new; + + src = pdb->p_type_val_to_name[ft->stype - 1]; + tgt = pdb->p_type_val_to_name[ft->ttype - 1]; + class = pdb->p_class_val_to_name[ft->tclass - 1]; + filename = ft->name; + new = pdb->p_type_val_to_name[datum->otype - 1]; + + return strs_create_and_add(strs, "(typetransition %s %s %s %s %s)", 5, + src, tgt, class, filename, new); +} + +static int write_filename_trans_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct map_filename_trans_args args; + struct strs *strs; + int rc = 0; + + rc = strs_init(&strs, 100); + if (rc != 0) { + goto exit; + } + + args.pdb = pdb; + args.strs = strs; + + rc = hashtab_map(pdb->filename_trans, map_filename_trans_to_str, &args); + if (rc != 0) { + goto exit; + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing filename typetransition rules to CIL\n"); + } + + return rc; +} + +static char *level_to_str(struct policydb *pdb, struct mls_level *level) +{ + ebitmap_t *cats = &level->cat; + char *level_str = NULL; + char *sens_str = pdb->p_sens_val_to_name[level->sens - 1]; + char *cats_str; + + if (ebitmap_cardinality(cats) > 0) { + cats_str = cats_ebitmap_to_str(cats, pdb->p_cat_val_to_name); + level_str = create_str("(%s %s)", 2, sens_str, cats_str); + free(cats_str); + } else { + level_str = create_str("(%s)", 1, sens_str); + } + + return level_str; +} + +static char *range_to_str(struct policydb *pdb, mls_range_t *range) +{ + char *low = NULL; + char *high = NULL; + char *range_str = NULL; + + low = level_to_str(pdb, &range->level[0]); + if (!low) { + goto exit; + } + + high = level_to_str(pdb, &range->level[1]); + if (!high) { + goto exit; + } + + range_str = create_str("(%s %s)", 2, low, high); + +exit: + free(low); + free(high); + + return range_str; +} + +struct map_range_trans_args { + struct policydb *pdb; + struct strs *strs; +}; + +static int map_range_trans_to_str(hashtab_key_t key, void *data, void *arg) +{ + range_trans_t *rt = (range_trans_t *)key; + mls_range_t *mls_range = data; + struct map_range_trans_args *map_args = arg; + struct policydb *pdb = map_args->pdb; + struct strs *strs = map_args->strs; + char *src, *tgt, *class, *range; + int rc; + + src = pdb->p_type_val_to_name[rt->source_type - 1]; + tgt = pdb->p_type_val_to_name[rt->target_type - 1]; + class = pdb->p_class_val_to_name[rt->target_class - 1]; + range = range_to_str(pdb, mls_range); + if (!range) { + rc = -1; + goto exit; + } + + rc = strs_create_and_add(strs, "(rangetransition %s %s %s %s)", 4, + src, tgt, class, range); + free(range); + if (rc != 0) { + goto exit; + } + +exit: + return rc; +} + +static int write_range_trans_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct map_range_trans_args args; + struct strs *strs; + int rc = 0; + + rc = strs_init(&strs, 100); + if (rc != 0) { + goto exit; + } + + args.pdb = pdb; + args.strs = strs; + + rc = hashtab_map(pdb->range_tr, map_range_trans_to_str, &args); + if (rc != 0) { + goto exit; + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing range transition rules to CIL\n"); + } + + return rc; +} + +static int write_cond_av_list_to_cil(FILE *out, struct policydb *pdb, cond_av_list_t *cond_list, int indent) +{ + cond_av_list_t *cond_av; + avtab_ptr_t node; + uint32_t flavor; + avtab_key_t *key; + avtab_datum_t *datum; + struct strs *strs; + char *rule; + unsigned i; + int rc; + + for (i = 0; i < AVTAB_FLAVORS_SZ; i++) { + flavor = avtab_flavors[i]; + rc = strs_init(&strs, 64); + if (rc != 0) { + goto exit; + } + + for (cond_av = cond_list; cond_av != NULL; cond_av = cond_av->next) { + node = cond_av->node; + key = &node->key; + datum = &node->datum; + if (key->specified & flavor) { + rule = avtab_node_to_str(pdb, key, datum); + if (!rule) { + rc = -1; + goto exit; + } + rc = strs_add(strs, rule); + if (rc != 0) { + free(rule); + goto exit; + } + } + } + + strs_sort(strs); + strs_write_each_indented(strs, out, indent); + strs_free_all(strs); + strs_destroy(&strs); + } + + return 0; + +exit: + return rc; +} + +struct cond_data { + char *expr; + struct cond_node *cond; +}; + +static int cond_node_cmp(const void *a, const void *b) +{ + const struct cond_data *aa = a; + const struct cond_data *bb = b; + return strcmp(aa->expr, bb->expr); +} + +static int write_cond_nodes_to_cil(FILE *out, struct policydb *pdb) +{ + struct cond_data *cond_data; + char *expr; + struct cond_node *cond; + unsigned i, num = 0; + int rc = 0; + + for (cond = pdb->cond_list; cond != NULL; cond = cond->next) { + num++; + } + + cond_data = calloc(sizeof(struct cond_data), num); + if (!cond_data) { + rc = -1; + goto exit; + } + + i = 0; + for (cond = pdb->cond_list; cond != NULL; cond = cond->next) { + cond_data[i].cond = cond; + expr = cond_expr_to_str(pdb, cond->expr); + if (!expr) { + num = i; + goto exit; + } + cond_data[i].expr = expr; + i++; + } + + qsort(cond_data, num, sizeof(*cond_data), cond_node_cmp); + + for (i=0; itrue_list != NULL) { + sepol_indent(out, 1); + sepol_printf(out, "(true\n"); + rc = write_cond_av_list_to_cil(out, pdb, cond->true_list, 2); + if (rc != 0) { + goto exit; + } + sepol_indent(out, 1); + sepol_printf(out, ")\n"); + } + + if (cond->false_list != NULL) { + sepol_indent(out, 1); + sepol_printf(out, "(false\n"); + rc = write_cond_av_list_to_cil(out, pdb, cond->false_list, 2); + if (rc != 0) { + goto exit; + } + sepol_indent(out, 1); + sepol_printf(out, ")\n"); + } + sepol_printf(out, ")\n"); + } + +exit: + if (cond_data) { + for (i=0; ip_roles.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_roles.nprim; i++) { + role = pdb->role_val_to_struct[i]; + if (role && role->flavor == ROLE_ROLE) { + rc = strs_add(strs, pdb->p_role_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + + for (i=0; ip_roles.table, child); + if (!role) { + rc = -1; + goto exit; + } + + if (role->bounds > 0) { + parent = pdb->p_role_val_to_name[role->bounds - 1]; + sepol_printf(out, "(rolebounds %s %s)\n", parent, child); + } + } + + for (i=0; ip_roles.table, name); + if (!role) { + rc = -1; + goto exit; + } + types = &role->types.types; + if (types && (ebitmap_cardinality(types) > 0)) { + rc = strs_init(&type_strs, pdb->p_types.nprim); + if (rc != 0) { + goto exit; + } + rc = ebitmap_to_strs(types, type_strs, pdb->p_type_val_to_name); + if (rc != 0) { + strs_destroy(&type_strs); + goto exit; + } + strs_sort(type_strs); + + num_types = strs_num_items(type_strs); + for (j=0; jp_types.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_types.nprim; i++) { + type_datum = pdb->type_val_to_struct[i]; + if (type_datum->flavor == TYPE_TYPE && type_datum->primary) { + rc = strs_add(strs, pdb->p_type_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + + for (i=0; irole_tr; + struct strs *strs; + char *role, *type, *class, *new; + int rc = 0; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + while (curr) { + role = pdb->p_role_val_to_name[curr->role - 1]; + type = pdb->p_type_val_to_name[curr->type - 1]; + class = pdb->p_class_val_to_name[curr->tclass - 1]; + new = pdb->p_role_val_to_name[curr->new_role - 1]; + + rc = strs_create_and_add(strs, "(roletransition %s %s %s %s)", 4, + role, type, class, new); + if (rc != 0) { + goto exit; + } + + curr = curr->next; + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing role transition rules to CIL\n"); + } + + return rc; +} + +static int write_role_allow_rules_to_cil(FILE *out, struct policydb *pdb) +{ + role_allow_t *curr = pdb->role_allow; + struct strs *strs; + char *role, *new; + int rc = 0; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + while (curr) { + role = pdb->p_role_val_to_name[curr->role - 1]; + new = pdb->p_role_val_to_name[curr->new_role - 1]; + + rc = strs_create_and_add(strs, "(roleallow %s %s)", 2, role, new); + if (rc != 0) { + goto exit; + } + + curr = curr->next; + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing role allow rules to CIL\n"); + } + + return rc; +} + +static int write_user_decl_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct user_datum *user; + struct strs *strs, *role_strs; + char *name, *role, *level, *range; + struct ebitmap *roles; + unsigned i, j, num, num_roles; + int rc = 0; + + rc = strs_init(&strs, pdb->p_users.nprim); + if (rc != 0) { + goto exit; + } + + for (i=0; i < pdb->p_users.nprim; i++) { + rc = strs_add(strs, pdb->p_user_val_to_name[i]); + if (rc != 0) { + goto exit; + } + } + + strs_sort(strs); + + num = strs_num_items(strs); + + for (i=0; ip_users.table, name); + if (!user) { + rc = -1; + goto exit; + } + + roles = &user->roles.roles; + if (roles && (ebitmap_cardinality(roles) > 0)) { + rc = strs_init(&role_strs, pdb->p_roles.nprim); + if (rc != 0) { + goto exit; + } + rc = ebitmap_to_strs(roles, role_strs, pdb->p_role_val_to_name); + if (rc != 0) { + strs_destroy(&role_strs); + goto exit; + } + + rc = strs_add(role_strs, DEFAULT_OBJECT); + if (rc != 0) { + strs_destroy(&role_strs); + goto exit; + } + + strs_sort(role_strs); + + num_roles = strs_num_items(role_strs); + for (j=0; jp_users.table, name); + if (!user) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(userlevel %s ", name); + + if (pdb->mls) { + level = level_to_str(pdb, &user->exp_dfltlevel); + if (!level) { + rc = -1; + goto exit; + } + sepol_printf(out, "%s", level); + free(level); + } else { + sepol_printf(out, "%s", DEFAULT_LEVEL); + } + sepol_printf(out, ")\n"); + } + + for (i=0; ip_users.table, name); + if (!user) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(userrange %s ", name); + if (pdb->mls) { + range = range_to_str(pdb, &user->exp_range); + if (!range) { + rc = -1; + goto exit; + } + sepol_printf(out, "%s", range); + free(range); + } else { + sepol_printf(out, "(%s %s)", DEFAULT_LEVEL, DEFAULT_LEVEL); + } + sepol_printf(out, ")\n"); + } + + strs_destroy(&strs); + +exit: + if (rc != 0) { + sepol_log_err("Error writing user declarations to CIL\n"); + } + + return rc; +} + +static char *context_to_str(struct policydb *pdb, struct context_struct *con) +{ + char *user, *role, *type, *range; + char *ctx = NULL; + + user = pdb->p_user_val_to_name[con->user - 1]; + role = pdb->p_role_val_to_name[con->role - 1]; + type = pdb->p_type_val_to_name[con->type - 1]; + + if (pdb->mls) { + range = range_to_str(pdb, &con->range); + } else { + range = create_str("(%s %s)", 2, DEFAULT_LEVEL, DEFAULT_LEVEL); + } + if (!range) { + goto exit; + } + + ctx = create_str("(%s %s %s %s)", 4, user, role, type, range); + free(range); + +exit: + return ctx; +} + +static int write_sid_context_rules_to_cil(FILE *out, struct policydb *pdb, const char *const *sid_to_str) +{ + struct ocontext *isid; + struct strs *strs; + const char *sid; + char *ctx, *rule; + unsigned i; + int rc = -1; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + for (isid = pdb->ocontexts[0]; isid != NULL; isid = isid->next) { + i = isid->sid[0]; + sid = sid_to_str[i]; + ctx = context_to_str(pdb, &isid->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + rule = create_str("(sidcontext %s %s)", 2, sid, ctx); + free(ctx); + if (!rule) { + rc = -1; + goto exit; + } + + rc = strs_add_at_index(strs, rule, i); + if (rc != 0) { + free(rule); + goto exit; + } + } + + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing sidcontext rules to CIL\n"); + } + + return rc; +} + +static int write_selinux_isid_rules_to_cil(FILE *out, struct policydb *pdb) +{ + return write_sid_context_rules_to_cil(out, pdb, selinux_sid_to_str); +} + +static int write_selinux_fsuse_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *fsuse; + const char *behavior; + char *name, *ctx; + int rc = 0; + + for (fsuse = pdb->ocontexts[5]; fsuse != NULL; fsuse = fsuse->next) { + switch (fsuse->v.behavior) { + case SECURITY_FS_USE_XATTR: behavior = "xattr"; break; + case SECURITY_FS_USE_TRANS: behavior = "trans"; break; + case SECURITY_FS_USE_TASK: behavior = "task"; break; + default: + sepol_log_err("Unknown fsuse behavior: %i", fsuse->v.behavior); + rc = -1; + goto exit; + } + + name = fsuse->u.name; + ctx = context_to_str(pdb, &fsuse->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(fsuse %s %s %s)\n", behavior, name, ctx); + + free(ctx); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing fsuse rules to CIL\n"); + } + + return rc; +} + +static int write_genfscon_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct genfs *genfs; + struct ocontext *ocon; + struct strs *strs; + char *fstype, *name, *ctx; + int rc; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) { + for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) { + fstype = genfs->fstype; + name = ocon->u.name; + + ctx = context_to_str(pdb, &ocon->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + rc = strs_create_and_add(strs, "(genfscon %s %s %s)", 3, + fstype, name, ctx); + free(ctx); + if (rc != 0) { + goto exit; + } + } + } + + strs_sort(strs); + strs_write_each(strs, out); + +exit: + strs_free_all(strs); + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing genfscon rules to CIL\n"); + } + + return rc; +} + +static int write_selinux_port_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *portcon; + const char *protocol; + uint16_t low; + uint16_t high; + char low_high_str[44]; /* 2^64 <= 20 digits so "(low high)" <= 44 chars */ + char *ctx; + int rc = 0; + + for (portcon = pdb->ocontexts[2]; portcon != NULL; portcon = portcon->next) { + switch (portcon->u.port.protocol) { + case IPPROTO_TCP: protocol = "tcp"; break; + case IPPROTO_UDP: protocol = "udp"; break; + case IPPROTO_DCCP: protocol = "dccp"; break; + default: + sepol_log_err("Unknown portcon protocol: %i", portcon->u.port.protocol); + rc = -1; + goto exit; + } + + low = portcon->u.port.low_port; + high = portcon->u.port.high_port; + if (low == high) { + rc = snprintf(low_high_str, 44, "%u", low); + } else { + rc = snprintf(low_high_str, 44, "(%u %u)", low, high); + } + if (rc < 0 || rc >= 44) { + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &portcon->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(portcon %s %s %s)\n", protocol, low_high_str, ctx); + + free(ctx); + } + + rc = 0; + +exit: + if (rc != 0) { + sepol_log_err("Error writing portcon rules to CIL\n"); + } + + return rc; +} + +static int write_selinux_netif_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *netif; + char *name, *ctx1, *ctx2; + int rc = 0; + + for (netif = pdb->ocontexts[3]; netif != NULL; netif = netif->next) { + name = netif->u.name; + ctx1 = context_to_str(pdb, &netif->context[0]); + if (!ctx1) { + rc = -1; + goto exit; + } + ctx2 = context_to_str(pdb, &netif->context[1]); + if (!ctx2) { + free(ctx1); + rc = -1; + goto exit; + } + + sepol_printf(out, "(netifcon %s %s %s)\n", name, ctx1, ctx2); + + free(ctx1); + free(ctx2); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing netifcon rules to CIL\n"); + } + + return rc; +} + +static int write_selinux_node_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *node; + char addr[INET_ADDRSTRLEN]; + char mask[INET_ADDRSTRLEN]; + char *ctx; + int rc = 0; + + for (node = pdb->ocontexts[4]; node != NULL; node = node->next) { + if (inet_ntop(AF_INET, &node->u.node.addr, addr, INET_ADDRSTRLEN) == NULL) { + sepol_log_err("Nodecon address is invalid: %s", strerror(errno)); + rc = -1; + goto exit; + } + + if (inet_ntop(AF_INET, &node->u.node.mask, mask, INET_ADDRSTRLEN) == NULL) { + sepol_log_err("Nodecon mask is invalid: %s", strerror(errno)); + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &node->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(nodecon (%s) (%s) %s)\n", addr, mask, ctx); + + free(ctx); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing nodecon rules to CIL\n"); + } + + return rc; +} + +static int write_selinux_node6_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *node; + char addr[INET6_ADDRSTRLEN]; + char mask[INET6_ADDRSTRLEN]; + char *ctx; + int rc = 0; + + for (node = pdb->ocontexts[6]; node != NULL; node = node->next) { + if (inet_ntop(AF_INET6, &node->u.node6.addr, addr, INET6_ADDRSTRLEN) == NULL) { + sepol_log_err("Nodecon address is invalid: %s", strerror(errno)); + rc = -1; + goto exit; + } + + if (inet_ntop(AF_INET6, &node->u.node6.mask, mask, INET6_ADDRSTRLEN) == NULL) { + sepol_log_err("Nodecon mask is invalid: %s", strerror(errno)); + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &node->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(nodecon (%s) (%s) %s)\n", addr, mask, ctx); + + free(ctx); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing nodecon rules to CIL\n"); + } + + return rc; +} + +static int write_xen_isid_rules_to_cil(FILE *out, struct policydb *pdb) +{ + return write_sid_context_rules_to_cil(out, pdb, xen_sid_to_str); +} + +static int write_xen_pirq_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *pirq; + char pirq_str[21]; /* 2^64-1 <= 20 digits */ + char *ctx; + int rc = 0; + + for (pirq = pdb->ocontexts[1]; pirq != NULL; pirq = pirq->next) { + rc = snprintf(pirq_str, 21, "%i", pirq->u.pirq); + if (rc < 0 || rc >= 21) { + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &pirq->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(pirqcon %s %s)\n", pirq_str, ctx); + + free(ctx); + } + + rc = 0; + +exit: + if (rc != 0) { + sepol_log_err("Error writing pirqcon rules to CIL\n"); + } + + return rc; +} + +static int write_xen_ioport_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *ioport; + uint32_t low; + uint32_t high; + char low_high_str[40]; /* 2^64-1 <= 16 digits (hex) so (low high) < 40 chars */ + char *ctx; + int rc = 0; + + for (ioport = pdb->ocontexts[2]; ioport != NULL; ioport = ioport->next) { + low = ioport->u.ioport.low_ioport; + high = ioport->u.ioport.high_ioport; + if (low == high) { + rc = snprintf(low_high_str, 40, "0x%x", low); + } else { + rc = snprintf(low_high_str, 40, "(0x%x 0x%x)", low, high); + } + if (rc < 0 || rc >= 40) { + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &ioport->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(ioportcon %s %s)\n", low_high_str, ctx); + + free(ctx); + } + + rc = 0; + +exit: + if (rc != 0) { + sepol_log_err("Error writing ioportcon rules to CIL\n"); + } + + return rc; +} + +static int write_xen_iomem_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *iomem; + uint64_t low; + uint64_t high; + char low_high_str[40]; /* 2^64-1 <= 16 digits (hex) so (low high) < 40 chars */ + char *ctx; + int rc = 0; + + for (iomem = pdb->ocontexts[3]; iomem != NULL; iomem = iomem->next) { + low = iomem->u.iomem.low_iomem; + high = iomem->u.iomem.high_iomem; + if (low == high) { + rc = snprintf(low_high_str, 40, "0x%"PRIx64, low); + } else { + rc = snprintf(low_high_str, 40, "(0x%"PRIx64" 0x%"PRIx64")", low, high); + } + if (rc < 0 || rc >= 40) { + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &iomem->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(iomemcon %s %s)\n", low_high_str, ctx); + + free(ctx); + } + + rc = 0; + +exit: + if (rc != 0) { + sepol_log_err("Error writing iomemcon rules to CIL\n"); + } + + return rc; +} + +static int write_xen_pcidevice_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *pcid; + char device_str[20]; /* 2^64-1 <= 16 digits (hex) so (low high) < 19 chars */ + char *ctx; + int rc = 0; + + for (pcid = pdb->ocontexts[4]; pcid != NULL; pcid = pcid->next) { + rc = snprintf(device_str, 20, "0x%lx", (unsigned long)pcid->u.device); + if (rc < 0 || rc >= 20) { + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &pcid->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(pcidevicecon %s %s)\n", device_str, ctx); + + free(ctx); + } + + rc = 0; + +exit: + if (rc != 0) { + sepol_log_err("Error writing pcidevicecon rules to CIL\n"); + } + + return rc; +} + +static int write_xen_devicetree_rules_to_cil(FILE *out, struct policydb *pdb) +{ + struct ocontext *dtree; + char *name, *ctx; + int rc = 0; + + for (dtree = pdb->ocontexts[5]; dtree != NULL; dtree = dtree->next) { + name = dtree->u.name; + ctx = context_to_str(pdb, &dtree->context[0]); + if (!ctx) { + rc = -1; + goto exit; + } + + sepol_printf(out, "(devicetreecon %s %s)\n", name, ctx); + + free(ctx); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing devicetreecon rules to CIL\n"); + } + + return rc; +} + +int sepol_kernel_policydb_to_cil(FILE *out, struct policydb *pdb) +{ + struct strs *mls_constraints = NULL; + struct strs *non_mls_constraints = NULL; + struct strs *mls_validatetrans = NULL; + struct strs *non_mls_validatetrans = NULL; + int rc = 0; + + rc = strs_init(&mls_constraints, 32); + if (rc != 0) { + goto exit; + } + + rc = strs_init(&non_mls_constraints, 32); + if (rc != 0) { + goto exit; + } + + rc = strs_init(&mls_validatetrans, 32); + if (rc != 0) { + goto exit; + } + + rc = strs_init(&non_mls_validatetrans, 32); + if (rc != 0) { + goto exit; + } + + if (pdb == NULL) { + sepol_log_err("No policy"); + rc = -1; + goto exit; + } + + if (pdb->policy_type != SEPOL_POLICY_KERN) { + sepol_log_err("Policy is not a kernel policy"); + rc = -1; + goto exit; + } + + rc = constraint_rules_to_strs(pdb, mls_constraints, non_mls_constraints); + if (rc != 0) { + goto exit; + } + + rc = validatetrans_rules_to_strs(pdb, mls_validatetrans, non_mls_validatetrans); + if (rc != 0) { + goto exit; + } + + rc = write_handle_unknown_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_class_decl_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_sid_decl_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_default_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_mls_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + strs_write_each(mls_constraints, out); + strs_write_each(mls_validatetrans, out); + + rc = write_polcap_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_attributes_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_attributes_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_boolean_decl_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_decl_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_alias_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_bounds_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_attribute_sets_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_permissive_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_avtab_to_cil(out, pdb, 0); + if (rc != 0) { + goto exit; + } + + rc = write_filename_trans_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + if (pdb->mls) { + rc = write_range_trans_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + } + + rc = write_cond_nodes_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_decl_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_transition_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_allow_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_user_decl_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + strs_write_each(non_mls_constraints, out); + strs_write_each(non_mls_validatetrans, out); + + rc = sort_ocontexts(pdb); + if (rc != 0) { + goto exit; + } + + if (pdb->target_platform == SEPOL_TARGET_SELINUX) { + rc = write_selinux_isid_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_fsuse_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_genfscon_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_port_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_netif_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_node_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_node6_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + } else if (pdb->target_platform == SEPOL_TARGET_XEN) { + rc = write_xen_isid_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_pirq_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_ioport_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_iomem_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_pcidevice_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_devicetree_rules_to_cil(out, pdb); + if (rc != 0) { + goto exit; + } + } + +exit: + strs_free_all(mls_constraints); + strs_destroy(&mls_constraints); + strs_free_all(non_mls_constraints); + strs_destroy(&non_mls_constraints); + strs_free_all(mls_validatetrans); + strs_destroy(&mls_validatetrans); + strs_free_all(non_mls_validatetrans); + strs_destroy(&non_mls_validatetrans); + + return rc; +} diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c new file mode 100644 index 0000000..45adc5d --- /dev/null +++ b/libsepol/src/kernel_to_common.c @@ -0,0 +1,677 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif + +#include +#include +#include + +#include "kernel_to_common.h" + + +void sepol_log_err(const char *fmt, ...) +{ + va_list argptr; + va_start(argptr, fmt); + if (vfprintf(stderr, fmt, argptr) < 0) { + _exit(EXIT_FAILURE); + } + va_end(argptr); + if (fprintf(stderr, "\n") < 0) { + _exit(EXIT_FAILURE); + } +} + +void sepol_indent(FILE *out, int indent) +{ + if (fprintf(out, "%*s", indent * 4, "") < 0) { + sepol_log_err("Failed to write to output"); + } +} + +void sepol_printf(FILE *out, const char *fmt, ...) +{ + va_list argptr; + va_start(argptr, fmt); + if (vfprintf(out, fmt, argptr) < 0) { + sepol_log_err("Failed to write to output"); + } + va_end(argptr); +} + +__attribute__ ((format(printf, 1, 0))) +static char *create_str_helper(const char *fmt, int num, va_list vargs) +{ + va_list vargs2; + char *str = NULL; + char *s; + size_t len; + int i, rc; + + va_copy(vargs2, vargs); + + len = strlen(fmt) + 1; /* +1 for '\0' */ + + for (i=0; i= (int)len) { + goto exit; + } + + return str; + +exit: + free(str); + return NULL; +} + +char *create_str(const char *fmt, int num, ...) +{ + char *str = NULL; + va_list vargs; + + va_start(vargs, num); + str = create_str_helper(fmt, num, vargs); + va_end(vargs); + + return str; +} + +int strs_init(struct strs **strs, size_t size) +{ + struct strs *new; + + *strs = NULL; + + new = malloc(sizeof(struct strs)); + if (!new) { + sepol_log_err("Out of memory"); + return -1; + } + + new->list = calloc(sizeof(char *), size); + if (!new->list) { + sepol_log_err("Out of memory"); + free(new); + return -1; + } + + new->num = 0; + new->size = size; + + *strs = new; + + return 0; +} + +void strs_destroy(struct strs **strs) +{ + if (!strs || !*strs) { + return; + } + + free((*strs)->list); + (*strs)->list = NULL; + (*strs)->num = 0; + (*strs)->size = 0; + free(*strs); + *strs = NULL; +} + +void strs_free_all(struct strs *strs) +{ + if (!strs) { + return; + } + + while (strs->num > 0) { + strs->num--; + free(strs->list[strs->num]); + } +} + +int strs_add(struct strs *strs, char *s) +{ + if (strs->num + 1 > strs->size) { + char **new; + unsigned i = strs->size; + strs->size *= 2; + new = realloc(strs->list, sizeof(char *)*strs->size); + if (!new) { + sepol_log_err("Out of memory"); + return -1; + } + strs->list = new; + memset(&strs->list[i], 0, sizeof(char *)*(strs->size-i)); + } + + strs->list[strs->num] = s; + strs->num++; + + return 0; +} + +int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...) +{ + char *str; + va_list vargs; + int rc; + + va_start(vargs, num); + str = create_str_helper(fmt, num, vargs); + va_end(vargs); + + if (!str) { + rc = -1; + goto exit; + } + + rc = strs_add(strs, str); + if (rc != 0) { + free(str); + goto exit; + } + + return 0; + +exit: + return rc; +} + +char *strs_remove_last(struct strs *strs) +{ + if (strs->num == 0) { + return NULL; + } + strs->num--; + return strs->list[strs->num]; +} + +int strs_add_at_index(struct strs *strs, char *s, unsigned index) +{ + if (index >= strs->size) { + char **new; + unsigned i = strs->size; + while (index >= strs->size) { + strs->size *= 2; + } + new = realloc(strs->list, sizeof(char *)*strs->size); + if (!new) { + sepol_log_err("Out of memory"); + return -1; + } + strs->list = new; + memset(&strs->list[i], 0, sizeof(char *)*(strs->size - i)); + } + + strs->list[index] = s; + if (index >= strs->num) { + strs->num = index+1; + } + + return 0; +} + +char *strs_read_at_index(struct strs *strs, unsigned index) +{ + if (index >= strs->num) { + return NULL; + } + + return strs->list[index]; +} + +static int strs_cmp(const void *a, const void *b) +{ + char *const *aa = a; + char *const *bb = b; + return strcmp(*aa,*bb); +} + +void strs_sort(struct strs *strs) +{ + if (strs->num == 0) { + return; + } + qsort(strs->list, strs->num, sizeof(char *), strs_cmp); +} + +unsigned strs_num_items(struct strs *strs) +{ + return strs->num; +} + +size_t strs_len_items(struct strs *strs) +{ + unsigned i; + size_t len = 0; + + for (i=0; inum; i++) { + if (!strs->list[i]) continue; + len += strlen(strs->list[i]); + } + + return len; +} + +char *strs_to_str(struct strs *strs) +{ + char *str = NULL; + size_t len = 0; + char *p; + unsigned i; + int rc; + + if (strs->num == 0) { + goto exit; + } + + /* strs->num added because either ' ' or '\0' follows each item */ + len = strs_len_items(strs) + strs->num; + str = malloc(len); + if (!str) { + sepol_log_err("Out of memory"); + goto exit; + } + + p = str; + for (i=0; inum; i++) { + if (!strs->list[i]) continue; + len = strlen(strs->list[i]); + rc = snprintf(p, len+1, "%s", strs->list[i]); + if (rc < 0 || rc > (int)len) { + free(str); + str = NULL; + goto exit; + } + p += len; + if (i < strs->num - 1) { + *p++ = ' '; + } + } + + *p = '\0'; + +exit: + return str; +} + +void strs_write_each(struct strs *strs, FILE *out) +{ + unsigned i; + + for (i=0; inum; i++) { + if (!strs->list[i]) { + continue; + } + sepol_printf(out, "%s\n",strs->list[i]); + } +} + +void strs_write_each_indented(struct strs *strs, FILE *out, int indent) +{ + unsigned i; + + for (i=0; inum; i++) { + if (!strs->list[i]) { + continue; + } + sepol_indent(out, indent); + sepol_printf(out, "%s\n",strs->list[i]); + } +} + +int hashtab_ordered_to_strs(char *key, void *data, void *args) +{ + struct strs *strs = (struct strs *)args; + symtab_datum_t *datum = data; + + return strs_add_at_index(strs, key, datum->value-1); +} + +int ebitmap_to_strs(struct ebitmap *map, struct strs *strs, char **val_to_name) +{ + struct ebitmap_node *node; + uint32_t i; + int rc; + + ebitmap_for_each_bit(map, node, i) { + if (!ebitmap_get_bit(map, i)) continue; + + rc = strs_add(strs, val_to_name[i]); + if (rc != 0) { + return -1; + } + } + + return 0; +} + +char *ebitmap_to_str(struct ebitmap *map, char **val_to_name, int sort) +{ + struct strs *strs; + char *str = NULL; + int rc; + + rc = strs_init(&strs, 32); + if (rc != 0) { + goto exit; + } + + rc = ebitmap_to_strs(map, strs, val_to_name); + if (rc != 0) { + goto exit; + } + + if (sort) { + strs_sort(strs); + } + + str = strs_to_str(strs); + +exit: + strs_destroy(&strs); + + return str; +} + +int stack_init(struct strs **stack) +{ + return strs_init(stack, STACK_SIZE); +} + +void stack_destroy(struct strs **stack) +{ + return strs_destroy(stack); +} + +int stack_push(struct strs *stack, char *s) +{ + return strs_add(stack, s); +} + +char *stack_pop(struct strs *stack) +{ + return strs_remove_last(stack); +} + +int stack_empty(struct strs *stack) +{ + return strs_num_items(stack) == 0; +} + +static int compare_ranges(uint64_t l1, uint64_t h1, uint64_t l2, uint64_t h2) +{ + uint64_t d1, d2; + + d1 = h1-l1; + d2 = h2-l2; + + if (d1 < d2) { + return -1; + } else if (d1 > d2) { + return 1; + } else { + if (l1 < l2) { + return -1; + } else if (l1 > l2) { + return 1; + } + } + + return 0; +} + +static int fsuse_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + if ((*aa)->v.behavior != (*bb)->v.behavior) { + if ((*aa)->v.behavior < (*bb)->v.behavior) { + return -1; + } else { + return 1; + } + } + + return strcmp((*aa)->u.name, (*bb)->u.name); +} + +static int portcon_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + int rc; + + rc = compare_ranges((*aa)->u.port.low_port, (*aa)->u.port.high_port, + (*bb)->u.port.low_port, (*bb)->u.port.high_port); + if (rc == 0) { + if ((*aa)->u.port.protocol == (*bb)->u.port.protocol) { + rc = 0; + } else if ((*aa)->u.port.protocol == IPPROTO_TCP) { + rc = -1; + } else { + rc = 1; + } + } + + return rc; +} + +static int netif_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + return strcmp((*aa)->u.name, (*bb)->u.name); +} + +static int node_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + int rc; + + rc = memcmp(&(*aa)->u.node.mask, &(*bb)->u.node.mask, sizeof((*aa)->u.node.mask)); + if (rc > 0) { + return -1; + } else if (rc < 0) { + return 1; + } + + return memcmp(&(*aa)->u.node.addr, &(*bb)->u.node.addr, sizeof((*aa)->u.node.addr)); +} + +static int node6_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + int rc; + + rc = memcmp(&(*aa)->u.node6.mask, &(*bb)->u.node6.mask, sizeof((*aa)->u.node6.mask)); + if (rc > 0) { + return -1; + } else if (rc < 0) { + return 1; + } + + return memcmp(&(*aa)->u.node6.addr, &(*bb)->u.node6.addr, sizeof((*aa)->u.node6.addr)); +} + +static int pirq_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + if ((*aa)->u.pirq < (*bb)->u.pirq) { + return -1; + } else if ((*aa)->u.pirq > (*bb)->u.pirq) { + return 1; + } + + return 0; +} + +static int ioport_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + return compare_ranges((*aa)->u.ioport.low_ioport, (*aa)->u.ioport.high_ioport, + (*bb)->u.ioport.low_ioport, (*bb)->u.ioport.high_ioport); +} + +static int iomem_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + return compare_ranges((*aa)->u.iomem.low_iomem, (*aa)->u.iomem.high_iomem, + (*bb)->u.iomem.low_iomem, (*bb)->u.iomem.high_iomem); +} + +static int pcid_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + if ((*aa)->u.device < (*bb)->u.device) { + return -1; + } else if ((*aa)->u.device > (*bb)->u.device) { + return 1; + } + + return 0; +} + +static int dtree_data_cmp(const void *a, const void *b) +{ + struct ocontext *const *aa = a; + struct ocontext *const *bb = b; + + return strcmp((*aa)->u.name, (*bb)->u.name); +} + +static int sort_ocontext_data(struct ocontext **ocons, int (*cmp)(const void *, const void *)) +{ + struct ocontext *ocon; + struct ocontext **data; + unsigned i, num; + + num = 0; + for (ocon = *ocons; ocon != NULL; ocon = ocon->next) { + num++; + } + + if (num == 0) { + return 0; + } + + data = calloc(sizeof(*data), num); + if (!data) { + sepol_log_err("Out of memory\n"); + return -1; + } + + i = 0; + for (ocon = *ocons; ocon != NULL; ocon = ocon->next) { + data[i] = ocon; + i++; + } + + qsort(data, num, sizeof(*data), cmp); + + *ocons = data[0]; + for (i=1; i < num; i++) { + data[i-1]->next = data[i]; + } + data[num-1]->next = NULL; + + free(data); + + return 0; +} + +int sort_ocontexts(struct policydb *pdb) +{ + int rc = 0; + + if (pdb->target_platform == SEPOL_TARGET_SELINUX) { + rc = sort_ocontext_data(&pdb->ocontexts[5], fsuse_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[2], portcon_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[3], netif_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[4], node_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[6], node6_data_cmp); + if (rc != 0) { + goto exit; + } + } else if (pdb->target_platform == SEPOL_TARGET_XEN) { + rc = sort_ocontext_data(&pdb->ocontexts[1], pirq_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[2], ioport_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[3], iomem_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[4], pcid_data_cmp); + if (rc != 0) { + goto exit; + } + + rc = sort_ocontext_data(&pdb->ocontexts[5], dtree_data_cmp); + if (rc != 0) { + goto exit; + } + } + +exit: + if (rc != 0) { + sepol_log_err("Error sorting ocontexts\n"); + } + + return rc; +} diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h new file mode 100644 index 0000000..992929a --- /dev/null +++ b/libsepol/src/kernel_to_common.h @@ -0,0 +1,114 @@ +#include +#include +#include + +#include +#include + + +#define STACK_SIZE 16 +#define DEFAULT_LEVEL "systemlow" +#define DEFAULT_OBJECT "object_r" + +// initial sid names aren't actually stored in the pp files, need to a have +// a mapping, taken from the linux kernel +static const char * const selinux_sid_to_str[] = { + "null", + "kernel", + "security", + "unlabeled", + "fs", + "file", + "file_labels", + "init", + "any_socket", + "port", + "netif", + "netmsg", + "node", + "igmp_packet", + "icmp_socket", + "tcp_socket", + "sysctl_modprobe", + "sysctl", + "sysctl_fs", + "sysctl_kernel", + "sysctl_net", + "sysctl_net_unix", + "sysctl_vm", + "sysctl_dev", + "kmod", + "policy", + "scmp_packet", + "devnull", +}; + +static const char * const xen_sid_to_str[] = { + "null", + "xen", + "dom0", + "domio", + "domxen", + "unlabeled", + "security", + "ioport", + "iomem", + "irq", + "device", +}; + +static const uint32_t avtab_flavors[] = { + AVTAB_ALLOWED, + AVTAB_AUDITALLOW, + AVTAB_AUDITDENY, + AVTAB_XPERMS_ALLOWED, + AVTAB_XPERMS_AUDITALLOW, + AVTAB_XPERMS_DONTAUDIT, + AVTAB_TRANSITION, + AVTAB_MEMBER, + AVTAB_CHANGE, +}; + +#define AVTAB_FLAVORS_SZ (sizeof(avtab_flavors)/sizeof(avtab_flavors[0])) + +struct strs { + char **list; + unsigned num; + size_t size; +}; + +__attribute__ ((format(printf, 1, 2))) +void sepol_log_err(const char *fmt, ...); +void sepol_indent(FILE *out, int indent); +__attribute__ ((format(printf, 2, 3))) +void sepol_printf(FILE *out, const char *fmt, ...); + +__attribute__ ((format(printf, 1, 3))) +char *create_str(const char *fmt, int num, ...); + +int strs_init(struct strs **strs, size_t size); +void strs_destroy(struct strs **strs); +void strs_free_all(struct strs *strs); +int strs_add(struct strs *strs, char *s); +__attribute__ ((format(printf, 2, 4))) +int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...); +char *strs_remove_last(struct strs *strs); +int strs_add_at_index(struct strs *strs, char *s, unsigned index); +char *strs_read_at_index(struct strs *strs, unsigned index); +void strs_sort(struct strs *strs); +unsigned strs_num_items(struct strs *strs); +size_t strs_len_items(struct strs *strs); +char *strs_to_str(struct strs *strs); +void strs_write_each(struct strs *strs, FILE *out); +void strs_write_each_indented(struct strs *strs, FILE *out, int indent); +int hashtab_ordered_to_strs(char *key, void *data, void *args); +int ebitmap_to_strs(struct ebitmap *map, struct strs *strs, char **val_to_name); +char *ebitmap_to_str(struct ebitmap *map, char **val_to_name, int sort); + +int stack_init(struct strs **stack); +void stack_destroy(struct strs **stack); +int stack_push(struct strs *stack, char *s); +char *stack_pop(struct strs *stack); +int stack_empty(struct strs *stack); + +int sort_ocontexts(struct policydb *pdb); diff --git a/libsepol/src/libsepol.map.in b/libsepol/src/libsepol.map.in index 47a4440..b78a3b3 100644 --- a/libsepol/src/libsepol.map.in +++ b/libsepol/src/libsepol.map.in @@ -49,4 +49,5 @@ LIBSEPOL_1.1 { sepol_ppfile_to_module_package; sepol_module_package_to_cil; sepol_module_policydb_to_cil; + sepol_kernel_policydb_to_cil; } LIBSEPOL_1.0;