1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <cstdio>
17#include <cstdlib>
18#include <getopt.h>
19#include <iosfwd>
20#include <iostream>
21#include <istream>
22#include <memory>
23#include <ostream>
24#include <string>
25#ifdef TIME_DISPLAY
26#include <sys/time.h>
27#endif
28#include <unistd.h>
29#include <sys/socket.h>
30#include <selinux/selinux.h>
31
32#include "selinux_error.h"
33#include "selinux_parameter.h"
34#include "param_checker.h"
35
36using namespace Selinux;
37
38#ifdef TIME_DISPLAY
39const static long USEC_PER_SEC = 1000000L;
40#endif
41
42struct TestInput {
43    std::string paraName;
44    char cmd = '\0';
45};
46
47static void TestLoadList()
48{
49    ParamContextsList *buff = nullptr;
50#ifdef TIME_DISPLAY
51    struct timeval start, end, diff;
52    gettimeofday(&start, nullptr);
53#endif
54    buff = GetParamList();
55    if (buff == nullptr) {
56        std::cout << "buff empty" << std::endl;
57        return;
58    }
59#ifdef TIME_DISPLAY
60    gettimeofday(&end, nullptr);
61    timersub(&end, &start, &diff);
62    int runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
63    std::cout << "GetParamList time use: " << runtime_us << std::endl;
64#endif
65
66    ParamContextsList *head = buff;
67    while (buff != nullptr) {
68        if (security_check_context(buff->info.paraContext) < 0) {
69            std::cout << "failed check context: " << buff->info.paraContext << " " << strlen(buff->info.paraContext)
70                      << std::endl;
71            buff = buff->next;
72            continue;
73        }
74        std::cout << "param: " << buff->info.paraName << ", contexts: " << buff->info.paraContext
75                  << ", index: " << buff->info.index << std::endl;
76        buff = buff->next;
77    }
78#ifdef TIME_DISPLAY
79    gettimeofday(&start, nullptr);
80#endif
81    DestroyParamList(&head);
82#ifdef TIME_DISPLAY
83    gettimeofday(&end, nullptr);
84    timersub(&end, &start, &diff);
85    runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
86    std::cout << "DestroyParamList time use: " << runtime_us << std::endl;
87#endif
88}
89
90static void TestGetContext(std::string &paraName)
91{
92#ifdef TIME_DISPLAY
93    struct timeval start, end, diff;
94    gettimeofday(&start, nullptr);
95#endif
96    const char *context = GetParamLabel(paraName.c_str());
97    int index = GetParamLabelIndex(paraName.c_str());
98    std::cout << "paraName: " << paraName << "context: " << context << " index: " << index << std::endl;
99#ifdef TIME_DISPLAY
100    gettimeofday(&end, nullptr);
101    timersub(&end, &start, &diff);
102    int runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
103    std::cout << "time use: " << runtime_us << std::endl;
104#endif
105    std::cout << "para " << paraName.c_str() << "'s context is " << context << std::endl;
106}
107
108static void TestReadPara(std::string &paraName)
109{
110#ifdef TIME_DISPLAY
111    struct timeval start, end, diff;
112    gettimeofday(&start, nullptr);
113#endif
114    const char *contexts = GetParamLabel(paraName.c_str());
115    std::string path = "/dev/__parameters__/" + std::string(contexts);
116    if (access(path.c_str(), F_OK) != 0) {
117        std::cout << "read param: " << paraName << " fail" << std::endl;
118    } else {
119        std::cout << "read param: " << paraName << " succ" << std::endl;
120    }
121#ifdef TIME_DISPLAY
122    gettimeofday(&end, nullptr);
123    timersub(&end, &start, &diff);
124    int runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
125    std::cout << "time use: " << runtime_us << std::endl;
126#endif
127}
128
129static void TestSetPara(std::string &paraName, SrcInfo *info)
130{
131#ifdef TIME_DISPLAY
132    struct timeval start, end, diff;
133    gettimeofday(&start, nullptr);
134#endif
135    std::cout << GetErrStr(SetParamCheck(paraName.c_str(), GetParamLabel(paraName.c_str()), info)) << std::endl;
136#ifdef TIME_DISPLAY
137    gettimeofday(&end, nullptr);
138    timersub(&end, &start, &diff);
139    int runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
140    std::cout << "time use: " << runtime_us << std::endl;
141#endif
142}
143
144static void PrintUsage()
145{
146    std::cout << "Options:" << std::endl;
147    std::cout << " -h (--help)           show the help information.      [eg: param_check -h]" << std::endl;
148    std::cout << " -g (--getContext)     get context for paraName.       [eg: param_check -g]" << std::endl;
149    std::cout << " -r (--read)           read para perm.                 [eg: param_check -r]" << std::endl;
150    std::cout << " -w (--write)          write para perm.                [eg: param_check -w]" << std::endl;
151    std::cout << " -l (--list)           load para list.                 [eg: param_check -l]" << std::endl;
152    std::cout << " -n (--paraName)       paraName.                       [eg: param_check -r|-w|-g -n para_name]"
153              << std::endl;
154    std::cout << "" << std::endl;
155    std::cout << "Usage:" << std::endl;
156    std::cout << ">>>>>>> choice 1: continuous input parameters" << std::endl;
157    std::cout << "step 1:" << std::endl;
158    std::cout << "param_check -r|-w|-g|-l" << std::endl;
159    std::cout << "step 2:" << std::endl;
160    std::cout << "input param name and press 'enter' to continue, or ctrl+C to end process" << std::endl;
161    std::cout << "" << std::endl;
162    std::cout << ">>>>>>> choice 2: single input parameter" << std::endl;
163    std::cout << "param_check -r|-w|-g -n para_name" << std::endl;
164    std::cout << "" << std::endl;
165}
166
167static void SetOptions(int argc, char *argv[], const option *options, TestInput &input)
168{
169    int index = 0;
170    const char *optStr = "hgrwln:";
171    int para = 0;
172    while ((para = getopt_long(argc, argv, optStr, options, &index)) != -1) {
173        switch (para) {
174            case 'h': {
175                PrintUsage();
176                exit(0);
177            }
178            case 'n': {
179                input.paraName = optarg;
180                break;
181            }
182            case 'g': {
183                input.cmd = 'g';
184                break;
185            }
186            case 'r': {
187                input.cmd = 'r';
188                break;
189            }
190            case 'w': {
191                input.cmd = 'w';
192                break;
193            }
194            case 'l': {
195                TestLoadList();
196                exit(0);
197            }
198            default:
199                std::cout << "Try 'param_check -h' for more information." << std::endl;
200                exit(-1);
201        }
202    }
203}
204
205static void TestWriteParameters(TestInput &testCmd)
206{
207    int fd[2];
208    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fd) < 0) {
209        perror("socketpair");
210        exit(EXIT_FAILURE);
211    }
212
213    SrcInfo info;
214    info.uc.pid = getpid();
215    info.uc.uid = getuid();
216    info.uc.gid = getgid();
217    info.sockFd = fd[0];
218    if (!testCmd.paraName.empty()) {
219        TestSetPara(testCmd.paraName, &info);
220        close(fd[0]);
221        close(fd[1]);
222        exit(0);
223    }
224    std::string paraName;
225    while (std::cin >> paraName) {
226        TestSetPara(paraName, &info);
227    }
228    close(fd[0]);
229    close(fd[1]);
230    exit(0);
231}
232
233static void Test(TestInput &testCmd)
234{
235    std::string paraName;
236    switch (testCmd.cmd) {
237        case 'g': {
238            if (!testCmd.paraName.empty()) {
239                TestGetContext(testCmd.paraName);
240                exit(0);
241            }
242            while (std::cin >> paraName) {
243                TestGetContext(paraName);
244            }
245            exit(0);
246        }
247        case 'r': {
248            if (!testCmd.paraName.empty()) {
249                TestReadPara(testCmd.paraName);
250                exit(0);
251            }
252            while (std::cin >> paraName) {
253                TestReadPara(paraName);
254            }
255            exit(0);
256        }
257        case 'w': {
258            TestWriteParameters(testCmd);
259            break;
260        }
261        default:
262            PrintUsage();
263            exit(-1);
264    }
265}
266
267int main(int argc, char *argv[])
268{
269    struct option options[] = {
270        {"help", no_argument, nullptr, 'h'}, {"paraName", required_argument, nullptr, 'n'},
271        {"read", no_argument, nullptr, 'r'}, {"write", no_argument, nullptr, 'w'},
272        {"list", no_argument, nullptr, 'l'}, {"getContext", no_argument, nullptr, 'g'},
273        {nullptr, no_argument, nullptr, 0},
274    };
275
276    if (argc == 1) {
277        PrintUsage();
278        exit(0);
279    }
280    InitParamSelinux(0);
281    TestInput testCmd;
282    SetOptions(argc, argv, options, testCmd);
283    Test(testCmd);
284    exit(0);
285}
286