1 /*
2  * Copyright 2015, Intel Corporation
3  * Copyright (C) 2015 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * Written by William Roberts <william.c.roberts@intel.com>
18  *
19  */
20 
21 #include <errno.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <sys/limits.h>
28 
29 #define LOG_TAG "packagelistparser"
30 #include <utils/Log.h>
31 
32 #include <packagelistparser/packagelistparser.h>
33 
34 #define CLOGE(fmt, ...) \
35     do {\
36         IF_ALOGE() {\
37             ALOGE(fmt, ##__VA_ARGS__);\
38         }\
39     } while(0)
40 
get_gid_cnt(const char * gids)41 static size_t get_gid_cnt(const char *gids)
42 {
43     size_t cnt;
44 
45     if (*gids == '\0') {
46         return 0;
47     }
48 
49     if (!strcmp(gids, "none")) {
50         return 0;
51     }
52 
53     for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
54         ;
55 
56     return cnt;
57 }
58 
parse_gids(char * gids,gid_t * gid_list,size_t * cnt)59 static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
60 {
61     gid_t gid;
62     char* token;
63     char *endptr;
64     size_t cmp = 0;
65 
66     while ((token = strsep(&gids, ",\r\n"))) {
67 
68         if (cmp > *cnt) {
69             return false;
70         }
71 
72         gid = strtoul(token, &endptr, 10);
73         if (*endptr != '\0') {
74             return false;
75         }
76 
77         /*
78          * if unsigned long is greater than size of gid_t,
79          * prevent a truncation based roll-over
80          */
81         if (gid > GID_MAX) {
82             CLOGE("A gid in field \"gid list\" greater than GID_MAX");
83             return false;
84         }
85 
86         gid_list[cmp++] = gid;
87     }
88     return true;
89 }
90 
packagelist_parse(pfn_on_package callback,void * userdata)91 extern bool packagelist_parse(pfn_on_package callback, void *userdata)
92 {
93 
94     FILE *fp;
95     char *cur;
96     char *next;
97     char *endptr;
98     unsigned long tmp;
99     ssize_t bytesread;
100 
101     bool rc = false;
102     char *buf = NULL;
103     size_t buflen = 0;
104     unsigned long lineno = 1;
105     const char *errmsg = NULL;
106     struct pkg_info *pkg_info = NULL;
107 
108     fp = fopen(PACKAGES_LIST_FILE, "re");
109     if (!fp) {
110         CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
111                 strerror(errno));
112         return false;
113     }
114 
115     while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
116 
117         pkg_info = calloc(1, sizeof(*pkg_info));
118         if (!pkg_info) {
119             goto err;
120         }
121 
122         next = buf;
123 
124         cur = strsep(&next, " \t\r\n");
125         if (!cur) {
126             errmsg = "Could not get next token for \"package name\"";
127             goto err;
128         }
129 
130         pkg_info->name = strdup(cur);
131         if (!pkg_info->name) {
132             goto err;
133         }
134 
135         cur = strsep(&next, " \t\r\n");
136         if (!cur) {
137             errmsg = "Could not get next token for field \"uid\"";
138             goto err;
139         }
140 
141         tmp = strtoul(cur, &endptr, 10);
142         if (*endptr != '\0') {
143             errmsg = "Could not convert field \"uid\" to integer value";
144             goto err;
145         }
146 
147         /*
148          * if unsigned long is greater than size of uid_t,
149          * prevent a truncation based roll-over
150          */
151         if (tmp > UID_MAX) {
152             errmsg = "Field \"uid\" greater than UID_MAX";
153             goto err;
154         }
155 
156         pkg_info->uid = (uid_t) tmp;
157 
158         cur = strsep(&next, " \t\r\n");
159         if (!cur) {
160             errmsg = "Could not get next token for field \"debuggable\"";
161             goto err;
162         }
163 
164         tmp = strtoul(cur, &endptr, 10);
165         if (*endptr != '\0') {
166             errmsg = "Could not convert field \"debuggable\" to integer value";
167             goto err;
168         }
169 
170         /* should be a valid boolean of 1 or 0 */
171         if (!(tmp == 0 || tmp == 1)) {
172             errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
173             goto err;
174         }
175 
176         pkg_info->debuggable = (bool) tmp;
177 
178         cur = strsep(&next, " \t\r\n");
179         if (!cur) {
180             errmsg = "Could not get next token for field \"data dir\"";
181             goto err;
182         }
183 
184         pkg_info->data_dir = strdup(cur);
185         if (!pkg_info->data_dir) {
186             goto err;
187         }
188 
189         cur = strsep(&next, " \t\r\n");
190         if (!cur) {
191             errmsg = "Could not get next token for field \"seinfo\"";
192             goto err;
193         }
194 
195         pkg_info->seinfo = strdup(cur);
196         if (!pkg_info->seinfo) {
197             goto err;
198         }
199 
200         cur = strsep(&next, " \t\r\n");
201         if (!cur) {
202             errmsg = "Could not get next token for field \"gid(s)\"";
203             goto err;
204         }
205 
206         /*
207          * Parse the gid list, could be in the form of none, single gid or list:
208          * none
209          * gid
210          * gid, gid ...
211          */
212         pkg_info->gids.cnt = get_gid_cnt(cur);
213         if (pkg_info->gids.cnt > 0) {
214 
215             pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
216             if (!pkg_info->gids.gids) {
217                 goto err;
218             }
219 
220             rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
221             if (!rc) {
222                 errmsg = "Could not parse field \"gid list\"";
223                 goto err;
224             }
225         }
226 
227         rc = callback(pkg_info, userdata);
228         if (rc == false) {
229             /*
230              * We do not log this as this can be intentional from
231              * callback to abort processing. We go to out to not
232              * free the pkg_info
233              */
234             rc = true;
235             goto out;
236         }
237         lineno++;
238     }
239 
240     rc = true;
241 
242 out:
243     free(buf);
244     fclose(fp);
245     return rc;
246 
247 err:
248     if (errmsg) {
249         CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
250                 PACKAGES_LIST_FILE, lineno, errmsg);
251     }
252     rc = false;
253     packagelist_free(pkg_info);
254     goto out;
255 }
256 
packagelist_free(pkg_info * info)257 void packagelist_free(pkg_info *info)
258 {
259     if (info) {
260         free(info->name);
261         free(info->data_dir);
262         free(info->seinfo);
263         free(info->gids.gids);
264         free(info);
265     }
266 }
267