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