From patchwork Thu Mar 23 16:48:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Carter X-Patchwork-Id: 9641613 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 87D7B602CA for ; Thu, 23 Mar 2017 16:52:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 74AF01FF60 for ; Thu, 23 Mar 2017 16:52:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6906D2841D; Thu, 23 Mar 2017 16:52:02 +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 124391FF60 for ; Thu, 23 Mar 2017 16:51:57 +0000 (UTC) X-IronPort-AV: E=Sophos;i="5.36,210,1486425600"; d="scan'208";a="4246715" IronPort-PHdr: =?us-ascii?q?9a23=3A1p9nfRXxWUaTPv964y934ZCb7yTV8LGtZVwlr6E/?= =?us-ascii?q?grcLSJyIuqrYYhaAvqdThVPEFb/W9+hDw7KP9fuxBCpfut3Q6jgrS99lb1c9k8?= =?us-ascii?q?IYnggtUoauKHbQC7rUVRE8B9lIT1R//nu2YgB/Ecf6YEDO8DXptWZBUhrwOhBo?= =?us-ascii?q?KevrB4Xck9q41/yo+53Ufg5EmCexbal8IRiyrQjdrMsbjIVtJqs11BfCv2dFdf?= =?us-ascii?q?lRyW50P1yYggzy5t23/J5t8iRQv+wu+stdWqjkfKo2UKJVAi0+P286+MPkux/D?= =?us-ascii?q?TRCS5nQHSWUZjgBIAwne4x7kWJr6rzb3ufB82CmeOs32UKw0VDG/5KplVBPklC?= =?us-ascii?q?EKPCM3/2HNjsx7kbxVrhSvqRdix4LYeZ+ZOOZ7cq7bYNgUR3dOXtxJWiJBHI2y?= =?us-ascii?q?coUBAekGM+laoYn9v1kDoQawCAajGOzhxSFHhmTt0K0m0eksCx3K0RY8E94Qtn?= =?us-ascii?q?nfsdX7NL0VUeCw1KTGyS/Mb/ZI1jfm9YPFbAouoeuQXb1ua8ra1E4iFwHLj1qM?= =?us-ascii?q?s4PlJS+V2voWvmiH7+tvSeKvi2k9pwF3uTig3N0giobOhoIL013J8zhyzogyJd?= =?us-ascii?q?29UkF7YNikHYNVty6AL4t6WMIiQ2dutS0nybMGoYa2cDUFxZko3RLSa+GLf5KW?= =?us-ascii?q?7h/sSuqdOyp0iXR4c7ylnRmy61KvyujkW8mx11ZFszRKn8HXtnAIyxzT8s+HSu?= =?us-ascii?q?Zh/ku52TaAyQTT6uZcLEAoj6XbMZ8hwqMrlpYJrUTCHjP5mEXxjKOMcEUr5vOo?= =?us-ascii?q?5Pj9brXjp5+cM5d4igD4MqswhsyyGfk0PwcBUmSB+emwyafv8VP2TblUlPE6j7?= =?us-ascii?q?HVsJXAKsQaoq65DRVV0oEm6xunFDepztAYnXgBLFJYdxOLlpPmO1bTIPD+Efiw?= =?us-ascii?q?nU+snSx3x/HGIrLhApLNImLFkLf6Z7lx8UFcyA0tzdxH/ZJbFqkBIO7vWk/2rN?= =?us-ascii?q?HYDBg5MwquzOb7CNVyzIUeVHmNAq+eNqPStEWH5vkzI+aSfo8VoyzxK/8/5/7h?= =?us-ascii?q?lXU5g0MSfbG13ZsLb3C1BvZmI0eDbnXwgdcBCmAKvgw/TOz0k1CCXzpTZ2u9Xq?= =?us-ascii?q?0n+zE7Dp6mDZrZSoCqmryB0z+xHodKaWBeFlCMDXDoep2KWvcNbCKSJ9FukiYf?= =?us-ascii?q?WLivVYAuyBautBXgxLp/NOrU/S8Yuoz/29hp/eHTkgsy9TNsBcSHz26NV310nn?= =?us-ascii?q?8PRzIuwa9wvUl9ylCF0ahknvBVDsJT6O1TUgc7NJ/cyOp6B8r0Wg3fYteDUEym?= =?us-ascii?q?Tcm+ATEtUtIxxMcDbFthG9q4lBDOxDalA7gQl7OXHpM06b7c02L3J8lj13bMzL?= =?us-ascii?q?MhgEU+QstTKW2mgbZy9gjSBo7OlUWUjKOqdaAF0C7M8meP12yOs1tCUA5zUKXF?= =?us-ascii?q?Q24QalHQrdvn+kPIV6WuBqg/Mgtd1c6CLbNHZcPxjVpcXvfjOc/ebnmpm2iqHx?= =?us-ascii?q?aIx6iMbJLue2oHwCrSFlIIkwcJ/XaJLQI+HDuuo3rCDDxyElLie03s8fNkp3O9?= =?us-ascii?q?UkA0zhqKYlZk17qt+x4ZnOecSv0I3r4evychsTp0Fk6n393KE9qAuxZhfKJEbN?= =?us-ascii?q?M++lhH1mXZtxFmM5yjNa9ihVkecxlts0PpzRV3BZ1KkdI2o3My0ApyNaWY3Utb?= =?us-ascii?q?eDyFx5DwO6PYKnPp8R+1d6HW30vS0NGM+qcA8P44sUnsvBm1Fko+9HVqy9lV3G?= =?us-ascii?q?WY5pXOCwofSpbxUlsr9xh9orHVeDMx54TO1X1jKaO0qCPN28o1BOs5zRatZ8xf?= =?us-ascii?q?MK2eFA/1CM0aG9OjKPcsm1iuYBMEOfpd9KguM8OgafSGxLahPPx8kzK+kWRH/I?= =?us-ascii?q?d931qC9iVmTu7Iw4gKw+uD3gacSTfzkk2hvdvqmYBBfz0SGXC/ySf8Do5LeqJy?= =?us-ascii?q?ZZoLCXupI8Cv2tVxmoPiW39C+16nHF4JxMipeR+OYFPnxg1QyV4boXq5liuk1z?= =?us-ascii?q?Z0iS0mrrKD3CzSxOTvbBwHOm9PRGlmk1jsOpO5gMoEU0iyaAgljhyl5Vz1x6JD?= =?us-ascii?q?vqRwM3HTQVtUfyjxN2xiTKqwuaaeY85I8pMkqzlYUOS7YVCcRb7wuB8a0yLlH2?= =?us-ascii?q?RE3jA7cCumupLjnxx1kGidNmp8rGLFecFswhfS/NjcRf9K3joaXSV4kifYBly9?= =?us-ascii?q?P9mv4NWVmIzOsvumV2KgUJ1TcDXkzZiauCu9+2JqHQW1n+qvld3/DQg6zSj72s?= =?us-ascii?q?FwVSXPqBb8Zpfk176mPuJgcEhlH0T869dmFYFliIcwg4sQ2XcCjJWP4XUHiXvz?= =?us-ascii?q?Mclc2a/mdHoCXyMLw9rP7Af/wkFsNXWJx4blVnWaxMtsfNy6bn8Q2iI6889GEq?= =?us-ascii?q?GU46JYnSFtuFq3sRrRYeRhnjca0fYu62AVg+cSuAc2zSWSHLYSHVJePSPyixSE?= =?us-ascii?q?9de+rL9YZGy3a7i/yFJ+ncy9DLGFugxcRnD5dYo5EC9u6MVwLVLM3Gf16o76ZN?= =?us-ascii?q?nadcgTuQGMkxfck+hVL4o8luIQiip6PWLwpmEqy+8njR1uxJ26spKIK2N38KK+?= =?us-ascii?q?GB5YOSX/Z9kP9TH1kaZegsGW0pi1Hpp8BzoGR5ToTfenET4Is/ToKRqOGiUmqn?= =?us-ascii?q?iHAbrfAROf6EB+onLXCZ+rKnWXK2cBzdp4QhmdOFJQgBoPUTUnhZ42CBqqxNf7?= =?us-ascii?q?fEdi4TAR+0T4pQdSxeJmMBn/SGjfqxmyZjcyVpefMAJc7hte6EfNLcye8uVzEj?= =?us-ascii?q?lW/pK7qgyNNmubZwFTAGEHREOLGlfjPqO06tnY6eiYAei+IOXSbrWSs+BeUO2I?= =?us-ascii?q?xZ232It84zmMLtmPPmVlD/Aj30pMR3R5G8DHlDUTVywYjTjCYtWdpBii5i13td?= =?us-ascii?q?yw/+7zWA3x/4ePDaVdMcl38RCsnaiDL/KQhDp+KTtAzJMA33HJxKIa3FEIjyFu?= =?us-ascii?q?cDmsHK8auiHRSaLcgKhXDwQUaylrLstH87o83hVROc7ckt712KR4geAoBFdeT1?= =?us-ascii?q?PhnN2pZcsRI2ymLlzIGEOLOK6BJT3RzMH9ebm8RqFIjOVIqx2wvi6WE0n5PjuZ?= =?us-ascii?q?jznpTwuiMe9NjCGBJxxepI+9chdrCWf5StLpdhy7P8VzjTEsxr00nHzKP3YGMT?= =?us-ascii?q?dga0NNsqGQ7SRAj/VnBWNO9WRqLe+FmyaF6OnVMZcWvuV1DStqje5a5246y7RN?= =?us-ascii?q?5iFeWPN1gDfSrsJpo1y+j+aP0SRoXwBBqjZRno2Lo15vOb7D+ZlDRHbE5ggC4X?= =?us-ascii?q?+UCxQXodtqEMfvt7lMytjIl6LyKDBC/8jS/cQCG8jeMNiHP2Y5MRr1BD7UCxMI?= =?us-ascii?q?TSWxOmHBnUNdn++d9maOo5ghsJfshoABSrBFW1wuDP8aDFpqHMAaKpdtQjwkia?= =?us-ascii?q?KbjNIP5XemtxbRRcpavpfBVv+JHPrgNi2WjbheaBsO27/4Kp4TNoLj0Ux4dlZ6?= =?us-ascii?q?hJjKG1bXXd1VuCJhYRU7oFtT/3VlSW0zw1zqagWo4HAJFP60mgU5igxxYOg38z?= =?us-ascii?q?fs+Vg3LELQpCQsiEkxhcnlgTeJfT7tK6ewWJtaCzDzt0kwLJz7RR16bRe1nUN6?= =?us-ascii?q?NzfJXK5ej7xldW93kgDcoodPFeRaTaJafB8a3euXaOkw0VRAtiWnwldK5ejFCJ?= =?us-ascii?q?tmigQnapusr3VO2wJtctE1ObDQJKpOzlhVmK2CpCmo2f4twAUGPUYC7HuSeDIU?= =?us-ascii?q?uEwPLrQmITSn8fZo6QGZmDtDf3MMV+YwrfJs7UM9OOWAwzjm07FdNkC7L/afIL?= =?us-ascii?q?+Bu2jcic6IRUs91lkWmEle47d5y90jfFaUV00017SRFg4JOtfFKQFbd8pS7mbc?= =?us-ascii?q?fCaUveXL3511I523Fvr0Qu+WqKYUnkWkER40EIQL6MQBGISj31/cLcfmN7MK0w?= =?us-ascii?q?si5B7sJFmfC/RJYh2Lmi8do86j1J93wZVdJjYFDGpgKyq3+LfXqRIxgPeYRtg2?= =?us-ascii?q?eG0aXowKN3ItRsK1hzNZv25BDDmr0uMV0g6C4CHgpi7IFjnzc8JjZOuIZRNrEN?= =?us-ascii?q?y2/TI/87Kohl7R85TeIHr3Ndtkut/S8+8aoIyHC/xMR7lnr0jcg5VYR2CtU2PX?= =?us-ascii?q?Ct66OZzwa4gqbdzpBXe3SUCwizUwT8jtJtmtNbSIjRvuRYlKrImRxCojOtOlFj?= =?us-ascii?q?ECBxdwoPkO5KFmZQwDeZU7ZwLotwQlOqywJweYysmhT3yrKTRIU/lV1f+6aKBP?= =?us-ascii?q?zyowcu+6z2MtTo07z+my9k4NWJEKjxHFyPm+ZolfUTL8FWdHdwXJuyU5i3BrNv?= =?us-ascii?q?wuzecn3BPIrV4cPiiRe+xtaWxLo88zCkuXIXR2DGo4QFmcgpDY7wG33rAd5S1d?= =?us-ascii?q?k89a0e1YqnT+uIHQYC60UqyxtZrVqzYgbcQho6BpKoPsPM2GuYjEkTPCUpnfrB?= =?us-ascii?q?eFUDWgGPpAmtlcOiVYQONHmWs9I8wJpZJB6VYtVsc5P7FPB7QsprGuaTpgEyES?= =?us-ascii?q?yDQUWJ6e0zMZg+ezwb3amg2XcJQ4PxwOqI9CjccFUy5qfiMeo7euV5/Ql2+FVm?= =?us-ascii?q?cLIAAT7QBX5AwYj4F+Y/rr7ZbPQpJX1zFWpPd0XTfRGZlz61v3UGeWjkbkSP+5?= =?us-ascii?q?ieymwRpSzO7w0tkcQBN/E1JSx/xRlkQzM7x3N7MQsZTUsjCWcUP6vWTtyOSiJF?= =?us-ascii?q?lQxs3Ud0H4DITfumrmVy0Q43sUSpFTyHvHD5QdjxJ5aLo3pFVLOI2ndFzy6CAk?= =?us-ascii?q?x4RoGrm4VNqkx1U+oHYHWSilDcROB/thsFLNVz1ve4qrp4n9O5VOXm9Q/4WQpE?= =?us-ascii?q?1DkEVwNS6105lcJtpM4j4NXThPvS6SsMG0SMJdxc98F4UMLctnu3fhBKNEP4Cc?= =?us-ascii?q?o3wwurzz1HDZ/y4zsFKkyzWvAa+5T/lU/2oGGgo3IWSer1MgD+oo8mjJ9VDMvU?= =?us-ascii?q?p4/+BBBriAlU9xuipyHohSBjZV0nCoN1tzTGNcvOVcNKTYaM1cQ/48ZB+pJxw+?= =?us-ascii?q?FeAp302P/UFog3j5eTZ+tg1E9CDBRwM0TzUagq/xmT0CrcGqISMaS5VNbTg6cy?= =?us-ascii?q?fJNwebljtNsBZZdU5qR4gTAsxZ9LEDwYtU4s3CRF6sKS4fRhxtLAY43udEmEFe?= =?us-ascii?q?rkqYYTjdAhSsdfnRqB17Z8CRrNSmLP7h5gdIlpvnsPwk96UEX3CmmwqtTs7Foo?= =?us-ascii?q?DhqNKKrVWBdL3kM+KifHDNVj7Mgg6shb08FZnF4zDTMBZHK5l90XcrepbhBnLX?= =?us-ascii?q?MRlEOq4WKVFWWrx9adVDvu9VfdRkeLoT9a9rGB2HWgvlGJazo/leMlbTWTPeIj?= =?us-ascii?q?2b8uOhvI3T7abSSfT7a8yIwHbHXbx4Molm5jn8Brjlz5VS+kzo1fdx7kl6U0TJ?= =?us-ascii?q?MzidrNT9IQMG/NGidlD+vpIzADzWHIx9kGHqxkFdbcoYXzOl8JIEyJND8nnwTf?= =?us-ascii?q?h00k/psO1d77Nk85U446h1yceoIqffMehasVV5DReKGwpn7YgtAGxjR2BLeOAR?= =?us-ascii?q?NvbRfaMdjc/wt+D7DasX6Aea++ZBc9vIO1nBmtWjCjGbURFLgQEBpiQBIwSB0f?= =?us-ascii?q?6KgbV4ScG/pej2wkgt+USxLgYazLBx4oeJ4qiIq/XNbxvW1rgEXrLlRt3oo7Qp?= =?us-ascii?q?pkOS4eEklL8SdWx0eQKnFvMdWtQaxmf6wqAg1TgsHN/bH7L85P5DUGo0njD6lJ?= =?us-ascii?q?B7EVUWAPQUEqeV8olFgmc3gerZNsYRcqxYnGaACwKkGKcYyXG38yuXPHVlghbW?= =?us-ascii?q?3hH2RWO+9172rSl9QSfWztfjlERVVqOtCkdJQyqpI1J4vy+TPAr1rtr4p7457E?= =?us-ascii?q?YsMmzrrNiNjneuOKtLH83jI9yROS80pFMLg502Qtygw5sbFcC8INcW93F+deDe?= =?us-ascii?q?5niukyJOrKZLnY3e4tua+v/PB3mvk7Waq6mRxDBf0nU3oUsw6tS+OfHV+92LQ/?= =?us-ascii?q?Oo2n0KTyhjugvOQRi1qqDar18OI0CL1lnEmJASNNFDwXY4zl3m5PQkQN8r8AVe?= =?us-ascii?q?DYPAavMZqDD9JjT73FGfY8gtWSmGyTtXGVD1HkdkF6ggwm7wu9jJlXjI8V0yWo?= =?us-ascii?q?Zwb1DnhQBwD4ggK0It6V0XzzEGEQcTaRGUEquoBUXjLYsLT0QDbwqI3KSicKct?= =?us-ascii?q?wUJz2q+v5PPUbeFkBqoMN+xSgRSSk1VAAZIZr7EeT6xgdFNH867XvAfiAZD9X/?= =?us-ascii?q?f6jXowKeG1QsdC/M8Ctnsi+Qi+SACi6ZdC87YbkIqHdrNAYZXXoM986Fpo6iAV?= =?us-ascii?q?eSxQmhh/iAm5XvoGqODk+Njbv4Ki6vyyW6Y1W+UX6x80Cnxmj5TqnFAsvdHX1u?= =?us-ascii?q?lbSo3PioTz6RtNLGCOuIbb1Rl8NOUPJpmzfLl663UIOygeJnwJPdqMaPkw+Sxt?= =?us-ascii?q?PyvJ51xEHMwMYckUPM3XmQBbkkfpQq1c9tLHGl+EDId+b9wo73DpxzAx65Q8VP?= =?us-ascii?q?jv5SSrKpDe4FFNPu9Dgz93lN3YvugV3ODeCC4J7nmFcxJ12D+Ny4GRC/bs+uWB?= =?us-ascii?q?0N7UWEkcHiEoT4ddJCGP+QimRuqpiprpUhmb6tLogJ4kckKfWGCxlrwfsqlQCe?= =?us-ascii?q?5Aljn73j9GG4D6mf2Vtcas6GxQtlFAC4Z88xnFGKRZPpVmNhX1jc2rSVZmBir/?= =?us-ascii?q?Ys7YbBwut/SKxu0U+eVxK1P+ZZMHIhID07/19XhVQRFyR7PtolmZWuYRZN9nSP?= =?us-ascii?q?zatHBV9ZxvJLMPPFSHo5zqtDhIokgsAAA1cL8wsiBadk7WkQ1QXKb0vqIAigQC?= =?us-ascii?q?XtNivE9MGHi/OGQg6DrbW6VVi7ORCP8O8jmJSKwBTVloODtkQxypwJVuZ6epnf?= =?us-ascii?q?dfv2NJgix9u+Mq3yV7ShWzvyPspqQN2S8v+byjuzUBvGZKTuOAnCvSDlVD1vsK?= =?us-ascii?q?h78GC3n+8Vy8fGUDbIzq7bllIsTg9Y8h724jbhUjey0GQPmvCiDxj6OOGYyPs9?= =?us-ascii?q?NciQWTuMXPc7+8NywSOak8yR75QXhyzBTelg5y8GQXWjWg8MMkJIKlNMYk2Cqn?= =?us-ascii?q?A27beU0I4qNUrcTxtFkLTPAsZVNm2mVj3dCNRjcRS8zXB2Y1kg8kZH1GcJ1d5x?= =?us-ascii?q?8aGa0ojy2Gv6Zc+AEbfivUHp6j+obOgcfCwWM9QstyxmLKuq2FgYsn0HJhm9Nz?= =?us-ascii?q?4C+BpnASd+jDXs93BXjz0IZfxvDka/WrrO8HVJNsyK69X/8aLsmj5Wy21Y1oWk?= =?us-ascii?q?+lxrUeB125MOsCxrfHTSioUmiYWeWXc2iWgTk1KEny5QOnLlcvcsdFs1c9Mvfe?= =?us-ascii?q?hp5bjwDhTbJ0RiCLqF/HzWwsLeAaeBwyuIu9fQwFUvIRbfCGJecy2P0+FEcMb3?= =?us-ascii?q?jRECtzCu+2sl2tnI9gNnVi+0r6YuPt8gb4P9uUABUEF5DVroJp8/ygWm2BIWNg?= =?us-ascii?q?zAF1PERs7OjQCVIxtvNHfpaKmdjQgMh70fIbd/hzLCIxoNkTmphs6YOMyseFbQ?= =?us-ascii?q?nRzoruJdHSuvWYG/rfz0AwdWFAXLsZZgf154shM948XL3TGqBZsgoACqggR5wh?= =?us-ascii?q?LHzx9KByLAN8aALRY660gsbyrOKRepRUv2PW7k43LCrEtR0M1Pi0TQ1jb5+0nH?= =?us-ascii?q?jzIZEwRjdEr9JzEBtmGJVAF9kHrwq9DJ6egLu7hMOp+0Nmp+8KtrL9C/7S1NSj?= =?us-ascii?q?3oV+Q4ZV6FeOPDbQGKlrmFpqgv61gvfa3Zn7Ednids8cVOhnXm7FbafLHoK7Kj?= =?us-ascii?q?2QIc/8Y0pG/KSH0LJlUxWeeDz5X7CYuyK6Lvlk51s0ypBgdurJ0DMt963b2Mf1?= =?us-ascii?q?Z2xDoiejrHiJNIZQ7VDTAezeXglUSeae8Gt+HK0Yd4309OAUPdwl2tic7BF57C?= =?us-ascii?q?5e38udP6ihskjM11pgdZ3FKEvp2iA5WZQFIRi4PkUsnW/ZpWrHAXtCNsirM9Jg?= =?us-ascii?q?gNWLARz34Ul+h2UtaXVcGmrnX9uRPnIb282mbg2Q6A1LF8oDn/KweUMgs629U/?= =?us-ascii?q?VnOpVEmeWss7UKishpKyDJRMdEOyHQLbl2PideD+nVo1goeBEEuaAvWogpfZiO?= =?us-ascii?q?PF8HMECYxCPw1wTC0kr0eMe32aaUJiYW7nVHz7XY0ThDpwm5peyVgsv9X7DFdJ?= =?us-ascii?q?v2RuLdMDI5VjGGQjQ/CV2m9kyjt/oAvfqYOnsfoksObiKJFgEcurpjrcPOAW/X?= =?us-ascii?q?mO1jeoAKhf+BVi/sSCx4jrcyBj1RuUGCWfUDCRLWb3jngGZGvwyiPPBM92r/b7?= =?us-ascii?q?KE3qpVR/AWAoxUf/2WWdTXYupeJzY2mTUdI+u8YdncoKwi0lLJUGsVCa/I+0OC?= =?us-ascii?q?TESOWPyT2yrrXZkJv4gzoico+NfQnihtE6TSPrafvCWj8oiigyadp+LeV3MhY0?= =?us-ascii?q?svgOINGGOB2gVPKHkYC9EJv0HgWrWAZ0hJ1H01iO9j2AQBeBptXX10031XnfG9?= =?us-ascii?q?GstDRV4SkmyuXOUMbEprAzMo4U+K/grybMQCucDUQW9e8qABSY8DI/Y15onXP7?= =?us-ascii?q?EQz+o30z58piw1rTmdXBthiVee/q7RGr9s7qFT7mk/ufdtVweARC2MXHLAz9+E?= =?us-ascii?q?AsdC3m9FqGr42tCc6uJyP7JAu9tRHEYBASUwIMvUuz9GTzeni0OJox+nEWHGc3?= =?us-ascii?q?oi6zERKHlALL54?= X-IPAS-Result: =?us-ascii?q?A2E6CgDj+9NY/wHyM5BdGwEBAQMBAQEJAQEBFgEBAQMBAQE?= =?us-ascii?q?JAQEBgn4oYXkSjmSSYwGVTCIDgXeHIVcBAQEBAQEBAQIBAmgFI4IzIoEqWzwGA?= =?us-ascii?q?QIXAQwTFCAOAwkBARcpCAgDAS0VEQ4LBRgEh19CgTcNrDM6JgKKR4QsizcRAYV?= =?us-ascii?q?iHwWBKwGHa4ZHgU2LKAKGe4tPAoJNiCGGP5NiWHwIGwkCFCVQZ4MsAQEBQh0Zg?= =?us-ascii?q?UkdWYdODRcHghABAQE?= Received: from unknown (HELO tarius.tycho.ncsc.mil) ([144.51.242.1]) by emsm-gh1-uea11.nsa.gov with ESMTP; 23 Mar 2017 16:51:54 +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 v2NGprwq002976; Thu, 23 Mar 2017 12:51:54 -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 v2NGmYuL248224 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 v2NGmXYS001400 for ; Thu, 23 Mar 2017 12:48:33 -0400 From: James Carter To: selinux@tycho.nsa.gov Subject: [PATCH 2/3 v3] libsepol: Add ability to convert binary policy to policy.conf file Date: Thu, 23 Mar 2017 12:48:56 -0400 Message-Id: <1490287737-31103-3-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_conf(), that takes a policydb created from a binary policy and writes a policy.conf file to the provided FILE pointer. Signed-off-by: James Carter --- libsepol/include/sepol/kernel_to_conf.h | 5 + libsepol/src/kernel_to_conf.c | 3096 +++++++++++++++++++++++++++++++ libsepol/src/libsepol.map.in | 1 + 3 files changed, 3102 insertions(+) create mode 100644 libsepol/include/sepol/kernel_to_conf.h create mode 100644 libsepol/src/kernel_to_conf.c diff --git a/libsepol/include/sepol/kernel_to_conf.h b/libsepol/include/sepol/kernel_to_conf.h new file mode 100644 index 0000000..2313f38 --- /dev/null +++ b/libsepol/include/sepol/kernel_to_conf.h @@ -0,0 +1,5 @@ +#include + +#include + +int sepol_kernel_policydb_to_conf(FILE *fp, struct policydb *pdb); diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c new file mode 100644 index 0000000..22a0909 --- /dev/null +++ b/libsepol/src/kernel_to_conf.c @@ -0,0 +1,3096 @@ +#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 = "!"; num_params = 1; break; + case COND_OR: op = "||"; num_params = 2; break; + case COND_AND: op = "&&"; num_params = 2; break; + case COND_XOR: op = "^"; num_params = 2; break; + case COND_EQ: op = "=="; num_params = 2; break; + case COND_NEQ: op = "!="; 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, val1, op, 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 = "=="; break; + case CEXPR_NEQ: op = "!="; 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, attr1, op, 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, attr1, op, 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, val1, op, 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) +{ + struct constraint_node *curr; + struct strs *strs; + const char *format_str, *flavor; + char *perms, *expr; + int is_mls; + int rc = 0; + + 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 (strchr(perms, ' ')) { + format_str = "%s %s { %s } %s;"; + } else { + format_str = "%s %s %s %s"; + } + if (is_mls) { + flavor = "mlsconstrain"; + strs = mls_list; + } else { + flavor = "constrain"; + strs = non_mls_list; + } + + rc = strs_create_and_add(strs, format_str, 4, + flavor, 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; + struct strs *strs; + const char *flavor; + char *expr; + int is_mls; + 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) { + flavor = "mlsvalidatetrans"; + strs = mls_list; + } else { + flavor = "validatetrans"; + strs = non_mls_list; + } + + rc = strs_create_and_add(strs, "%s %s %s;", 3, flavor, 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_conf(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, "# handle_unknown %s\n", action); + + return 0; +} + +static int write_class_decl_rules_to_conf(FILE *out, struct policydb *pdb) +{ + char *name; + unsigned i; + + for (i=0; i < pdb->p_classes.nprim; i++) { + name = pdb->p_class_val_to_name[i]; + sepol_printf(out, "class %s\n", name); + } + + return 0; +} + +static int write_sids_to_conf(FILE *out, const char *const *sid_to_str, struct ocontext *isids) +{ + struct ocontext *isid; + struct strs *strs; + char *sid; + 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_conf(out, selinux_sid_to_str, pdb->ocontexts[0]); + } else if (pdb->target_platform == SEPOL_TARGET_XEN) { + rc = write_sids_to_conf(out, xen_sid_to_str, pdb->ocontexts[0]); + } else { + sepol_log_err("Unknown target platform: %i", pdb->target_platform); + rc = -1; + } + + return rc; +} +static char *class_or_common_perms_to_str(symtab_t *permtab) +{ + struct strs *strs; + char *perms = NULL; + int rc = 0; + + 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_and_common_rules_to_conf(FILE *out, struct policydb *pdb) +{ + class_datum_t *class; + common_datum_t *common; + int *used; + char *name, *perms; + unsigned i; + int rc = 0; + + /* 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) continue; + 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(used); + goto exit; + } + sepol_printf(out, "common %s { %s }\n", name, perms); + free(perms); + used[common->s.value-1] = 1; + } + } + free(used); + + /* 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]; + sepol_printf(out, "class %s", name); + if (class->comkey) { + sepol_printf(out, " inherits %s", class->comkey); + } + perms = class_or_common_perms_to_str(&class->permissions); + if (perms) { + sepol_printf(out, " { %s }", perms); + free(perms); + } + sepol_printf(out, "\n"); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing class rules to policy.conf\n"); + } + + return rc; +} + +static int write_default_user_to_conf(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, "default_user { %s } %s;\n", class_name, dft); + + return 0; +} + +static int write_default_role_to_conf(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, "default_role { %s } %s;\n", class_name, dft); + + return 0; +} + +static int write_default_type_to_conf(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, "default_type { %s } %s;\n", class_name, dft); + + return 0; +} + +static int write_default_range_to_conf(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, "default_range { %s } %s;\n", class_name, dft); + + return 0; +} + +static int write_default_rules_to_conf(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_conf(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_conf(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_conf(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 != 0) { + rc = write_default_range_to_conf(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 policy.conf\n"); + } + + return rc; +} + +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_conf(FILE *out, struct policydb *pdb) +{ + level_datum_t *level; + struct strs *strs; + char **sens_alias_map = NULL; + char *name, *prev, *alias; + unsigned i, j, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_levels.nprim); + if (rc != 0) { + goto exit; + } + + rc = hashtab_map(pdb->p_levels.table, map_sensitivity_aliases_to_strs, strs); + if (rc != 0) { + goto exit; + } + + num = strs_num_items(strs); + + if (num > 0) { + sens_alias_map = calloc(sizeof(*sens_alias_map), pdb->p_levels.nprim); + if (!sens_alias_map) { + rc = -1; + goto exit; + } + + /* map aliases to sensitivities */ + 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; + } + j = level->level->sens - 1; + if (!sens_alias_map[j]) { + sens_alias_map[j] = strdup(name); + } else { + alias = sens_alias_map[j]; + sens_alias_map[j] = create_str("%s %s", 2, alias, name); + free(alias); + if (!sens_alias_map[j]) { + rc = -1; + 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; + + if (sens_alias_map && sens_alias_map[i]) { + alias = sens_alias_map[i]; + if (strchr(alias, ' ')) { + sepol_printf(out, "sensitivity %s alias { %s };\n", name, alias); + } else { + sepol_printf(out, "sensitivity %s alias %s;\n", name, alias); + } + } else { + sepol_printf(out, "sensitivity %s;\n", name); + } + } + + /* dominance */ + sepol_printf(out, "dominance { "); + 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"); + +exit: + if (sens_alias_map) { + for (i=0; i < pdb->p_levels.nprim; i++) { + free(sens_alias_map[i]); + } + free(sens_alias_map); + } + + 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_conf(FILE *out, struct policydb *pdb) +{ + cat_datum_t *cat; + struct strs *strs; + char **cat_alias_map = NULL; + char *name, *alias; + unsigned i, j, num; + int rc = 0; + + rc = strs_init(&strs, pdb->p_levels.nprim); + if (rc != 0) { + goto exit; + } + + rc = hashtab_map(pdb->p_cats.table, map_category_aliases_to_strs, strs); + if (rc != 0) { + goto exit; + } + + num = strs_num_items(strs); + + if (num > 0) { + cat_alias_map = calloc(sizeof(*cat_alias_map), pdb->p_cats.nprim); + if (!cat_alias_map) { + rc = -1; + goto exit; + } + + /* map aliases to categories */ + 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; + } + j = cat->s.value - 1; + if (!cat_alias_map[j]) { + cat_alias_map[j] = strdup(name); + } else { + alias = cat_alias_map[j]; + cat_alias_map[j] = create_str("%s %s", 2, alias, name); + free(alias); + if (!cat_alias_map[j]) { + rc = -1; + 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; + + if (cat_alias_map && cat_alias_map[i]) { + alias = cat_alias_map[i]; + if (strchr(alias, ' ')) { + sepol_printf(out, "category %s alias { %s };\n", name, alias); + } else { + sepol_printf(out, "category %s alias %s;\n", name, alias); + } + } else { + sepol_printf(out, "category %s;\n", name); + } + } + +exit: + if (cat_alias_map) { + for (i=0; i < pdb->p_cats.nprim; i++) { + free(cat_alias_map[i]); + } + free(cat_alias_map); + } + + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing category rules to policy.conf\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]) + 1; + if (range > 1) { + len += strlen(val_to_name[i]) + 1; + } + + range = 0; + } + + return len; +} + +static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) +{ + struct ebitmap_node *node; + uint32_t i, start, range, first; + char *catsbuf, *p; + const char *fmt; + char sep; + int len, remaining; + + remaining = (int)cats_ebitmap_len(cats, val_to_name); + catsbuf = malloc(remaining); + if (!catsbuf) { + goto exit; + } + + p = catsbuf; + + first = 1; + 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) { + sep = (range == 2) ? ',' : '.'; + fmt = first ? "%s%c%s" : ",%s%c%s"; + len = snprintf(p, remaining, fmt, + val_to_name[start], sep, val_to_name[i]); + } else { + fmt = first ? "%s" : ",%s"; + len = snprintf(p, remaining, fmt, val_to_name[start]); + + } + if (len < 0 || len >= remaining) { + goto exit; + } + p += len; + remaining -= len; + first = 0; + range = 0; + } + + *p = '\0'; + + return catsbuf; + +exit: + free(catsbuf); + return NULL; +} + +static int write_level_rules_to_conf(FILE *out, struct policydb *pdb) +{ + level_datum_t *level; + char *name, *cats; + unsigned i; + int rc = 0; + + 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, "level %s:%s;\n", name, cats); + free(cats); + } else { + sepol_printf(out, "level %s;\n", name); + } + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing level rules to policy.conf\n"); + } + + return rc; +} + +static int write_mls_rules_to_conf(FILE *out, struct policydb *pdb) +{ + int rc = 0; + + if (!pdb->mls) { + return 0; + } + + rc = write_sensitivity_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_category_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_level_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing mls rules to policy.conf\n"); + } + + return rc; +} + +static int write_polcap_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_type_attributes_to_conf(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, "attribute %s;\n", name); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing typeattribute rules to policy.conf\n"); + } + + return rc; +} + +static int write_role_attributes_to_conf(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, "bool %s %s;", 2, key, value); +} + +static int write_boolean_decl_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_type_decl_rules_to_conf(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, "typealias %s %s;\n", type, name); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing type alias rules to policy.conf\n"); + } + + return rc; +} + +static int write_type_bounds_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static char *attr_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; + } + + /* 2*strs->num - 1 because ", " follows all but last attr (followed by '\0') */ + len = strs_len_items(strs) + 2*strs->num - 1; + 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++ = ' '; + } + } + + *p = '\0'; + +exit: + return str; +} + +static char *attrmap_to_str(struct ebitmap *map, char **val_to_name) +{ + 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; + } + + strs_sort(strs); + + str = attr_strs_to_str(strs); + +exit: + strs_destroy(&strs); + + return str; +} + +static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb) +{ + type_datum_t *type; + struct strs *strs; + ebitmap_t attrmap; + char *name, *attrs; + 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++) { + type = pdb->type_val_to_struct[i]; + if (type->flavor != TYPE_TYPE || !type->primary) continue; + if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue; + + rc = ebitmap_cpy(&attrmap, &pdb->type_attr_map[i]); + if (rc != 0) { + goto exit; + } + rc = ebitmap_set_bit(&attrmap, i, 0); + if (rc != 0) { + ebitmap_destroy(&attrmap); + goto exit; + } + name = pdb->p_type_val_to_name[i]; + attrs = attrmap_to_str(&attrmap, pdb->p_type_val_to_name); + ebitmap_destroy(&attrmap); + if (!attrs) { + rc = -1; + goto exit; + } + + rc = strs_create_and_add(strs, "typeattribute %s %s;", + 2, name, attrs); + free(attrs); + 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 policy.conf\n"); + } + + return rc; +} + +static int write_type_permissive_rules_to_conf(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; idata; + type_datum_t *type; + const char *flavor, *src, *tgt, *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 = "allowxperm"; + break; + case AVTAB_XPERMS_AUDITALLOW: + flavor = "auditallowxperm"; + break; + case AVTAB_XPERMS_DONTAUDIT: + flavor = "dontauditxperm"; + break; + case AVTAB_TRANSITION: + flavor = "type_transition"; + break; + case AVTAB_MEMBER: + flavor = "type_member"; + break; + case AVTAB_CHANGE: + flavor = "type_change"; + 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 = sepol_extended_perms_to_string(datum->xperms); + if (perms == NULL) { + sepol_log_err("Failed to generate extended permission string"); + goto exit; + } + + rule = create_str("%s %s %s:%s %s;", 5, flavor, src, tgt, 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_conf(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_conf(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_conf(out, pdb, avtab_flavors[i], indent); + if (rc != 0) { + goto exit; + } + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing avtab rules to policy.conf\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, "type_transition %s %s:%s %s \"%s\";", 5, + src, tgt, class, new, filename); +} + +static int write_filename_trans_rules_to_conf(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 policy.conf\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, "range_transition %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_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_cond_av_list_to_conf(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_conf(FILE *out, struct policydb *pdb) +{ + struct cond_data *cond_data; + char *expr; + struct cond_node *cond; + unsigned i, num; + int rc = 0; + + num = 0; + for (cond = pdb->cond_list; cond != NULL; cond = cond->next) { + num++; + } + + if (num == 0) { + return 0; + } + + 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) { + rc = write_cond_av_list_to_conf(out, pdb, cond->true_list, 1); + if (rc != 0) { + goto exit; + } + } + + if (cond->false_list != NULL) { + sepol_printf(out, "} else {\n"); + rc = write_cond_av_list_to_conf(out, pdb, cond->false_list, 1); + if (rc != 0) { + goto exit; + } + } + sepol_printf(out, "}\n"); + } + +exit: + if (cond_data) { + for (i=0; ip_roles.nprim); + if (rc != 0) { + goto exit; + } + + /* Start at 1 to skip object_r */ + for (i=1; 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, name); + if (!role) { + rc = -1; + goto exit; + } + if (ebitmap_cardinality(&role->types.types) == 0) continue; + types = ebitmap_to_str(&role->types.types, pdb->p_type_val_to_name, 1); + if (!types) { + rc = -1; + goto exit; + } + if (strlen(types) > 900) { + p1 = types; + while (p1) { + p2 = p1; + while (p2 - p1 < 600) { + p2 = strchr(p2, ' '); + if (!p2) + break; + p2++; + } + if (p2) { + *(p2-1) = '\0'; + } + sepol_printf(out, "role %s types { %s };\n", name, p1); + p1 = p2; + } + } else { + sepol_printf(out, "role %s types { %s };\n", name, types); + } + free(types); + } + +exit: + strs_destroy(&strs); + + if (rc != 0) { + sepol_log_err("Error writing role declarations to policy.conf\n"); + } + + return rc; +} + +static int write_role_transition_rules_to_conf(FILE *out, struct policydb *pdb) +{ + role_trans_t *curr = pdb->role_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, "role_transition %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 policy.conf\n"); + } + + return rc; +} + +static int write_role_allow_rules_to_conf(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, "allow %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 policy.conf\n"); + } + + return rc; +} + +static int write_user_decl_rules_to_conf(FILE *out, struct policydb *pdb) +{ + struct user_datum *user; + struct strs *strs; + char *name, *roles, *level, *range; + unsigned i, num; + 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; + } + sepol_printf(out, "user %s", name); + + if (ebitmap_cardinality(&user->roles.roles) > 0) { + roles = ebitmap_to_str(&user->roles.roles, + pdb->p_role_val_to_name, 1); + if (!roles) { + rc = -1; + goto exit; + } + if (strchr(roles, ' ')) { + sepol_printf(out, " roles { %s }", roles); + } else { + sepol_printf(out, " roles %s", roles); + } + free(roles); + } + + if (pdb->mls) { + level = level_to_str(pdb, &user->exp_dfltlevel); + if (!level) { + rc = -1; + goto exit; + } + sepol_printf(out, " level %s", level); + free(level); + + range = range_to_str(pdb, &user->exp_range); + if (!range) { + rc = -1; + goto exit; + } + sepol_printf(out, " range %s", range); + free(range); + } + sepol_printf(out, ";\n"); + } + + strs_destroy(&strs); + +exit: + if (rc != 0) { + sepol_log_err("Error writing user declarations to policy.conf\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); + ctx = create_str("%s:%s:%s:%s", 4, user, role, type, range); + free(range); + } else { + ctx = create_str("%s:%s:%s", 3, user, role, type); + } + + return ctx; +} + +static int write_sid_context_rules_to_conf(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; + + 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("sid %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 policy.conf\n"); + } + + return rc; +} + +static int write_selinux_isid_rules_to_conf(FILE *out, struct policydb *pdb) +{ + return write_sid_context_rules_to_conf(out, pdb, selinux_sid_to_str); +} + +static int write_selinux_fsuse_rules_to_conf(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, "fs_use_%s %s %s;\n", behavior, name, ctx); + + free(ctx); + } + +exit: + if (rc != 0) { + sepol_log_err("Error writing fsuse rules to policy.conf\n"); + } + + return rc; +} + +static int write_genfscon_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_selinux_port_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_selinux_netif_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_selinux_node_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + + +static int write_selinux_node6_rules_to_conf(FILE *out, struct policydb *pdb) +{ + struct ocontext *node6; + char addr[INET6_ADDRSTRLEN]; + char mask[INET6_ADDRSTRLEN]; + char *ctx; + int rc = 0; + + for (node6 = pdb->ocontexts[6]; node6 != NULL; node6 = node6->next) { + if (inet_ntop(AF_INET6, &node6->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, &node6->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, &node6->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 policy.conf\n"); + } + + return rc; +} + +static int write_xen_isid_rules_to_conf(FILE *out, struct policydb *pdb) +{ + return write_sid_context_rules_to_conf(out, pdb, xen_sid_to_str); +} + + +static int write_xen_pirq_rules_to_conf(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) { + fprintf(stderr,"error1\n"); + rc = -1; + goto exit; + } + + ctx = context_to_str(pdb, &pirq->context[0]); + if (!ctx) { + rc = -1; + fprintf(stderr,"error2\n"); + 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 policy.conf\n"); + } + + return rc; +} + +static int write_xen_ioport_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_xen_iomem_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +static int write_xen_pcidevice_rules_to_conf(FILE *out, struct policydb *pdb) +{ + struct ocontext *pcid; + char device_str[20]; /* 2^64-1 <= 16 digits (hex) so < 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 policy.conf\n"); + } + + return rc; +} + +static int write_xen_devicetree_rules_to_conf(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 policy.conf\n"); + } + + return rc; +} + +int sepol_kernel_policydb_to_conf(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_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_class_decl_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_sid_decl_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_class_and_common_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_default_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_mls_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + strs_write_each(mls_constraints, out); + strs_write_each(mls_validatetrans, out); + + rc = write_polcap_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_attributes_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_attributes_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_boolean_decl_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_decl_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_alias_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_bounds_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_attribute_sets_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_type_permissive_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_avtab_to_conf(out, pdb, 0); + if (rc != 0) { + goto exit; + } + write_filename_trans_rules_to_conf(out, pdb); + + if (pdb->mls) { + rc = write_range_trans_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + } + + rc = write_cond_nodes_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_decl_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_transition_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_role_allow_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_user_decl_rules_to_conf(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_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_fsuse_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_genfscon_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_port_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_netif_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_node_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_selinux_node6_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + } else if (pdb->target_platform == SEPOL_TARGET_XEN) { + rc = write_xen_isid_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_genfscon_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_pirq_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_iomem_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_ioport_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_pcidevice_rules_to_conf(out, pdb); + if (rc != 0) { + goto exit; + } + + rc = write_xen_devicetree_rules_to_conf(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/libsepol.map.in b/libsepol/src/libsepol.map.in index b78a3b3..5e68fcb 100644 --- a/libsepol/src/libsepol.map.in +++ b/libsepol/src/libsepol.map.in @@ -50,4 +50,5 @@ LIBSEPOL_1.1 { sepol_module_package_to_cil; sepol_module_policydb_to_cil; sepol_kernel_policydb_to_cil; + sepol_kernel_policydb_to_conf; } LIBSEPOL_1.0;