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 #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 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 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 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 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