xref: /NextBSD/lib/libcapsicum/libcapsicum_pwd.c (revision 287e3b14e9552995def1802ec9c5034f4adf28ec)
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/types.h>
34 #include <sys/nv.h>
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <pwd.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "libcapsicum.h"
44 #include "libcapsicum_pwd.h"
45 
46 static struct passwd gpwd;
47 static char *gbuffer;
48 static size_t gbufsize;
49 
50 static int
passwd_resize(void)51 passwd_resize(void)
52 {
53 	char *buf;
54 
55 	if (gbufsize == 0)
56 		gbufsize = 1024;
57 	else
58 		gbufsize *= 2;
59 
60 	buf = gbuffer;
61 	gbuffer = realloc(buf, gbufsize);
62 	if (gbuffer == NULL) {
63 		free(buf);
64 		gbufsize = 0;
65 		return (ENOMEM);
66 	}
67 	memset(gbuffer, 0, gbufsize);
68 
69 	return (0);
70 }
71 
72 static int
passwd_unpack_string(const nvlist_t * nvl,const char * fieldname,char ** fieldp,char ** bufferp,size_t * bufsizep)73 passwd_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp,
74     char **bufferp, size_t *bufsizep)
75 {
76 	const char *str;
77 	size_t len;
78 
79 	str = nvlist_get_string(nvl, fieldname);
80 	len = strlcpy(*bufferp, str, *bufsizep);
81 	if (len >= *bufsizep)
82 		return (ERANGE);
83 	*fieldp = *bufferp;
84 	*bufferp += len + 1;
85 	*bufsizep -= len + 1;
86 
87 	return (0);
88 }
89 
90 static int
passwd_unpack(const nvlist_t * nvl,struct passwd * pwd,char * buffer,size_t bufsize)91 passwd_unpack(const nvlist_t *nvl, struct passwd *pwd, char *buffer,
92     size_t bufsize)
93 {
94 	int error;
95 
96 	if (!nvlist_exists_string(nvl, "pw_name"))
97 		return (EINVAL);
98 
99 	memset(pwd, 0, sizeof(*pwd));
100 
101 	error = passwd_unpack_string(nvl, "pw_name", &pwd->pw_name, &buffer,
102 	    &bufsize);
103 	if (error != 0)
104 		return (error);
105 	pwd->pw_uid = (uid_t)nvlist_get_number(nvl, "pw_uid");
106 	pwd->pw_gid = (gid_t)nvlist_get_number(nvl, "pw_gid");
107 	pwd->pw_change = (time_t)nvlist_get_number(nvl, "pw_change");
108 	error = passwd_unpack_string(nvl, "pw_passwd", &pwd->pw_passwd, &buffer,
109 	    &bufsize);
110 	if (error != 0)
111 		return (error);
112 	error = passwd_unpack_string(nvl, "pw_class", &pwd->pw_class, &buffer,
113 	    &bufsize);
114 	if (error != 0)
115 		return (error);
116 	error = passwd_unpack_string(nvl, "pw_gecos", &pwd->pw_gecos, &buffer,
117 	    &bufsize);
118 	if (error != 0)
119 		return (error);
120 	error = passwd_unpack_string(nvl, "pw_dir", &pwd->pw_dir, &buffer,
121 	    &bufsize);
122 	if (error != 0)
123 		return (error);
124 	error = passwd_unpack_string(nvl, "pw_shell", &pwd->pw_shell, &buffer,
125 	    &bufsize);
126 	if (error != 0)
127 		return (error);
128 	pwd->pw_expire = (time_t)nvlist_get_number(nvl, "pw_expire");
129 	pwd->pw_fields = (int)nvlist_get_number(nvl, "pw_fields");
130 
131 	return (0);
132 }
133 
134 static int
cap_getpwcommon_r(cap_channel_t * chan,const char * cmd,const char * login,uid_t uid,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)135 cap_getpwcommon_r(cap_channel_t *chan, const char *cmd, const char *login,
136     uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
137     struct passwd **result)
138 {
139 	nvlist_t *nvl;
140 	bool getpw_r;
141 	int error;
142 
143 	nvl = nvlist_create(0);
144 	nvlist_add_string(nvl, "cmd", cmd);
145 	if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0) {
146 		/* Add nothing. */
147 	} else if (strcmp(cmd, "getpwnam") == 0 ||
148 	    strcmp(cmd, "getpwnam_r") == 0) {
149 		nvlist_add_string(nvl, "name", login);
150 	} else if (strcmp(cmd, "getpwuid") == 0 ||
151 	    strcmp(cmd, "getpwuid_r") == 0) {
152 		nvlist_add_number(nvl, "uid", (uint64_t)uid);
153 	} else {
154 		abort();
155 	}
156 	nvl = cap_xfer_nvlist(chan, nvl, 0);
157 	if (nvl == NULL) {
158 		assert(errno != 0);
159 		*result = NULL;
160 		return (errno);
161 	}
162 	error = (int)nvlist_get_number(nvl, "error");
163 	if (error != 0) {
164 		nvlist_destroy(nvl);
165 		*result = NULL;
166 		return (error);
167 	}
168 
169 	if (!nvlist_exists_string(nvl, "pw_name")) {
170 		/* Not found. */
171 		nvlist_destroy(nvl);
172 		*result = NULL;
173 		return (0);
174 	}
175 
176 	getpw_r = (strcmp(cmd, "getpwent_r") == 0 ||
177 	    strcmp(cmd, "getpwnam_r") == 0 || strcmp(cmd, "getpwuid_r") == 0);
178 
179 	for (;;) {
180 		error = passwd_unpack(nvl, pwd, buffer, bufsize);
181 		if (getpw_r || error != ERANGE)
182 			break;
183 		assert(buffer == gbuffer);
184 		assert(bufsize == gbufsize);
185 		error = passwd_resize();
186 		if (error != 0)
187 			break;
188 		/* Update pointers after resize. */
189 		buffer = gbuffer;
190 		bufsize = gbufsize;
191 	}
192 
193 	nvlist_destroy(nvl);
194 
195 	if (error == 0)
196 		*result = pwd;
197 	else
198 		*result = NULL;
199 
200 	return (error);
201 }
202 
203 static struct passwd *
cap_getpwcommon(cap_channel_t * chan,const char * cmd,const char * login,uid_t uid)204 cap_getpwcommon(cap_channel_t *chan, const char *cmd, const char *login,
205     uid_t uid)
206 {
207 	struct passwd *result;
208 	int error, serrno;
209 
210 	serrno = errno;
211 
212 	error = cap_getpwcommon_r(chan, cmd, login, uid, &gpwd, gbuffer,
213 	    gbufsize, &result);
214 	if (error != 0) {
215 		errno = error;
216 		return (NULL);
217 	}
218 
219 	errno = serrno;
220 
221 	return (result);
222 }
223 
224 struct passwd *
cap_getpwent(cap_channel_t * chan)225 cap_getpwent(cap_channel_t *chan)
226 {
227 
228 	return (cap_getpwcommon(chan, "getpwent", NULL, 0));
229 }
230 
231 struct passwd *
cap_getpwnam(cap_channel_t * chan,const char * login)232 cap_getpwnam(cap_channel_t *chan, const char *login)
233 {
234 
235 	return (cap_getpwcommon(chan, "getpwnam", login, 0));
236 }
237 
238 struct passwd *
cap_getpwuid(cap_channel_t * chan,uid_t uid)239 cap_getpwuid(cap_channel_t *chan, uid_t uid)
240 {
241 
242 	return (cap_getpwcommon(chan, "getpwuid", NULL, uid));
243 }
244 
245 int
cap_getpwent_r(cap_channel_t * chan,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)246 cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer,
247     size_t bufsize, struct passwd **result)
248 {
249 
250 	return (cap_getpwcommon_r(chan, "getpwent_r", NULL, 0, pwd, buffer,
251 	    bufsize, result));
252 }
253 
254 int
cap_getpwnam_r(cap_channel_t * chan,const char * name,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)255 cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd,
256     char *buffer, size_t bufsize, struct passwd **result)
257 {
258 
259 	return (cap_getpwcommon_r(chan, "getpwnam_r", name, 0, pwd, buffer,
260 	    bufsize, result));
261 }
262 
263 int
cap_getpwuid_r(cap_channel_t * chan,uid_t uid,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)264 cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd, char *buffer,
265     size_t bufsize, struct passwd **result)
266 {
267 
268 	return (cap_getpwcommon_r(chan, "getpwuid_r", NULL, uid, pwd, buffer,
269 	    bufsize, result));
270 }
271 
272 int
cap_setpassent(cap_channel_t * chan,int stayopen)273 cap_setpassent(cap_channel_t *chan, int stayopen)
274 {
275 	nvlist_t *nvl;
276 
277 	nvl = nvlist_create(0);
278 	nvlist_add_string(nvl, "cmd", "setpassent");
279 	nvlist_add_bool(nvl, "stayopen", stayopen != 0);
280 	nvl = cap_xfer_nvlist(chan, nvl, 0);
281 	if (nvl == NULL)
282 		return (0);
283 	if (nvlist_get_number(nvl, "error") != 0) {
284 		errno = nvlist_get_number(nvl, "error");
285 		nvlist_destroy(nvl);
286 		return (0);
287 	}
288 	nvlist_destroy(nvl);
289 
290 	return (1);
291 }
292 
293 static void
cap_set_end_pwent(cap_channel_t * chan,const char * cmd)294 cap_set_end_pwent(cap_channel_t *chan, const char *cmd)
295 {
296 	nvlist_t *nvl;
297 
298 	nvl = nvlist_create(0);
299 	nvlist_add_string(nvl, "cmd", cmd);
300 	/* Ignore any errors, we have no way to report them. */
301 	nvlist_destroy(cap_xfer_nvlist(chan, nvl, 0));
302 }
303 
304 void
cap_setpwent(cap_channel_t * chan)305 cap_setpwent(cap_channel_t *chan)
306 {
307 
308 	cap_set_end_pwent(chan, "setpwent");
309 }
310 
311 void
cap_endpwent(cap_channel_t * chan)312 cap_endpwent(cap_channel_t *chan)
313 {
314 
315 	cap_set_end_pwent(chan, "endpwent");
316 }
317 
318 int
cap_pwd_limit_cmds(cap_channel_t * chan,const char * const * cmds,size_t ncmds)319 cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds)
320 {
321 	nvlist_t *limits, *nvl;
322 	unsigned int i;
323 
324 	if (cap_limit_get(chan, &limits) < 0)
325 		return (-1);
326 	if (limits == NULL) {
327 		limits = nvlist_create(0);
328 	} else {
329 		if (nvlist_exists_nvlist(limits, "cmds"))
330 			nvlist_free_nvlist(limits, "cmds");
331 	}
332 	nvl = nvlist_create(0);
333 	for (i = 0; i < ncmds; i++)
334 		nvlist_add_null(nvl, cmds[i]);
335 	nvlist_move_nvlist(limits, "cmds", nvl);
336 	return (cap_limit_set(chan, limits));
337 }
338 
339 int
cap_pwd_limit_fields(cap_channel_t * chan,const char * const * fields,size_t nfields)340 cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields,
341     size_t nfields)
342 {
343 	nvlist_t *limits, *nvl;
344 	unsigned int i;
345 
346 	if (cap_limit_get(chan, &limits) < 0)
347 		return (-1);
348 	if (limits == NULL) {
349 		limits = nvlist_create(0);
350 	} else {
351 		if (nvlist_exists_nvlist(limits, "fields"))
352 			nvlist_free_nvlist(limits, "fields");
353 	}
354 	nvl = nvlist_create(0);
355 	for (i = 0; i < nfields; i++)
356 		nvlist_add_null(nvl, fields[i]);
357 	nvlist_move_nvlist(limits, "fields", nvl);
358 	return (cap_limit_set(chan, limits));
359 }
360 
361 int
cap_pwd_limit_users(cap_channel_t * chan,const char * const * names,size_t nnames,uid_t * uids,size_t nuids)362 cap_pwd_limit_users(cap_channel_t *chan, const char * const *names,
363     size_t nnames, uid_t *uids, size_t nuids)
364 {
365 	nvlist_t *limits, *users;
366 	char nvlname[64];
367 	unsigned int i;
368 	int n;
369 
370 	if (cap_limit_get(chan, &limits) < 0)
371 		return (-1);
372 	if (limits == NULL) {
373 		limits = nvlist_create(0);
374 	} else {
375 		if (nvlist_exists_nvlist(limits, "users"))
376 			nvlist_free_nvlist(limits, "users");
377 	}
378 	users = nvlist_create(0);
379 	for (i = 0; i < nuids; i++) {
380 		n = snprintf(nvlname, sizeof(nvlname), "uid%u", i);
381 		assert(n > 0 && n < (int)sizeof(nvlname));
382 		nvlist_add_number(users, nvlname, (uint64_t)uids[i]);
383 	}
384 	for (i = 0; i < nnames; i++) {
385 		n = snprintf(nvlname, sizeof(nvlname), "name%u", i);
386 		assert(n > 0 && n < (int)sizeof(nvlname));
387 		nvlist_add_string(users, nvlname, names[i]);
388 	}
389 	nvlist_move_nvlist(limits, "users", users);
390 	return (cap_limit_set(chan, limits));
391 }
392