PageRenderTime 24ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/remctl-3.2/server/config.c

#
C | 1034 lines | 663 code | 91 blank | 280 comment | 186 complexity | ae7cd2ca0ea3309152007383b641db12 MD5 | raw file
Possible License(s): ISC
  1. /*
  2. * Configuration parsing.
  3. *
  4. * These are the functions for parsing the remctld configuration file and
  5. * checking access.
  6. *
  7. * Written by Russ Allbery <rra@stanford.edu>
  8. * Based on work by Anton Ushakov
  9. * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
  10. * The Board of Trustees of the Leland Stanford Junior University
  11. * Copyright 2008 Carnegie Mellon University
  12. *
  13. * See LICENSE for licensing terms.
  14. */
  15. #include <config.h>
  16. #include <portable/system.h>
  17. #include <ctype.h>
  18. #include <dirent.h>
  19. #include <errno.h>
  20. #ifdef HAVE_PCRE
  21. # include <pcre.h>
  22. #endif
  23. #include <pwd.h>
  24. #ifdef HAVE_REGCOMP
  25. # include <regex.h>
  26. #endif
  27. #include <sys/stat.h>
  28. #include <server/internal.h>
  29. #include <util/macros.h>
  30. #include <util/messages.h>
  31. #include <util/vector.h>
  32. #include <util/xmalloc.h>
  33. /*
  34. * acl_gput_file is currently used only by the test suite to point GPUT at a
  35. * separate file for testing. If it becomes available as a configurable
  36. * parameter, we'll want to do something other than a local static variable
  37. * for it.
  38. */
  39. #ifdef HAVE_GPUT
  40. # include <gput.h>
  41. static char *acl_gput_file = NULL;
  42. #endif
  43. /* Return codes for configuration and ACL parsing. */
  44. enum config_status {
  45. CONFIG_SUCCESS = 0,
  46. CONFIG_NOMATCH = -1,
  47. CONFIG_ERROR = -2,
  48. CONFIG_DENY = -3
  49. };
  50. /* Holds information about parsing configuration options. */
  51. struct config_option {
  52. const char *name;
  53. enum config_status (*parse)(struct confline *, char *option,
  54. const char *file, size_t lineno);
  55. };
  56. /* Holds information about ACL schemes */
  57. struct acl_scheme {
  58. const char *name;
  59. enum config_status (*check)(const char *user, const char *data,
  60. const char *file, int lineno);
  61. };
  62. /*
  63. * The following must match the indexes of these schemes in schemes[].
  64. * They're used to implement default ACL schemes in particular contexts.
  65. */
  66. #define ACL_SCHEME_FILE 0
  67. #define ACL_SCHEME_PRINC 1
  68. /* Forward declarations. */
  69. static enum config_status acl_check(const char *user, const char *entry,
  70. int def_index, const char *file,
  71. int lineno);
  72. /*
  73. * Check a filename for acceptable characters. Returns true if the file
  74. * consists solely of [a-zA-Z0-9_-] and false otherwise.
  75. */
  76. static bool
  77. valid_filename(const char *filename)
  78. {
  79. const char *p;
  80. for (p = filename; *p != '\0'; p++) {
  81. if (*p >= 'A' && *p <= 'Z')
  82. continue;
  83. if (*p >= 'a' && *p <= 'z')
  84. continue;
  85. if (*p >= '0' && *p <= '9')
  86. continue;
  87. if (*p == '_' || *p == '-')
  88. continue;
  89. return false;
  90. }
  91. return true;
  92. }
  93. /*
  94. * Process a request for including a file, either for configuration or for
  95. * ACLs. Called by read_conf_file and acl_check_file.
  96. *
  97. * Takes the file to include, the current file, the line number, the function
  98. * to call for each included file, and a piece of data to pass to that
  99. * function. Handles including either files or directories. When used for
  100. * processing ACL files named in the configuration file, the current file and
  101. * line number will be passed as zero.
  102. *
  103. * If the function returns a value less than -1, return its return code. If
  104. * the file is recursively included or if there is an error in reading a file
  105. * or processing an include directory, return CONFIG_ERROR. Otherwise, return
  106. * the greatest of all status codes returned by function, or CONFIG_NOMATCH if
  107. * the file was empty.
  108. */
  109. static enum config_status
  110. handle_include(const char *included, const char *file, int lineno,
  111. enum config_status (*function)(void *, const char *),
  112. void *data)
  113. {
  114. struct stat st;
  115. /* Sanity checking. */
  116. if (strcmp(included, file) == 0) {
  117. warn("%s:%d: %s recursively included", file, lineno, file);
  118. return CONFIG_ERROR;
  119. }
  120. if (stat(included, &st) < 0) {
  121. syswarn("%s:%d: included file %s not found", file, lineno, included);
  122. return CONFIG_ERROR;
  123. }
  124. /*
  125. * If it's a directory, include everything in the directory whose
  126. * filenames contain only the allowed characters. Otherwise, just include
  127. * the one file.
  128. */
  129. if (!S_ISDIR(st.st_mode)) {
  130. return (*function)(data, included);
  131. } else {
  132. DIR *dir;
  133. struct dirent *entry;
  134. int status = CONFIG_NOMATCH;
  135. int last;
  136. dir = opendir(included);
  137. if (dir == NULL) {
  138. syswarn("%s:%d: included directory %s cannot be opened", file,
  139. lineno, included);
  140. return CONFIG_ERROR;
  141. }
  142. while ((entry = readdir(dir)) != NULL) {
  143. char *path;
  144. if (!valid_filename(entry->d_name))
  145. continue;
  146. xasprintf(&path, "%s/%s", included, entry->d_name);
  147. last = (*function)(data, path);
  148. free(path);
  149. if (last < -1) {
  150. closedir(dir);
  151. return last;
  152. }
  153. if (last > status)
  154. status = last;
  155. }
  156. closedir(dir);
  157. return status;
  158. }
  159. }
  160. /*
  161. * Check whether a given string is an option setting. An option setting must
  162. * start with a letter and consists of one or more alphanumerics or hyphen (-)
  163. * followed by an equal sign (=) and at least one additional character.
  164. */
  165. static bool
  166. is_option(const char *option)
  167. {
  168. const char *p;
  169. if (!isalpha((unsigned int) *option))
  170. return false;
  171. for (p = option; *p != '\0'; p++) {
  172. if (*p == '=' && p > option && p[1] != '\0')
  173. return true;
  174. if (!isalnum((unsigned int) *p) && *p != '-')
  175. return false;
  176. }
  177. return false;
  178. }
  179. /*
  180. * Convert a string to a long, validating that the number converts properly.
  181. * Returns true on success and false on failure.
  182. */
  183. static bool
  184. convert_number(const char *string, long *result)
  185. {
  186. char *end;
  187. long arg;
  188. errno = 0;
  189. arg = strtol(string, &end, 10);
  190. if (errno != 0 || *end != '\0' || arg <= 0)
  191. return false;
  192. *result = arg;
  193. return true;
  194. }
  195. /*
  196. * Parse the logmask configuration option. Verifies the listed argument
  197. * numbers, stores them in the configuration line struct, and returns
  198. * CONFIG_SUCCESS on success and CONFIG_ERROR on error.
  199. */
  200. static enum config_status
  201. option_logmask(struct confline *confline, char *value, const char *name,
  202. size_t lineno)
  203. {
  204. struct cvector *logmask;
  205. size_t i;
  206. long mask;
  207. logmask = cvector_split(value, ',', NULL);
  208. if (confline->logmask != NULL)
  209. free(confline->logmask);
  210. confline->logmask = xcalloc(logmask->count + 1, sizeof(unsigned int));
  211. for (i = 0; i < logmask->count; i++) {
  212. if (!convert_number(logmask->strings[i], &mask)) {
  213. warn("%s:%lu: invalid logmask parameter %s", name,
  214. (unsigned long) lineno, logmask->strings[i]);
  215. cvector_free(logmask);
  216. free(confline->logmask);
  217. confline->logmask = NULL;
  218. return CONFIG_ERROR;
  219. }
  220. confline->logmask[i] = mask;
  221. }
  222. confline->logmask[i] = 0;
  223. cvector_free(logmask);
  224. return CONFIG_SUCCESS;
  225. }
  226. /*
  227. * Parse the stdin configuration option. Verifies the argument number or
  228. * "last" keyword, stores it in the configuration line struct, and returns
  229. * CONFIG_SUCCESS on success and CONFIG_ERROR on error.
  230. */
  231. static enum config_status
  232. option_stdin(struct confline *confline, char *value, const char *name,
  233. size_t lineno)
  234. {
  235. if (strcmp(value, "last") == 0)
  236. confline->stdin_arg = -1;
  237. else if (!convert_number(value, &confline->stdin_arg)) {
  238. warn("%s:%lu: invalid stdin value %s", name,
  239. (unsigned long) lineno, value);
  240. return CONFIG_ERROR;
  241. }
  242. return CONFIG_SUCCESS;
  243. }
  244. /*
  245. * Parse the user configuration option. Verifies that the value is either a
  246. * UID or a username, stores the user in the configuration line struct, and
  247. * looks up the UID and primary GID and stores that in the configuration
  248. * struct as well. Returns CONFIG_SUCCESS on success and CONFIG_ERROR on
  249. * error.
  250. */
  251. static enum config_status
  252. option_user(struct confline *confline, char *value, const char *name,
  253. size_t lineno)
  254. {
  255. struct passwd *pw;
  256. long uid;
  257. if (convert_number(value, &uid))
  258. pw = getpwuid(uid);
  259. else
  260. pw = getpwnam(value);
  261. if (pw == NULL) {
  262. warn("%s:%lu: invalid user value %s", name, (unsigned long) lineno,
  263. value);
  264. return CONFIG_ERROR;
  265. }
  266. confline->user = xstrdup(pw->pw_name);
  267. confline->uid = pw->pw_uid;
  268. confline->gid = pw->pw_gid;
  269. return CONFIG_SUCCESS;
  270. }
  271. /*
  272. * Parse the summary configuration option. Stores the summary option in the
  273. * configuration line struct. Returns CONFIG_SUCCESS on success and
  274. * CONFIG_ERROR on error.
  275. */
  276. static enum config_status
  277. option_summary(struct confline *confline, char *value,
  278. const char *name UNUSED, size_t lineno UNUSED)
  279. {
  280. confline->summary = value;
  281. return CONFIG_SUCCESS;
  282. }
  283. /*
  284. * Parse the help configuration option. Stores the help option in the
  285. * configuration line struct. Returns CONFIG_SUCCESS on success and
  286. * CONFIG_ERROR on error.
  287. */
  288. static enum config_status
  289. option_help(struct confline *confline, char *value,
  290. const char *name UNUSED, size_t lineno UNUSED)
  291. {
  292. confline->help = value;
  293. return CONFIG_SUCCESS;
  294. }
  295. /*
  296. * The table relating configuration option names to functions.
  297. */
  298. static const struct config_option options[] = {
  299. { "help", option_help },
  300. { "logmask", option_logmask },
  301. { "stdin", option_stdin },
  302. { "summary", option_summary },
  303. { "user", option_user },
  304. { NULL, NULL }
  305. };
  306. /*
  307. * Parse a configuration option. This is something after the command but
  308. * before the ACLs that contains an equal sign. The configuration option is
  309. * the part before the equals and the value is the part afterwards. Takes the
  310. * configuration line, the option string, the file name, and the line number,
  311. * and stores data in the configuration line struct as needed.
  312. *
  313. * Returns CONFIG_SUCCESS on success and CONFIG_ERROR on error, reporting an
  314. * error message.
  315. */
  316. static enum config_status
  317. parse_conf_option(struct confline *confline, char *option, const char *name,
  318. size_t lineno)
  319. {
  320. char *end;
  321. size_t length;
  322. const struct config_option *handler;
  323. end = strchr(option, '=');
  324. if (end == NULL) {
  325. warn("%s:%lu: invalid option %s", name, (unsigned long) lineno,
  326. option);
  327. return CONFIG_ERROR;
  328. }
  329. length = end - option;
  330. for (handler = options; handler->name != NULL; handler++)
  331. if (strlen(handler->name) == length)
  332. if (strncmp(handler->name, option, length) == 0)
  333. return (handler->parse)(confline, end + 1, name, lineno);
  334. warn("%s:%lu: unknown option %s", name, (unsigned long) lineno, option);
  335. return CONFIG_ERROR;
  336. }
  337. /*
  338. * Reads the configuration file and parses every line, populating a data
  339. * structure that will be traversed on each request to translate a command
  340. * into an executable path and ACL file.
  341. *
  342. * config is populated with the parsed configuration file. Empty lines and
  343. * lines beginning with # are ignored. Each line is divided into fields,
  344. * separated by spaces. The fields are defined by struct confline. Lines
  345. * ending in backslash are continued on the next line. config is passed in as
  346. * a void * so that read_conf_file and acl_check_file can use common include
  347. * handling code.
  348. *
  349. * As a special case, include <file> will call read_conf_file recursively to
  350. * parse an included file (or, if <file> is a directory, every file in that
  351. * directory that doesn't contain a period).
  352. *
  353. * Returns CONFIG_SUCCESS on success and CONFIG_ERROR on error, reporting an
  354. * error message.
  355. */
  356. static enum config_status
  357. read_conf_file(void *data, const char *name)
  358. {
  359. struct config *config = data;
  360. FILE *file;
  361. char *buffer, *p, *option;
  362. size_t bufsize, length, size, count, i, arg_i;
  363. enum config_status s;
  364. struct vector *line = NULL;
  365. struct confline *confline = NULL;
  366. size_t lineno = 0;
  367. DIR *dir = NULL;
  368. bufsize = 1024;
  369. buffer = xmalloc(bufsize);
  370. file = fopen(name, "r");
  371. if (file == NULL) {
  372. free(buffer);
  373. syswarn("cannot open config file %s", name);
  374. return CONFIG_ERROR;
  375. }
  376. while (fgets(buffer, bufsize, file) != NULL) {
  377. length = strlen(buffer);
  378. if (length == 2 && buffer[length - 1] != '\n') {
  379. warn("%s:%lu: no final newline", name, (unsigned long) lineno);
  380. goto fail;
  381. }
  382. if (length < 2)
  383. continue;
  384. /*
  385. * Allow for long lines and continuation lines. As long as we've
  386. * either filled the buffer or have a line ending in a backslash, we
  387. * keep reading more data. If we filled the buffer, increase it by
  388. * another 1KB; otherwise, back up and write over the backslash and
  389. * newline.
  390. */
  391. p = buffer + length - 2;
  392. while (length > 2 && (p[1] != '\n' || p[0] == '\\')) {
  393. if (p[1] != '\n') {
  394. bufsize += 1024;
  395. buffer = xrealloc(buffer, bufsize);
  396. } else {
  397. length -= 2;
  398. lineno++;
  399. }
  400. if (fgets(buffer + length, bufsize - length, file) == NULL) {
  401. warn("%s:%lu: no final line or newline", name,
  402. (unsigned long) lineno);
  403. goto fail;
  404. }
  405. length = strlen(buffer);
  406. p = buffer + length - 2;
  407. }
  408. if (length > 0)
  409. buffer[length - 1] = '\0';
  410. lineno++;
  411. /*
  412. * Skip blank lines or commented-out lines. Note that because of the
  413. * above logic, comments can be continued on the next line, so be
  414. * careful.
  415. */
  416. p = buffer;
  417. while (isspace((int) *p))
  418. p++;
  419. if (*p == '\0' || *p == '#')
  420. continue;
  421. /*
  422. * We have a valid configuration line. Do a quick syntax check and
  423. * handle include.
  424. */
  425. line = vector_split_space(buffer, NULL);
  426. if (line->count == 2 && strcmp(line->strings[0], "include") == 0) {
  427. s = handle_include(line->strings[1], name, lineno, read_conf_file,
  428. config);
  429. if (s < -1)
  430. goto fail;
  431. vector_free(line);
  432. line = NULL;
  433. continue;
  434. } else if (line->count < 4) {
  435. warn("%s:%lu: parse error", name, (unsigned long) lineno);
  436. goto fail;
  437. }
  438. /*
  439. * Okay, we have a regular configuration line. Make sure there's
  440. * space for it in the config struct and stuff the vector into place.
  441. */
  442. if (config->count == config->allocated) {
  443. if (config->allocated < 4)
  444. config->allocated = 4;
  445. else
  446. config->allocated *= 2;
  447. size = config->allocated * sizeof(struct confline *);
  448. config->rules = xrealloc(config->rules, size);
  449. }
  450. confline = xcalloc(1, sizeof(struct confline));
  451. confline->line = line;
  452. confline->command = line->strings[0];
  453. confline->subcommand = line->strings[1];
  454. confline->program = line->strings[2];
  455. /*
  456. * Parse config options.
  457. */
  458. for (arg_i = 3; arg_i < line->count; arg_i++) {
  459. option = line->strings[arg_i];
  460. if (!is_option(option))
  461. break;
  462. s = parse_conf_option(confline, option, name, lineno);
  463. if (s != CONFIG_SUCCESS)
  464. goto fail;
  465. }
  466. /*
  467. * One more syntax error possibility here: a line that only has a
  468. * logmask setting but no ACL files.
  469. */
  470. if (line->count <= arg_i) {
  471. warn("%s:%lu: config parse error", name, (unsigned long) lineno);
  472. goto fail;
  473. }
  474. /* Grab the metadata and list of ACL files. */
  475. confline->file = xstrdup(name);
  476. confline->lineno = lineno;
  477. count = line->count - arg_i + 1;
  478. confline->acls = xmalloc(count * sizeof(char *));
  479. for (i = 0; i < line->count - arg_i; i++)
  480. confline->acls[i] = line->strings[i + arg_i];
  481. confline->acls[i] = NULL;
  482. /* Success. Put the configuration line in place. */
  483. config->rules[config->count] = confline;
  484. config->count++;
  485. confline = NULL;
  486. line = NULL;
  487. }
  488. /* Free allocated memory and return success. */
  489. free(buffer);
  490. fclose(file);
  491. return 0;
  492. /* Abort with an error. */
  493. fail:
  494. if (dir != NULL)
  495. closedir(dir);
  496. if (line != NULL)
  497. vector_free(line);
  498. if (confline != NULL) {
  499. if (confline->logmask != NULL)
  500. free(confline->logmask);
  501. free(confline);
  502. }
  503. free(buffer);
  504. fclose(file);
  505. return CONFIG_ERROR;
  506. }
  507. /*
  508. * Check to see if a principal is authorized by a given ACL file.
  509. *
  510. * This function is used to handle included ACL files and only does a simple
  511. * check to prevent infinite recursion, so be careful. The first argument is
  512. * the user to check, which is passed in as a void * so that acl_check_file
  513. * and read_conf_file can share common include-handling code.
  514. *
  515. * Returns the result of the first check that returns a result other than
  516. * CONFIG_NOMATCH, or CONFIG_NOMATCH if no check returns some other value.
  517. * Also returns CONFIG_ERROR on some sort of failure (such as failure to read
  518. * a file or a syntax error).
  519. */
  520. static enum config_status
  521. acl_check_file_internal(void *data, const char *aclfile)
  522. {
  523. const char *user = data;
  524. FILE *file = NULL;
  525. char buffer[BUFSIZ];
  526. char *p;
  527. int lineno;
  528. enum config_status s;
  529. size_t length;
  530. struct vector *line = NULL;
  531. file = fopen(aclfile, "r");
  532. if (file == NULL) {
  533. syswarn("cannot open ACL file %s", aclfile);
  534. return CONFIG_ERROR;
  535. }
  536. lineno = 0;
  537. while (fgets(buffer, sizeof(buffer), file) != NULL) {
  538. lineno++;
  539. length = strlen(buffer);
  540. if (length >= sizeof(buffer) - 1) {
  541. warn("%s:%d: ACL file line too long", aclfile, lineno);
  542. goto fail;
  543. }
  544. /*
  545. * Skip blank lines or commented-out lines and remove trailing
  546. * whitespace.
  547. */
  548. p = buffer + length - 1;
  549. while (p > buffer && isspace((int) *p))
  550. p--;
  551. p[1] = '\0';
  552. p = buffer;
  553. while (isspace((int) *p))
  554. p++;
  555. if (*p == '\0' || *p == '#')
  556. continue;
  557. /* Parse the line. */
  558. if (strchr(p, ' ') == NULL)
  559. s = acl_check(user, p, ACL_SCHEME_PRINC, aclfile, lineno);
  560. else {
  561. line = vector_split_space(buffer, NULL);
  562. if (line->count == 2 && strcmp(line->strings[0], "include") == 0) {
  563. s = acl_check(data, line->strings[1], ACL_SCHEME_FILE,
  564. aclfile, lineno);
  565. vector_free(line);
  566. line = NULL;
  567. } else {
  568. warn("%s:%d: parse error", aclfile, lineno);
  569. goto fail;
  570. }
  571. }
  572. if (s != CONFIG_NOMATCH) {
  573. fclose(file);
  574. return s;
  575. }
  576. }
  577. return CONFIG_NOMATCH;
  578. fail:
  579. if (line != NULL)
  580. vector_free(line);
  581. if (file != NULL)
  582. fclose(file);
  583. return CONFIG_ERROR;
  584. }
  585. /*
  586. * The ACL check operation for the file method. Takes the user to check, the
  587. * ACL file or directory name, and the referencing file name and line number.
  588. *
  589. * Conceptually, this returns CONFIG_SUCCESS if the user is authorized,
  590. * CONFIG_NOMATCH if they aren't, CONFIG_ERROR on some sort of failure, and
  591. * CONFIG_DENY for an explicit deny. What actually happens is the result of
  592. * the interplay between handle_include and acl_check_file_internal:
  593. *
  594. * - For each file, return the first result other than CONFIG_NOMATCH
  595. * (indicating no match), or CONFIG_NOMATCH if there is no other result.
  596. *
  597. * - Return the first result from any file less than CONFIG_NOMATCH,
  598. * indicating a failure or an explicit deny.
  599. *
  600. * - If there is no result less than CONFIG_NOMATCH, return the largest
  601. * remaining result, which should be CONFIG_SUCCESS or CONFIG_NOMATCH.
  602. */
  603. static enum config_status
  604. acl_check_file(const char *user, const char *aclfile, const char *file,
  605. int lineno)
  606. {
  607. return handle_include(aclfile, file, lineno, acl_check_file_internal,
  608. (void *) user);
  609. }
  610. /*
  611. * The ACL check operation for the princ method. Takes the user to check, the
  612. * principal name we are checking against, and the referencing file name and
  613. * line number.
  614. *
  615. * Returns CONFIG_SUCCESS if the user is authorized, or CONFIG_NOMATCH if they
  616. * aren't.
  617. */
  618. static enum config_status
  619. acl_check_princ(const char *user, const char *data, const char *file UNUSED,
  620. int lineno UNUSED)
  621. {
  622. return (strcmp(user, data) == 0) ? CONFIG_SUCCESS : CONFIG_NOMATCH;
  623. }
  624. /*
  625. * The ACL check operation for the deny method. Takes the user to check, the
  626. * scheme:method we are checking against, and the referencing file name and
  627. * line number.
  628. *
  629. * This one is a little unusual:
  630. *
  631. * - If the recursive check matches (status CONFIG_SUCCESS), it returns
  632. * CONFIG_DENY. This is treated by handle_include and
  633. * acl_check_file_internal as an error condition, and causes processing to
  634. * be stopped immediately, without doing further checks as would be done for
  635. * a normal CONFIG_NOMATCH "no match" return.
  636. *
  637. * - If the recursive check does not match (status CONFIG_NOMATCH), it returns
  638. * CONFIG_NOMATCH, which indicates "no match". This allows processing to
  639. * continue without either granting or denying access.
  640. *
  641. * - If the recursive check returns CONFIG_DENY, that is treated as a forced
  642. * deny from a recursive call to acl_check_deny, and is returned as
  643. * CONFIG_NOMATCH, indicating "no match".
  644. *
  645. * Any other result indicates a processing error and is returned as-is.
  646. */
  647. static enum config_status
  648. acl_check_deny(const char *user, const char *data, const char *file,
  649. int lineno)
  650. {
  651. enum config_status s;
  652. s = acl_check(user, data, ACL_SCHEME_PRINC, file, lineno);
  653. switch (s) {
  654. case CONFIG_SUCCESS: return CONFIG_DENY;
  655. case CONFIG_NOMATCH: return CONFIG_NOMATCH;
  656. case CONFIG_DENY: return CONFIG_NOMATCH;
  657. case CONFIG_ERROR: return CONFIG_ERROR;
  658. default: return s;
  659. }
  660. }
  661. /*
  662. * Sets the GPUT ACL file. Currently, this function is only used by the test
  663. * suite.
  664. */
  665. #ifdef HAVE_GPUT
  666. void
  667. server_config_set_gput_file(char *file)
  668. {
  669. acl_gput_file = file;
  670. }
  671. #else
  672. void
  673. server_config_set_gput_file(char *file UNUSED)
  674. {
  675. return;
  676. }
  677. #endif
  678. /*
  679. * The ACL check operation for the gput method. Takes the user to check, the
  680. * GPUT group name (and optional transform) we are checking against, and the
  681. * referencing file name and line number.
  682. *
  683. * The syntax of the data is "group" or "group[xform]".
  684. *
  685. * Returns CONFIG_SUCCESS if the user is authorized, CONFIG_NOMATCH if they
  686. * aren't, and CONFIG_ERROR on some sort of failure (such as failure to read a
  687. * file or a syntax error).
  688. */
  689. #ifdef HAVE_GPUT
  690. static enum config_status
  691. acl_check_gput(const char *user, const char *data, const char *file,
  692. int lineno)
  693. {
  694. GPUT *G;
  695. char *role, *xform, *xform_start, *xform_end;
  696. enum config_status s;
  697. xform_start = strchr(data, '[');
  698. if (xform_start != NULL) {
  699. xform_end = strchr(xform_start + 1, ']');
  700. if (xform_end == NULL) {
  701. warn("%s:%d: missing ] in GPUT specification '%s'", file, lineno,
  702. data);
  703. return CONFIG_ERROR;
  704. }
  705. if (xform_end[1] != '\0') {
  706. warn("%s:%d: invalid GPUT specification '%s'", file, lineno,
  707. data);
  708. return CONFIG_ERROR;
  709. }
  710. role = xstrndup(data, xform_start - data);
  711. xform = xstrndup(xform_start + 1, xform_end - (xform_start + 1));
  712. } else {
  713. role = (char *) data;
  714. xform = NULL;
  715. }
  716. /*
  717. * Sigh; apparently I wasn't flexible enough in GPUT error reporting. You
  718. * can direct diagnostics to a file descriptor, but there's not much else
  719. * you can do with them. In a future GPUT version, I'll make it possible
  720. * to have diagnostics reported via a callback.
  721. */
  722. G = gput_open(acl_gput_file, NULL);
  723. if (G == NULL)
  724. s = CONFIG_ERROR;
  725. else {
  726. if (gput_check(G, role, (char *) user, xform, NULL))
  727. s = CONFIG_SUCCESS;
  728. else
  729. s = CONFIG_NOMATCH;
  730. gput_close(G);
  731. }
  732. if (xform_start) {
  733. free(role);
  734. free(xform);
  735. }
  736. return s;
  737. }
  738. #endif /* HAVE_GPUT */
  739. /*
  740. * The ACL check operation for PCRE matches. Takes the user to check, the
  741. * regular expression, and the referencing file name and line number. This
  742. * can be used to do things like allow only host principals and deny everyone
  743. * else.
  744. */
  745. #ifdef HAVE_PCRE
  746. static enum config_status
  747. acl_check_pcre(const char *user, const char *data, const char *file,
  748. int lineno)
  749. {
  750. pcre *regex;
  751. const char *error;
  752. int offset, status;
  753. regex = pcre_compile(data, PCRE_NO_AUTO_CAPTURE, &error, &offset, NULL);
  754. if (regex == NULL) {
  755. warn("%s:%d: compilation of regex '%s' failed around %d", file,
  756. lineno, data, offset);
  757. return CONFIG_ERROR;
  758. }
  759. status = pcre_exec(regex, NULL, user, strlen(user), 0, 0, NULL, 0);
  760. pcre_free(regex);
  761. switch (status) {
  762. case 0:
  763. return CONFIG_SUCCESS;
  764. case PCRE_ERROR_NOMATCH:
  765. return CONFIG_NOMATCH;
  766. default:
  767. warn("%s:%d: matching with regex '%s' failed with status %d", file,
  768. lineno, data, status);
  769. return CONFIG_ERROR;
  770. }
  771. }
  772. #endif /* HAVE_PCRE */
  773. /*
  774. * The ACL check operation for POSIX regex matches. Takes the user to check,
  775. * the regular expression, and the referencing file name and line number.
  776. * This can be used to do things like allow only host principals and deny
  777. * everyone else.
  778. */
  779. #ifdef HAVE_REGCOMP
  780. static enum config_status
  781. acl_check_regex(const char *user, const char *data, const char *file,
  782. int lineno)
  783. {
  784. regex_t regex;
  785. char error[BUFSIZ];
  786. int status;
  787. enum config_status result;
  788. memset(&regex, 0, sizeof(regex));
  789. status = regcomp(&regex, data, REG_EXTENDED | REG_NOSUB);
  790. if (status != 0) {
  791. regerror(status, &regex, error, sizeof(error));
  792. warn("%s:%d: compilation of regex '%s' failed: %s", file, lineno,
  793. data, error);
  794. return CONFIG_ERROR;
  795. }
  796. status = regexec(&regex, user, 0, NULL, 0);
  797. switch (status) {
  798. case 0:
  799. result = CONFIG_SUCCESS;
  800. break;
  801. case REG_NOMATCH:
  802. result = CONFIG_NOMATCH;
  803. break;
  804. default:
  805. regerror(status, &regex, error, sizeof(error));
  806. warn("%s:%d: matching with regex '%s' failed: %s", file, lineno,
  807. data, error);
  808. result = CONFIG_ERROR;
  809. break;
  810. }
  811. regfree(&regex);
  812. return result;
  813. }
  814. #endif /* HAVE_REGCOMP */
  815. /*
  816. * The table relating ACL scheme names to functions. The first two ACL
  817. * schemes must remain in their current slots or the index constants set at
  818. * the top of the file need to change.
  819. */
  820. static const struct acl_scheme schemes[] = {
  821. { "file", acl_check_file },
  822. { "princ", acl_check_princ },
  823. { "deny", acl_check_deny },
  824. #ifdef HAVE_GPUT
  825. { "gput", acl_check_gput },
  826. #else
  827. { "gput", NULL },
  828. #endif
  829. #ifdef HAVE_PCRE
  830. { "pcre", acl_check_pcre },
  831. #else
  832. { "pcre", NULL },
  833. #endif
  834. #ifdef HAVE_REGCOMP
  835. { "regex", acl_check_regex },
  836. #else
  837. { "regex", NULL },
  838. #endif
  839. { NULL, NULL }
  840. };
  841. /*
  842. * The access control check switch. Takes the user to check, the ACL entry,
  843. * default scheme index, and referencing file name and line number.
  844. *
  845. * Returns CONFIG_SUCCESS if the user is authorized, CONFIG_NOMATCH if they
  846. * aren't, CONFIG_ERROR on some sort of failure (such as failure to read a
  847. * file or a syntax error), and CONFIG_DENY for an explicit deny.
  848. */
  849. static enum config_status
  850. acl_check(const char *user, const char *entry, int def_index,
  851. const char *file, int lineno)
  852. {
  853. const struct acl_scheme *scheme;
  854. char *prefix;
  855. const char *data;
  856. data = strchr(entry, ':');
  857. if (data != NULL) {
  858. prefix = xstrndup(entry, data - entry);
  859. data++;
  860. for (scheme = schemes; scheme->name != NULL; scheme++)
  861. if (strcmp(prefix, scheme->name) == 0)
  862. break;
  863. if (scheme->name == NULL) {
  864. warn("%s:%d: invalid ACL scheme '%s'", file, lineno, prefix);
  865. free(prefix);
  866. return CONFIG_ERROR;
  867. }
  868. free(prefix);
  869. } else {
  870. /* Use the default scheme. */
  871. scheme = schemes + def_index;
  872. data = entry;
  873. }
  874. if (scheme->check == NULL) {
  875. warn("%s:%d: ACL scheme '%s' is not supported", file, lineno,
  876. scheme->name);
  877. return CONFIG_ERROR;
  878. }
  879. return scheme->check(user, data, file, lineno);
  880. }
  881. /*
  882. * Load a configuration file. Returns a newly allocated config struct if
  883. * successful or NULL on failure, logging an appropriate error message.
  884. */
  885. struct config *
  886. server_config_load(const char *file)
  887. {
  888. struct config *config;
  889. /* Read the configuration file. */
  890. config = xcalloc(1, sizeof(struct config));
  891. if (read_conf_file(config, file) != 0) {
  892. free(config);
  893. return NULL;
  894. }
  895. return config;
  896. }
  897. /*
  898. * Free the config structure created by calling server_config_load.
  899. */
  900. void
  901. server_config_free(struct config *config)
  902. {
  903. struct confline *rule;
  904. size_t i;
  905. for (i = 0; i < config->count; i++) {
  906. rule = config->rules[i];
  907. if (rule->logmask != NULL)
  908. free(rule->logmask);
  909. if (rule->user != NULL)
  910. free(rule->user);
  911. if (rule->acls != NULL)
  912. free(rule->acls);
  913. if (rule->line != NULL)
  914. vector_free(rule->line);
  915. if (rule->file != NULL)
  916. free(rule->file);
  917. }
  918. free(config->rules);
  919. free(config);
  920. }
  921. /*
  922. * Given the confline corresponding to the command and the principal
  923. * requesting access, see if the command is allowed. Return true if so, false
  924. * otherwise.
  925. */
  926. bool
  927. server_config_acl_permit(struct confline *cline, const char *user)
  928. {
  929. char **acls = cline->acls;
  930. size_t i;
  931. enum config_status status;
  932. if (strcmp(acls[0], "ANYUSER") == 0)
  933. return true;
  934. for (i = 0; acls[i] != NULL; i++) {
  935. status = acl_check(user, acls[i], ACL_SCHEME_FILE, cline->file,
  936. cline->lineno);
  937. if (status == 0)
  938. return true;
  939. else if (status < -1)
  940. return false;
  941. }
  942. return false;
  943. }