Fix a few 'gcc -fanalyzer' warnings.
[pwmd.git] / src / acl.c
blobc5906c3518fc43276db51ae625d3213dc0863e43
1 /*
2 Copyright (C) 2006-2023 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 Pwmd is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <grp.h>
29 #include "common.h"
30 #include "acl.h"
31 #include "rcfile.h"
32 #include "util-misc.h"
33 #include "util-string.h"
34 #include "mutex.h"
35 #include "mem.h"
36 #ifdef WITH_GNUTLS
37 #include "tls.h"
38 #endif
40 static char *
41 acl_get_proc (pid_t pid, gpg_error_t *rc)
43 #ifdef WITH_PROC_SYMLINK
44 char buf[64];
45 char path[PATH_MAX];
46 char *p;
47 ssize_t n;
49 snprintf (buf, sizeof (buf), "/proc/%lu/%s", (long)pid, WITH_PROC_SYMLINK);
50 n = readlink (buf, path, sizeof (path)-1);
52 if (n == -1)
54 *rc = gpg_error_from_syserror ();
55 log_write ("%s: %s", __FUNCTION__, gpg_strerror (*rc));
56 return NULL;
59 *rc = 0;
60 path[n] = 0;
62 p = str_dup (path);
63 if (!p)
64 *rc = GPG_ERR_ENOMEM;
66 return p;
67 #else
68 *rc = 0;
69 return NULL;
70 #endif
73 /* Check for further matches of a pathname which may be negated. */
74 static int
75 acl_check_proc_dup (char **users, const char *path)
77 char **p;
78 int allowed = 0;
80 for (p = users; p && *p; p++)
82 int not = 0;
83 char *s = *p;
85 if (*s == '!' || *s == '-')
87 not = 1;
88 s++;
91 if (*s != '/')
92 continue;
94 if (!strcmp (s, path))
95 allowed = !not;
98 return allowed;
101 static int
102 acl_check_proc (char **users, char *user, const char *path, int *have_path)
104 char *p = user;
106 if (*p == '!' || *p == '-')
107 p++;
109 if (*p != '/')
110 return 2;
112 *have_path = 1;
113 if (!strcmp (user, path))
114 return acl_check_proc_dup (users, user);
116 return 0;
119 gpg_error_t
120 do_validate_peer (assuan_context_t ctx, const char *section,
121 assuan_peercred_t *peer, char **rpath)
123 char **users;
124 int allowed = 0;
125 int have_user = 0;
126 gpg_error_t rc;
127 struct client_s *client = assuan_get_pointer (ctx);
129 if (!client)
130 return GPG_ERR_FORBIDDEN;
132 #ifdef WITH_GNUTLS
133 if (client->thd->remote)
134 return tls_validate_access (client, section);
135 #endif
137 rc = assuan_get_peercred (ctx, peer);
138 if (rc)
139 return rc;
141 users = config_get_list (section, "allowed");
142 if (users)
144 char **cmds = NULL;
146 for (char **p = users; !rc && *p; p++)
148 char *user = *p;
149 int not = 0;
151 if (*user == '-' || *user == '!')
153 not = 1;
154 user++;
157 if (*user == '/')
159 if (strv_printf (&cmds, "%s%s", not ? "!" : "", user) == 0)
161 strv_free (cmds);
162 strv_free (users);
163 return GPG_ERR_ENOMEM;
168 for (char **p = users; !rc && *p; p++)
170 char *user = *p;
172 if (*user == '/' || (*user == '!' && *(user+1) == '/')
173 || (*user == '-' && *(user+1) == '/'))
174 continue;
176 have_user = 1;
177 rc = acl_check_common(client, user, (*peer)->uid, (*peer)->gid,
178 &allowed);
181 /* Skip getting process name from a UID that is not our own to prevent
182 * an ENOENT error since most modern systems hide process details from
183 * other UID's. Access will be allowed based on other tests (filesystem
184 * ACL to the socket, configuration, etc). */
185 int exec_allowed = 0;
186 int have_path = 0;
187 int same_uid = (*peer)->uid == getuid();
189 if (allowed && !rc && same_uid)
191 char *path = acl_get_proc ((*peer)->pid, &rc);
193 for (char **p = users; !rc && path && *p; p++)
195 exec_allowed = acl_check_proc (users, *p, path, &have_path);
196 if (!rc && exec_allowed == 1)
197 break;
200 if (rpath && exec_allowed)
201 *rpath = path;
202 else
203 xfree (path);
206 strv_free (users);
208 if (!rc && cmds && (allowed || !have_user) && same_uid)
210 allowed = 0;
211 if (!client->thd->cmdname)
212 client->thd->cmdname = rpath ? *rpath : NULL;
214 for (char **p = cmds; !rc && p && *p && !allowed; p++)
216 rc = acl_check_common (client, *p, client->thd->peer->uid,
217 client->thd->peer->gid, &allowed);
221 if (have_path)
222 allowed = allowed && exec_allowed == 1;
224 strv_free(cmds);
227 return allowed && !rc ? 0 : rc ? rc : GPG_ERR_FORBIDDEN;
230 /* Test if uid is a member of group 'name'. Invert when 'not' is true. */
231 #ifdef HAVE_GETGRNAM_R
232 static gpg_error_t
233 acl_check_group (const char *name, uid_t uid, gid_t gid, int not, int *allowed)
235 char *buf;
236 struct group gr, *gresult;
237 size_t len = sysconf (_SC_GETGR_R_SIZE_MAX);
238 int err;
239 gpg_error_t rc = 0;
241 if (len == -1)
242 len = 16384;
244 buf = xmalloc (len);
245 if (!buf)
246 return GPG_ERR_ENOMEM;
248 err = getgrnam_r (name, &gr, buf, len, &gresult);
249 if (!err && gresult)
251 if (gresult->gr_gid == gid)
253 xfree (buf);
254 *allowed = !not;
255 return 0;
258 for (char **t = gresult->gr_mem; !rc && *t; t++)
260 char *tbuf;
261 struct passwd pw;
262 struct passwd *result = get_pwd_struct (*t, 0, &pw, &tbuf, &rc);
264 if (!rc && result && result->pw_uid == uid)
266 xfree (tbuf);
267 *allowed = !not;
268 break;
271 xfree (tbuf);
274 xfree (buf);
275 return rc;
277 else if (err)
278 rc = gpg_error_from_errno (err);
280 xfree (buf);
281 return rc ? rc : !gresult ? 0 : GPG_ERR_EACCES;
283 #else
284 static gpg_error_t
285 acl_check_group (const char *name, uid_t uid, gid_t gid, int not, int *allowed)
287 struct group *gresult = NULL;
288 gpg_error_t rc = 0;
290 errno = 0;
291 gresult = getgrnam (name);
292 if (!errno && gresult && gresult->gr_gid == gid)
294 *allowed = !not;
295 return 0;
297 else if (errno)
298 rc = gpg_error_from_syserror ();
300 if (!gresult)
301 return rc;
303 for (char **t = gresult->gr_mem; !rc && *t; t++)
305 char *buf;
306 struct passwd pw;
307 struct passwd *result = get_pwd_struct (*t, 0, &pw, &buf, &rc);
309 if (!rc && result && result->pw_uid == uid)
311 xfree (buf);
312 *allowed = !not;
313 break;
316 xfree (buf);
319 return rc;
321 #endif
323 gpg_error_t
324 peer_is_invoker(struct client_s *client)
326 struct invoking_user_s *user;
327 int allowed = 0;
329 if (client->thd->state == CLIENT_STATE_UNKNOWN)
330 return GPG_ERR_EACCES;
332 for (user = invoking_users; user; user = user->next)
334 #ifdef WITH_GNUTLS
335 if (client->thd->remote)
337 if (user->type == INVOKING_TLS
338 && !strcmp(client->thd->tls->fp, user->id))
339 allowed = user->not ? 0 : 1;
341 continue;
343 #endif
345 if (user->type == INVOKING_GID)
347 gpg_error_t rc = acl_check_group (user->id,
348 client->thd->peer->uid,
349 client->thd->peer->gid,
350 user->not, &allowed);
351 if (rc)
352 return rc;
354 else if (user->type == INVOKING_UID && client->thd->peer->uid == user->uid)
355 allowed = user->not ? 0 : 1;
358 return allowed ? 0 : GPG_ERR_EACCES;
361 gpg_error_t
362 acl_check_common (struct client_s *client, const char *user, uid_t uid,
363 gid_t gid, int *allowed)
365 int not = 0;
366 int rw = 0;
367 int tls = 0;
368 int cmd = 0;
369 gpg_error_t rc = 0;
371 if (!user || !*user)
372 return 0;
374 if (*user == '-' || *user == '!')
376 not = 1;
377 user++;
380 if (*user == '+') // not implemented yet
381 rw = 1;
383 if (*user == '#') // TLS fingerprint hash
384 tls = 1;
386 if (*user == '/') // client command name path
387 cmd = 1;
389 if (rw || tls)
390 user++;
392 if (tls)
394 #ifdef WITH_GNUTLS
395 if (client->thd->remote)
397 if (!strcasecmp (client->thd->tls->fp, user))
398 *allowed = !not;
401 return 0;
402 #else
403 (void)client;
404 return 0;
405 #endif
407 #ifdef WITH_GNUTLS
408 else if (client->thd->remote) // Remote client with no FP in the ACL
409 return 0;
410 #endif
412 if (*user == '@') // all users in group
413 return acl_check_group (user+1, uid, gid, not, allowed);
414 else if (!cmd)
416 char *buf;
417 struct passwd pw;
418 struct passwd *pwd = get_pwd_struct (user, 0, &pw, &buf, &rc);
420 if (!rc && pwd && pwd->pw_uid == uid)
421 *allowed = !not;
423 xfree (buf);
426 if (!rc && cmd && client->thd->cmdname)
428 if (!strcmp (user, client->thd->cmdname))
429 *allowed = !not;
430 else
431 *allowed = 0;
434 return rc;
437 gpg_error_t
438 validate_peer (struct client_s *cl)
440 gpg_error_t rc;
441 char *path = NULL;
443 #ifdef WITH_GNUTLS
444 if (cl->thd->remote)
445 return tls_validate_access (cl, NULL);
446 #endif
448 MUTEX_LOCK (&cn_mutex);
449 pthread_cleanup_push (release_mutex_cb, &cn_mutex);
450 rc = do_validate_peer (cl->ctx, "global", &cl->thd->peer, &path);
451 pthread_cleanup_pop (1);
452 log_write ("peer %s: path=%s, uid=%i, gid=%i, pid=%i, rc=%u",
453 !rc ? _("accepted") : _("rejected"), path,
454 cl->thd->peer->uid, cl->thd->peer->gid,
455 cl->thd->peer->pid, rc);
457 if (!rc)
458 cl->thd->cmdname = path;
459 else
460 xfree (path);
462 return rc;