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