1 /*
2 * Vector handling (counted lists of char *'s).
3 *
4 * A vector is a table for handling a list of strings with less overhead than
5 * linked list. The intention is for vectors, once allocated, to be reused;
6 * this saves on memory allocations once the array of char *'s reaches a
7 * stable size.
8 *
9 * This is based on the util/vector.c library, but that library uses xmalloc
10 * routines to exit the program if memory allocation fails. This is a
11 * modified version of the vector library that instead returns false on
12 * failure to allocate memory, allowing the caller to do appropriate recovery.
13 *
14 * Vectors require list of strings, not arbitrary binary data, and cannot
15 * handle data elements containing nul characters.
16 *
17 * Only the portions of the vector library used by PAM modules are
18 * implemented.
19 *
20 * The canonical version of this file is maintained in the rra-c-util package,
21 * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
22 *
23 * Written by Russ Allbery <eagle@eyrie.org>
24 * Copyright 2017-2018 Russ Allbery <eagle@eyrie.org>
25 * Copyright 2010-2011, 2014
26 * The Board of Trustees of the Leland Stanford Junior University
27 *
28 * Copying and distribution of this file, with or without modification, are
29 * permitted in any medium without royalty provided the copyright notice and
30 * this notice are preserved. This file is offered as-is, without any
31 * warranty.
32 *
33 * SPDX-License-Identifier: FSFAP
34 */
35
36 #include <config.h>
37 #include <portable/system.h>
38
39 #include <pam-util/vector.h>
40
41
42 /*
43 * Allocate a new, empty vector. Returns NULL if memory allocation fails.
44 */
45 struct vector *
vector_new(void)46 vector_new(void)
47 {
48 struct vector *vector;
49
50 vector = calloc(1, sizeof(struct vector));
51 vector->allocated = 1;
52 vector->strings = calloc(1, sizeof(char *));
53 return vector;
54 }
55
56
57 /*
58 * Allocate a new vector that's a copy of an existing vector. Returns NULL if
59 * memory allocation fails.
60 */
61 struct vector *
vector_copy(const struct vector * old)62 vector_copy(const struct vector *old)
63 {
64 struct vector *vector;
65 size_t i;
66
67 vector = vector_new();
68 if (!vector_resize(vector, old->count)) {
69 vector_free(vector);
70 return NULL;
71 }
72 vector->count = old->count;
73 for (i = 0; i < old->count; i++) {
74 vector->strings[i] = strdup(old->strings[i]);
75 if (vector->strings[i] == NULL) {
76 vector_free(vector);
77 return NULL;
78 }
79 }
80 return vector;
81 }
82
83
84 /*
85 * Resize a vector (using reallocarray to resize the table). Return false if
86 * memory allocation fails.
87 */
88 bool
vector_resize(struct vector * vector,size_t size)89 vector_resize(struct vector *vector, size_t size)
90 {
91 size_t i;
92 char **strings;
93
94 if (vector->count > size) {
95 for (i = size; i < vector->count; i++)
96 free(vector->strings[i]);
97 vector->count = size;
98 }
99 if (size == 0)
100 size = 1;
101 strings = reallocarray(vector->strings, size, sizeof(char *));
102 if (strings == NULL)
103 return false;
104 vector->strings = strings;
105 vector->allocated = size;
106 return true;
107 }
108
109
110 /*
111 * Add a new string to the vector, resizing the vector as necessary. The
112 * vector is resized an element at a time; if a lot of resizes are expected,
113 * vector_resize should be called explicitly with a more suitable size.
114 * Return false if memory allocation fails.
115 */
116 bool
vector_add(struct vector * vector,const char * string)117 vector_add(struct vector *vector, const char *string)
118 {
119 size_t next = vector->count;
120
121 if (vector->count == vector->allocated)
122 if (!vector_resize(vector, vector->allocated + 1))
123 return false;
124 vector->strings[next] = strdup(string);
125 if (vector->strings[next] == NULL)
126 return false;
127 vector->count++;
128 return true;
129 }
130
131
132 /*
133 * Empty a vector but keep the allocated memory for the pointer table.
134 */
135 void
vector_clear(struct vector * vector)136 vector_clear(struct vector *vector)
137 {
138 size_t i;
139
140 for (i = 0; i < vector->count; i++)
141 if (vector->strings[i] != NULL)
142 free(vector->strings[i]);
143 vector->count = 0;
144 }
145
146
147 /*
148 * Free a vector completely.
149 */
150 void
vector_free(struct vector * vector)151 vector_free(struct vector *vector)
152 {
153 if (vector == NULL)
154 return;
155 vector_clear(vector);
156 free(vector->strings);
157 free(vector);
158 }
159
160
161 /*
162 * Given a vector that we may be reusing, clear it out. If the first argument
163 * is NULL, allocate a new vector. Used by vector_split*. Returns NULL if
164 * memory allocation fails.
165 */
166 static struct vector *
vector_reuse(struct vector * vector)167 vector_reuse(struct vector *vector)
168 {
169 if (vector == NULL)
170 return vector_new();
171 else {
172 vector_clear(vector);
173 return vector;
174 }
175 }
176
177
178 /*
179 * Given a string and a set of separators expressed as a string, count the
180 * number of strings that it will split into when splitting on those
181 * separators.
182 */
183 static size_t
split_multi_count(const char * string,const char * seps)184 split_multi_count(const char *string, const char *seps)
185 {
186 const char *p;
187 size_t count;
188
189 if (*string == '\0')
190 return 0;
191 for (count = 1, p = string + 1; *p != '\0'; p++)
192 if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
193 count++;
194
195 /*
196 * If the string ends in separators, we've overestimated the number of
197 * strings by one.
198 */
199 if (strchr(seps, p[-1]) != NULL)
200 count--;
201 return count;
202 }
203
204
205 /*
206 * Given a string, split it at any of the provided separators to form a
207 * vector, copying each string segment. If the third argument isn't NULL,
208 * reuse that vector; otherwise, allocate a new one. Any number of
209 * consecutive separators are considered a single separator. Returns NULL on
210 * memory allocation failure, after which the provided vector may only have
211 * partial results.
212 */
213 struct vector *
vector_split_multi(const char * string,const char * seps,struct vector * vector)214 vector_split_multi(const char *string, const char *seps, struct vector *vector)
215 {
216 const char *p, *start;
217 size_t i, count;
218 bool created = false;
219
220 if (vector == NULL)
221 created = true;
222 vector = vector_reuse(vector);
223 if (vector == NULL)
224 return NULL;
225
226 count = split_multi_count(string, seps);
227 if (vector->allocated < count && !vector_resize(vector, count))
228 goto fail;
229
230 vector->count = 0;
231 for (start = string, p = string, i = 0; *p != '\0'; p++)
232 if (strchr(seps, *p) != NULL) {
233 if (start != p) {
234 vector->strings[i] = strndup(start, (size_t)(p - start));
235 if (vector->strings[i] == NULL)
236 goto fail;
237 i++;
238 vector->count++;
239 }
240 start = p + 1;
241 }
242 if (start != p) {
243 vector->strings[i] = strndup(start, (size_t)(p - start));
244 if (vector->strings[i] == NULL)
245 goto fail;
246 vector->count++;
247 }
248 return vector;
249
250 fail:
251 if (created)
252 vector_free(vector);
253 return NULL;
254 }
255
256
257 /*
258 * Given a vector and a path to a program, exec that program with the vector
259 * as its arguments. This requires adding a NULL terminator to the vector and
260 * casting it appropriately. Returns 0 on success and -1 on error, like exec
261 * does.
262 */
263 int
vector_exec(const char * path,struct vector * vector)264 vector_exec(const char *path, struct vector *vector)
265 {
266 if (vector->allocated == vector->count)
267 if (!vector_resize(vector, vector->count + 1))
268 return -1;
269 vector->strings[vector->count] = NULL;
270 return execv(path, (char *const *) vector->strings);
271 }
272
273
274 /*
275 * Given a vector, a path to a program, and the environment, exec that program
276 * with the vector as its arguments and the given environment. This requires
277 * adding a NULL terminator to the vector and casting it appropriately.
278 * Returns 0 on success and -1 on error, like exec does.
279 */
280 int
vector_exec_env(const char * path,struct vector * vector,const char * const env[])281 vector_exec_env(const char *path, struct vector *vector,
282 const char *const env[])
283 {
284 if (vector->allocated == vector->count)
285 if (!vector_resize(vector, vector->count + 1))
286 return -1;
287 vector->strings[vector->count] = NULL;
288 return execve(path, (char *const *) vector->strings, (char *const *) env);
289 }
290