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 }