github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/cmd/libsnap-confine-private/infofile.c (about)

     1  /*
     2   * Copyright (C) 2019 Canonical Ltd
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License version 3 as
     6   * published by the Free Software Foundation.
     7   *
     8   * This program is distributed in the hope that it will be useful,
     9   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11   * GNU General Public License for more details.
    12   *
    13   * You should have received a copy of the GNU General Public License
    14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15   *
    16   */
    17  
    18  #include "infofile.h"
    19  
    20  #include <errno.h>
    21  #include <stdarg.h>
    22  #include <stdbool.h>
    23  #include <string.h>
    24  
    25  #include "../libsnap-confine-private/cleanup-funcs.h"
    26  #include "../libsnap-confine-private/error.h"
    27  #include "../libsnap-confine-private/string-utils.h"
    28  #include "../libsnap-confine-private/utils.h"
    29  
    30  int sc_infofile_get_key(FILE *stream, const char *key, char **value, sc_error **err_out) {
    31      return sc_infofile_get_ini_section_key(stream, NULL, key, value, err_out);
    32  }
    33  
    34  int sc_infofile_get_ini_section_key(FILE *stream, const char *section, const char *key, char **value,
    35                                      sc_error **err_out) {
    36      sc_error *err = NULL;
    37      size_t line_size = 0;
    38      char *line_buf SC_CLEANUP(sc_cleanup_string) = NULL;
    39  
    40      if (stream == NULL) {
    41          err = sc_error_init_api_misuse("stream cannot be NULL");
    42          goto out;
    43      }
    44      if (key == NULL) {
    45          err = sc_error_init_api_misuse("key cannot be NULL");
    46          goto out;
    47      }
    48      if (value == NULL) {
    49          err = sc_error_init_api_misuse("value cannot be NULL");
    50          goto out;
    51      }
    52      if (section != NULL && strlen(section) == 0) {
    53          err = sc_error_init_api_misuse("section name cannot be empty");
    54          goto out;
    55      }
    56  
    57      /* Store NULL in case we don't find the key.
    58       * This makes the value always well-defined. */
    59      *value = NULL;
    60  
    61      bool section_matched = false;
    62  
    63      /* This loop advances through subsequent lines. */
    64      for (int lineno = 1;; ++lineno) {
    65          errno = 0;
    66          ssize_t nread = getline(&line_buf, &line_size, stream);
    67          if (nread < 0 && errno != 0) {
    68              err = sc_error_init_from_errno(errno, "cannot read beyond line %d", lineno);
    69              goto out;
    70          }
    71          if (nread <= 0) {
    72              break; /* There is nothing more to read. */
    73          }
    74          /* NOTE: beyond this line the buffer is never empty (ie, nread > 0). */
    75  
    76          /* Guard against malformed input that may contain NUL bytes that
    77           * would confuse the code below. */
    78          if (memchr(line_buf, '\0', nread) != NULL) {
    79              err = sc_error_init_simple("line %d contains NUL byte", lineno);
    80              goto out;
    81          }
    82          /* Guard against non-strictly formatted input that doesn't contain
    83           * trailing newline. */
    84          if (line_buf[nread - 1] != '\n') {
    85              err = sc_error_init(SC_LIBSNAP_DOMAIN, 0, "line %d does not end with a newline", lineno);
    86              goto out;
    87          }
    88          /* Replace the trailing newline character with the NUL byte. */
    89          line_buf[nread - 1] = '\0';
    90  
    91          /* Handle ini sections (if requested via non-null section name) */
    92          if (line_buf[0] == '[') {
    93              if (section == NULL) {
    94                  err = sc_error_init_simple("line %d contains unexpected section", lineno);
    95                  goto out;
    96              }
    97              section_matched = false;
    98              char *start_section_name = line_buf + 1;
    99              // skip the leading [ and trailing \0
   100              char *end_section_name = memchr(start_section_name, ']', nread - 2);
   101              if (end_section_name == NULL) {
   102                  err = sc_error_init_simple("line %d is not a valid ini section", lineno);
   103                  goto out;
   104              }
   105              /* Replace closing ']' with string terminator byte */
   106              *end_section_name = '\0';
   107              if (sc_streq(start_section_name, section)) {
   108                  section_matched = true;
   109              }
   110              /* Advance to next line */
   111              continue;
   112          }
   113  
   114          /* Skip this line until we are in a matching section */
   115          if (section != NULL && !section_matched) {
   116              continue;
   117          }
   118  
   119          /* Guard against malformed input that does not contain '=' byte */
   120          char *eq_ptr = memchr(line_buf, '=', nread);
   121          if (eq_ptr == NULL) {
   122              err = sc_error_init_simple("line %d is not a key=value assignment", lineno);
   123              goto out;
   124          }
   125          /* Guard against malformed input with empty key. */
   126          if (eq_ptr == line_buf) {
   127              err = sc_error_init_simple("line %d contains empty key", lineno);
   128              goto out;
   129          }
   130          /* Replace the first '=' with string terminator byte. */
   131          *eq_ptr = '\0';
   132  
   133          /* If the key matches the one we are looking for, store it and stop scanning. */
   134          const char *scanned_key = line_buf;
   135          const char *scanned_value = eq_ptr + 1;
   136          if (sc_streq(scanned_key, key)) {
   137              *value = sc_strdup(scanned_value);
   138              break;
   139          }
   140      }
   141  
   142  out:
   143      return sc_error_forward(err_out, err);
   144  }