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