1/*
2 * Copyright 2016 Chris Torek <chris.torek@gmail.com>
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#include <errno.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33#if defined(WITH_CASPER)
34#include <libcasper.h>
35#include <casper/cap_pwd.h>
36#include <casper/cap_grp.h>
37#endif
38
39#include "rfuncs.h"
40
41/*
42 * This is essentially a clone of the BSD basename_r function,
43 * which is like POSIX basename() but puts the result in a user
44 * supplied buffer.
45 *
46 * In BSD basename_r, the buffer must be least MAXPATHLEN bytes
47 * long.  In our case we take the size of the buffer as an argument.
48 *
49 * Note that it's impossible in general to do this without
50 * a temporary buffer since basename("foo/bar") is "bar",
51 * but basename("foo/bar/") is still "bar" -- no trailing
52 * slash is allowed.
53 *
54 * The return value is your supplied buffer <buf>, or NULL if
55 * the length of the basename of the supplied <path> equals or
56 * exceeds your indicated <bufsize>.
57 *
58 * As a special but useful case, if you supply NULL for the <buf>
59 * argument, we allocate the buffer dynamically to match the
60 * basename, i.e., the result is basically strdup()ed for you.
61 * In this case <bufsize> is ignored (recommended: pass 0 here).
62 */
63char *
64r_basename(const char *path, char *buf, size_t bufsize)
65{
66	const char *endp, *comp;
67	size_t len;
68
69	/*
70	 * NULL or empty path means ".".  This is perhaps overly
71	 * forgiving but matches libc basename_r(), and avoids
72	 * breaking the code below.
73	 */
74	if (path == NULL || *path == '\0') {
75		comp = ".";
76		len = 1;
77	} else {
78		/*
79		 * Back up over any trailing slashes.  If we reach
80		 * the top of the path and it's still a trailing
81		 * slash, it's also a leading slash and the entire
82		 * path is just "/" (or "//", or "///", etc).
83		 */
84		endp = path + strlen(path) - 1;
85		while (*endp == '/' && endp > path)
86			endp--;
87		/* Invariant: *endp != '/' || endp == path */
88		if (*endp == '/') {
89			/* then endp==path and hence entire path is "/" */
90			comp = "/";
91			len = 1;
92		} else {
93			/*
94			 * We handled empty strings earlier, and
95			 * we just proved *endp != '/'.  Hence
96			 * we have a non-empty basename, ending
97			 * at endp.
98			 *
99			 * Back up one path name component.  The
100			 * part between these two is the basename.
101			 *
102			 * Note that we only stop backing up when
103			 * either comp==path, or comp[-1] is '/'.
104			 *
105			 * Suppose path[0] is '/'.  Then, since *endp
106			 * is *not* '/', we had comp>path initially, and
107			 * stopped backing up because we found a '/'
108			 * (perhaps path[0], perhaps a later '/').
109			 *
110			 * Or, suppose path[0] is NOT '/'.  Then,
111			 * either there are no '/'s at all and
112			 * comp==path, or comp[-1] is '/'.
113			 *
114			 * In all cases, we want all bytes from *comp
115			 * to *endp, inclusive.
116			 */
117			comp = endp;
118			while (comp > path && comp[-1] != '/')
119				comp--;
120			len = (size_t)(endp - comp + 1);
121		}
122	}
123	if (buf == NULL) {
124		buf = malloc(len + 1);
125		if (buf == NULL)
126			return (NULL);
127	} else {
128		if (len >= bufsize) {
129			errno = ENAMETOOLONG;
130			return (NULL);
131		}
132	}
133	memcpy(buf, comp, len);
134	buf[len] = '\0';
135	return (buf);
136}
137
138/*
139 * This is much like POSIX dirname(), but is reentrant.
140 *
141 * We examine a path, find the directory portion, and copy that
142 * to a user supplied buffer <buf> of the given size <bufsize>.
143 *
144 * Note that dirname("/foo/bar/") is "/foo", dirname("/foo") is "/",
145 * and dirname("////") is "/". However, dirname("////foo/bar") is
146 * "////foo" (we do not resolve these leading slashes away -- this
147 * matches the BSD libc behavior).
148 *
149 * The return value is your supplied buffer <buf>, or NULL if
150 * the length of the dirname of the supplied <path> equals or
151 * exceeds your indicated <bufsize>.
152 *
153 * As a special but useful case, if you supply NULL for the <buf>
154 * argument, we allocate the buffer dynamically to match the
155 * dirname, i.e., the result is basically strdup()ed for you.
156 * In this case <bufsize> is ignored (recommended: pass 0 here).
157 */
158char *
159r_dirname(const char *path, char *buf, size_t bufsize)
160{
161	const char *endp, *dirpart;
162	size_t len;
163
164	/*
165	 * NULL or empty path means ".".  This is perhaps overly
166	 * forgiving but matches libc dirname(), and avoids breaking
167	 * the code below.
168	 */
169	if (path == NULL || *path == '\0') {
170		dirpart = ".";
171		len = 1;
172	} else {
173		/*
174		 * Back up over any trailing slashes, then back up
175		 * one path name, then back up over more slashes.
176		 * In all cases, stop as soon as endp==path so
177		 * that we do not back out of the buffer entirely.
178		 *
179		 * The first loop takes care of trailing slashes
180		 * in names like "/foo/bar//" (where the dirname
181		 * part is to be "/foo"), the second strips out
182		 * the non-dir-name part, and the third leaves us
183		 * pointing to the end of the directory component.
184		 *
185		 * If the entire name is of the form "/foo" or
186		 * "//foo" (or "/foo/", etc, but we already
187		 * handled trailing slashes), we end up pointing
188		 * to the leading "/", which is what we want; but
189		 * if it is of the form "foo" (or "foo/", etc) we
190		 * point to a non-slash.  So, if (and only if)
191		 * endp==path AND *endp is not '/', the dirname is
192		 * ".", but in all cases, the LENGTH of the
193		 * dirname is (endp-path+1).
194		 */
195		endp = path + strlen(path) - 1;
196		while (endp > path && *endp == '/')
197			endp--;
198		while (endp > path && *endp != '/')
199			endp--;
200		while (endp > path && *endp == '/')
201			endp--;
202
203		len = (size_t)(endp - path + 1);
204		if (endp == path && *endp != '/')
205			dirpart = ".";
206		else
207			dirpart = path;
208	}
209	if (buf == NULL) {
210		buf = malloc(len + 1);
211		if (buf == NULL)
212			return (NULL);
213	} else {
214		if (len >= bufsize) {
215			errno = ENAMETOOLONG;
216			return (NULL);
217		}
218	}
219	memcpy(buf, dirpart, len);
220	buf[len] = '\0';
221	return (buf);
222}
223
224static void
225r_pginit(struct r_pgdata *pg)
226{
227
228	/* Note: init to half size since the first thing we do is double it */
229	pg->r_pgbufsize = 1 << 9;
230	pg->r_pgbuf = NULL;	/* note that realloc(NULL) == malloc */
231}
232
233static int
234r_pgexpand(struct r_pgdata *pg)
235{
236	size_t nsize;
237
238	nsize = pg->r_pgbufsize << 1;
239	if (nsize >= (1 << 20) ||
240	    (pg->r_pgbuf = realloc(pg->r_pgbuf, nsize)) == NULL)
241		return (ENOMEM);
242	return (0);
243}
244
245void
246r_pgfree(struct r_pgdata *pg)
247{
248
249	free(pg->r_pgbuf);
250}
251
252struct passwd *
253r_getpwuid(uid_t uid, struct r_pgdata *pg)
254{
255	struct passwd *result = NULL;
256	int error;
257
258	r_pginit(pg);
259	do {
260		error = r_pgexpand(pg);
261		if (error == 0)
262			error = getpwuid_r(uid, &pg->r_pgun.un_pw,
263			    pg->r_pgbuf, pg->r_pgbufsize, &result);
264	} while (error == ERANGE);
265
266	return (error ? NULL : result);
267}
268
269struct group *
270r_getgrgid(gid_t gid, struct r_pgdata *pg)
271{
272	struct group *result = NULL;
273	int error;
274
275	r_pginit(pg);
276	do {
277		error = r_pgexpand(pg);
278		if (error == 0)
279			error = getgrgid_r(gid, &pg->r_pgun.un_gr,
280			    pg->r_pgbuf, pg->r_pgbufsize, &result);
281	} while (error == ERANGE);
282
283	return (error ? NULL : result);
284}
285
286#if defined(WITH_CASPER)
287struct passwd *
288r_cap_getpwuid(cap_channel_t *cap, uid_t uid, struct r_pgdata *pg)
289{
290	struct passwd *result = NULL;
291	int error;
292
293	r_pginit(pg);
294	do {
295		error = r_pgexpand(pg);
296		if (error == 0)
297			error = cap_getpwuid_r(cap, uid, &pg->r_pgun.un_pw,
298			    pg->r_pgbuf, pg->r_pgbufsize, &result);
299	} while (error == ERANGE);
300
301	return (error ? NULL : result);
302}
303
304struct group *
305r_cap_getgrgid(cap_channel_t *cap, gid_t gid, struct r_pgdata *pg)
306{
307	struct group *result = NULL;
308	int error;
309
310	r_pginit(pg);
311	do {
312		error = r_pgexpand(pg);
313		if (error == 0)
314			error = cap_getgrgid_r(cap, gid, &pg->r_pgun.un_gr,
315			    pg->r_pgbuf, pg->r_pgbufsize, &result);
316	} while (error == ERANGE);
317
318	return (error ? NULL : result);
319}
320#endif
321