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