1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* Filesystem parameter parser. 3 * 4 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. 5 * Written by David Howells (dhowells@redhat.com) 6 */ 7 8 #include <linux/export.h> 9 #include <linux/fs_context.h> 10 #include <linux/fs_parser.h> 11 #include <linux/slab.h> 12 #include <linux/security.h> 13 #include <linux/namei.h> 14 #include "internal.h" 15 16 const struct constant_table bool_names[] = { 17 { "0", false }, 18 { "1", true }, 19 { "false", false }, 20 { "no", false }, 21 { "true", true }, 22 { "yes", true }, 23 { }, 24 }; 25 EXPORT_SYMBOL(bool_names); 26 27 static const struct constant_table * 28 __lookup_constant(const struct constant_table *tbl, const char *name) 29 { 30 for ( ; tbl->name; tbl++) 31 if (strcmp(name, tbl->name) == 0) 32 return tbl; 33 return NULL; 34 } 35 36 /** 37 * lookup_constant - Look up a constant by name in an ordered table 38 * @tbl: The table of constants to search. 39 * @name: The name to look up. 40 * @not_found: The value to return if the name is not found. 41 */ 42 int lookup_constant(const struct constant_table *tbl, const char *name, int not_found) 43 { 44 const struct constant_table *p = __lookup_constant(tbl, name); 45 46 return p ? p->value : not_found; 47 } 48 EXPORT_SYMBOL(lookup_constant); 49 50 static inline bool is_flag(const struct fs_parameter_spec *p) 51 { 52 return p->type == NULL; 53 } 54 55 static const struct fs_parameter_spec *fs_lookup_key( 56 const struct fs_parameter_spec *desc, 57 struct fs_parameter *param, bool *negated) 58 { 59 const struct fs_parameter_spec *p, *other = NULL; 60 const char *name = param->key; 61 bool want_flag = param->type == fs_value_is_flag; 62 63 *negated = false; 64 for (p = desc; p->name; p++) { 65 if (strcmp(p->name, name) != 0) 66 continue; 67 if (likely(is_flag(p) == want_flag)) 68 return p; 69 other = p; 70 } 71 if (want_flag) { 72 if (name[0] == 'n' && name[1] == 'o' && name[2]) { 73 for (p = desc; p->name; p++) { 74 if (strcmp(p->name, name + 2) != 0) 75 continue; 76 if (!(p->flags & fs_param_neg_with_no)) 77 continue; 78 *negated = true; 79 return p; 80 } 81 } 82 } 83 return other; 84 } 85 86 /* 87 * __fs_parse - Parse a filesystem configuration parameter 88 * @log: The filesystem context to log errors through. 89 * @desc: The parameter description to use. 90 * @param: The parameter. 91 * @result: Where to place the result of the parse 92 * 93 * Parse a filesystem configuration parameter and attempt a conversion for a 94 * simple parameter for which this is requested. If successful, the determined 95 * parameter ID is placed into @result->key, the desired type is indicated in 96 * @result->t and any converted value is placed into an appropriate member of 97 * the union in @result. 98 * 99 * The function returns the parameter number if the parameter was matched, 100 * -ENOPARAM if it wasn't matched and @desc->ignore_unknown indicated that 101 * unknown parameters are okay and -EINVAL if there was a conversion issue or 102 * the parameter wasn't recognised and unknowns aren't okay. 103 */ 104 int __fs_parse(struct p_log *log, 105 const struct fs_parameter_spec *desc, 106 struct fs_parameter *param, 107 struct fs_parse_result *result) 108 { 109 const struct fs_parameter_spec *p; 110 111 result->uint_64 = 0; 112 113 p = fs_lookup_key(desc, param, &result->negated); 114 if (!p) 115 return -ENOPARAM; 116 117 if (p->flags & fs_param_deprecated) 118 warn_plog(log, "Deprecated parameter '%s'", param->key); 119 120 /* Try to turn the type we were given into the type desired by the 121 * parameter and give an error if we can't. 122 */ 123 if (is_flag(p)) { 124 if (param->type != fs_value_is_flag) 125 return inval_plog(log, "Unexpected value for '%s'", 126 param->key); 127 result->boolean = !result->negated; 128 } else { 129 int ret = p->type(log, p, param, result); 130 if (ret) 131 return ret; 132 } 133 return p->opt; 134 } 135 EXPORT_SYMBOL(__fs_parse); 136 137 /** 138 * fs_lookup_param - Look up a path referred to by a parameter 139 * @fc: The filesystem context to log errors through. 140 * @param: The parameter. 141 * @want_bdev: T if want a blockdev 142 * @flags: Pathwalk flags passed to filename_lookup() 143 * @_path: The result of the lookup 144 */ 145 int fs_lookup_param(struct fs_context *fc, 146 struct fs_parameter *param, 147 bool want_bdev, 148 unsigned int flags, 149 struct path *_path) 150 { 151 struct filename *f; 152 bool put_f; 153 int ret; 154 155 switch (param->type) { 156 case fs_value_is_string: 157 f = getname_kernel(param->string); 158 if (IS_ERR(f)) 159 return PTR_ERR(f); 160 param->dirfd = AT_FDCWD; 161 put_f = true; 162 break; 163 case fs_value_is_filename: 164 f = param->name; 165 put_f = false; 166 break; 167 default: 168 return invalf(fc, "%s: not usable as path", param->key); 169 } 170 171 ret = filename_lookup(param->dirfd, f, flags, _path, NULL); 172 if (ret < 0) { 173 errorf(fc, "%s: Lookup failure for '%s'", param->key, f->name); 174 goto out; 175 } 176 177 if (want_bdev && 178 !S_ISBLK(d_backing_inode(_path->dentry)->i_mode)) { 179 path_put(_path); 180 _path->dentry = NULL; 181 _path->mnt = NULL; 182 errorf(fc, "%s: Non-blockdev passed as '%s'", 183 param->key, f->name); 184 ret = -ENOTBLK; 185 } 186 187 out: 188 if (put_f) 189 putname(f); 190 return ret; 191 } 192 EXPORT_SYMBOL(fs_lookup_param); 193 194 static int fs_param_bad_value(struct p_log *log, struct fs_parameter *param) 195 { 196 return inval_plog(log, "Bad value for '%s'", param->key); 197 } 198 199 int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p, 200 struct fs_parameter *param, struct fs_parse_result *result) 201 { 202 int b; 203 if (param->type != fs_value_is_string) 204 return fs_param_bad_value(log, param); 205 if (!*param->string && (p->flags & fs_param_can_be_empty)) 206 return 0; 207 b = lookup_constant(bool_names, param->string, -1); 208 if (b == -1) 209 return fs_param_bad_value(log, param); 210 result->boolean = b; 211 return 0; 212 } 213 EXPORT_SYMBOL(fs_param_is_bool); 214 215 int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p, 216 struct fs_parameter *param, struct fs_parse_result *result) 217 { 218 int base = (unsigned long)p->data; 219 if (param->type != fs_value_is_string) 220 return fs_param_bad_value(log, param); 221 if (!*param->string && (p->flags & fs_param_can_be_empty)) 222 return 0; 223 if (kstrtouint(param->string, base, &result->uint_32) < 0) 224 return fs_param_bad_value(log, param); 225 return 0; 226 } 227 EXPORT_SYMBOL(fs_param_is_u32); 228 229 int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p, 230 struct fs_parameter *param, struct fs_parse_result *result) 231 { 232 if (param->type != fs_value_is_string) 233 return fs_param_bad_value(log, param); 234 if (!*param->string && (p->flags & fs_param_can_be_empty)) 235 return 0; 236 if (kstrtoint(param->string, 0, &result->int_32) < 0) 237 return fs_param_bad_value(log, param); 238 return 0; 239 } 240 EXPORT_SYMBOL(fs_param_is_s32); 241 242 int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p, 243 struct fs_parameter *param, struct fs_parse_result *result) 244 { 245 if (param->type != fs_value_is_string) 246 return fs_param_bad_value(log, param); 247 if (!*param->string && (p->flags & fs_param_can_be_empty)) 248 return 0; 249 if (kstrtoull(param->string, 0, &result->uint_64) < 0) 250 return fs_param_bad_value(log, param); 251 return 0; 252 } 253 EXPORT_SYMBOL(fs_param_is_u64); 254 255 int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p, 256 struct fs_parameter *param, struct fs_parse_result *result) 257 { 258 const struct constant_table *c; 259 if (param->type != fs_value_is_string) 260 return fs_param_bad_value(log, param); 261 if (!*param->string && (p->flags & fs_param_can_be_empty)) 262 return 0; 263 c = __lookup_constant(p->data, param->string); 264 if (!c) 265 return fs_param_bad_value(log, param); 266 result->uint_32 = c->value; 267 return 0; 268 } 269 EXPORT_SYMBOL(fs_param_is_enum); 270 271 int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p, 272 struct fs_parameter *param, struct fs_parse_result *result) 273 { 274 if (param->type != fs_value_is_string || 275 (!*param->string && !(p->flags & fs_param_can_be_empty))) 276 return fs_param_bad_value(log, param); 277 return 0; 278 } 279 EXPORT_SYMBOL(fs_param_is_string); 280 281 int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p, 282 struct fs_parameter *param, struct fs_parse_result *result) 283 { 284 if (param->type != fs_value_is_blob) 285 return fs_param_bad_value(log, param); 286 return 0; 287 } 288 EXPORT_SYMBOL(fs_param_is_blob); 289 290 int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p, 291 struct fs_parameter *param, struct fs_parse_result *result) 292 { 293 switch (param->type) { 294 case fs_value_is_string: 295 if ((!*param->string && !(p->flags & fs_param_can_be_empty)) || 296 kstrtouint(param->string, 0, &result->uint_32) < 0) 297 break; 298 if (result->uint_32 <= INT_MAX) 299 return 0; 300 break; 301 case fs_value_is_file: 302 result->uint_32 = param->dirfd; 303 if (result->uint_32 <= INT_MAX) 304 return 0; 305 break; 306 default: 307 break; 308 } 309 return fs_param_bad_value(log, param); 310 } 311 EXPORT_SYMBOL(fs_param_is_fd); 312 313 int fs_param_is_file_or_string(struct p_log *log, 314 const struct fs_parameter_spec *p, 315 struct fs_parameter *param, 316 struct fs_parse_result *result) 317 { 318 switch (param->type) { 319 case fs_value_is_string: 320 return fs_param_is_string(log, p, param, result); 321 case fs_value_is_file: 322 result->uint_32 = param->dirfd; 323 if (result->uint_32 <= INT_MAX) 324 return 0; 325 break; 326 default: 327 break; 328 } 329 return fs_param_bad_value(log, param); 330 } 331 EXPORT_SYMBOL(fs_param_is_file_or_string); 332 333 int fs_param_is_uid(struct p_log *log, const struct fs_parameter_spec *p, 334 struct fs_parameter *param, struct fs_parse_result *result) 335 { 336 kuid_t uid; 337 338 if (fs_param_is_u32(log, p, param, result) != 0) 339 return fs_param_bad_value(log, param); 340 341 uid = make_kuid(current_user_ns(), result->uint_32); 342 if (!uid_valid(uid)) 343 return inval_plog(log, "Invalid uid '%s'", param->string); 344 345 result->uid = uid; 346 return 0; 347 } 348 EXPORT_SYMBOL(fs_param_is_uid); 349 350 int fs_param_is_gid(struct p_log *log, const struct fs_parameter_spec *p, 351 struct fs_parameter *param, struct fs_parse_result *result) 352 { 353 kgid_t gid; 354 355 if (fs_param_is_u32(log, p, param, result) != 0) 356 return fs_param_bad_value(log, param); 357 358 gid = make_kgid(current_user_ns(), result->uint_32); 359 if (!gid_valid(gid)) 360 return inval_plog(log, "Invalid gid '%s'", param->string); 361 362 result->gid = gid; 363 return 0; 364 } 365 EXPORT_SYMBOL(fs_param_is_gid); 366 367 int fs_param_is_blockdev(struct p_log *log, const struct fs_parameter_spec *p, 368 struct fs_parameter *param, struct fs_parse_result *result) 369 { 370 return 0; 371 } 372 EXPORT_SYMBOL(fs_param_is_blockdev); 373 374 int fs_param_is_path(struct p_log *log, const struct fs_parameter_spec *p, 375 struct fs_parameter *param, struct fs_parse_result *result) 376 { 377 return 0; 378 } 379 EXPORT_SYMBOL(fs_param_is_path); 380 381 #ifdef CONFIG_VALIDATE_FS_PARSER 382 /** 383 * validate_constant_table - Validate a constant table 384 * @tbl: The constant table to validate. 385 * @tbl_size: The size of the table. 386 * @low: The lowest permissible value. 387 * @high: The highest permissible value. 388 * @special: One special permissible value outside of the range. 389 */ 390 bool validate_constant_table(const struct constant_table *tbl, size_t tbl_size, 391 int low, int high, int special) 392 { 393 size_t i; 394 bool good = true; 395 396 if (tbl_size == 0) { 397 pr_warn("VALIDATE C-TBL: Empty\n"); 398 return true; 399 } 400 401 for (i = 0; i < tbl_size; i++) { 402 if (!tbl[i].name) { 403 pr_err("VALIDATE C-TBL[%zu]: Null\n", i); 404 good = false; 405 } else if (i > 0 && tbl[i - 1].name) { 406 int c = strcmp(tbl[i-1].name, tbl[i].name); 407 408 if (c == 0) { 409 pr_err("VALIDATE C-TBL[%zu]: Duplicate %s\n", 410 i, tbl[i].name); 411 good = false; 412 } 413 if (c > 0) { 414 pr_err("VALIDATE C-TBL[%zu]: Missorted %s>=%s\n", 415 i, tbl[i-1].name, tbl[i].name); 416 good = false; 417 } 418 } 419 420 if (tbl[i].value != special && 421 (tbl[i].value < low || tbl[i].value > high)) { 422 pr_err("VALIDATE C-TBL[%zu]: %s->%d const out of range (%d-%d)\n", 423 i, tbl[i].name, tbl[i].value, low, high); 424 good = false; 425 } 426 } 427 428 return good; 429 } 430 431 /** 432 * fs_validate_description - Validate a parameter description 433 * @name: The parameter name to search for. 434 * @desc: The parameter description to validate. 435 */ 436 bool fs_validate_description(const char *name, 437 const struct fs_parameter_spec *desc) 438 { 439 const struct fs_parameter_spec *param, *p2; 440 bool good = true; 441 442 for (param = desc; param->name; param++) { 443 /* Check for duplicate parameter names */ 444 for (p2 = desc; p2 < param; p2++) { 445 if (strcmp(param->name, p2->name) == 0) { 446 if (is_flag(param) != is_flag(p2)) 447 continue; 448 pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n", 449 name, param->name); 450 good = false; 451 } 452 } 453 } 454 return good; 455 } 456 #endif /* CONFIG_VALIDATE_FS_PARSER */ 457