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/>.
25 #include <sys/types.h>
32 #include "util-misc.h"
33 #include "util-string.h"
41 acl_get_proc (pid_t pid
, gpg_error_t
*rc
)
43 #ifdef WITH_PROC_SYMLINK
49 snprintf (buf
, sizeof (buf
), "/proc/%lu/%s", (long)pid
, WITH_PROC_SYMLINK
);
50 n
= readlink (buf
, path
, sizeof (path
)-1);
54 *rc
= gpg_error_from_syserror ();
55 log_write ("%s: %s", __FUNCTION__
, gpg_strerror (*rc
));
73 /* Check for further matches of a pathname which may be negated. */
75 acl_check_proc_dup (char **users
, const char *path
)
80 for (p
= users
; p
&& *p
; p
++)
85 if (*s
== '!' || *s
== '-')
94 if (!strcmp (s
, path
))
102 acl_check_proc (char **users
, char *user
, const char *path
, int *have_path
)
106 if (*p
== '!' || *p
== '-')
113 if (!strcmp (user
, path
))
114 return acl_check_proc_dup (users
, user
);
120 do_validate_peer (assuan_context_t ctx
, const char *section
,
121 assuan_peercred_t
*peer
, char **rpath
)
127 struct client_s
*client
= assuan_get_pointer (ctx
);
130 return GPG_ERR_FORBIDDEN
;
133 if (client
->thd
->remote
)
134 return tls_validate_access (client
, section
);
137 rc
= assuan_get_peercred (ctx
, peer
);
141 users
= config_get_list (section
, "allowed");
146 for (char **p
= users
; !rc
&& *p
; p
++)
151 if (*user
== '-' || *user
== '!')
159 if (strv_printf (&cmds
, "%s%s", not ? "!" : "", user
) == 0)
163 return GPG_ERR_ENOMEM
;
168 for (char **p
= users
; !rc
&& *p
; p
++)
172 if (*user
== '/' || (*user
== '!' && *(user
+1) == '/')
173 || (*user
== '-' && *(user
+1) == '/'))
177 rc
= acl_check_common(client
, user
, (*peer
)->uid
, (*peer
)->gid
,
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;
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)
200 if (rpath
&& exec_allowed
)
208 if (!rc
&& cmds
&& (allowed
|| !have_user
) && same_uid
)
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
);
222 allowed
= allowed
&& exec_allowed
== 1;
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
233 acl_check_group (const char *name
, uid_t uid
, gid_t gid
, int not, int *allowed
)
236 struct group gr
, *gresult
;
237 size_t len
= sysconf (_SC_GETGR_R_SIZE_MAX
);
246 return GPG_ERR_ENOMEM
;
248 err
= getgrnam_r (name
, &gr
, buf
, len
, &gresult
);
251 if (gresult
->gr_gid
== gid
)
258 for (char **t
= gresult
->gr_mem
; !rc
&& *t
; t
++)
262 struct passwd
*result
= get_pwd_struct (*t
, 0, &pw
, &tbuf
, &rc
);
264 if (!rc
&& result
&& result
->pw_uid
== uid
)
278 rc
= gpg_error_from_errno (err
);
281 return rc
? rc
: !gresult
? 0 : GPG_ERR_EACCES
;
285 acl_check_group (const char *name
, uid_t uid
, gid_t gid
, int not, int *allowed
)
287 struct group
*gresult
= NULL
;
291 gresult
= getgrnam (name
);
292 if (!errno
&& gresult
&& gresult
->gr_gid
== gid
)
298 rc
= gpg_error_from_syserror ();
303 for (char **t
= gresult
->gr_mem
; !rc
&& *t
; t
++)
307 struct passwd
*result
= get_pwd_struct (*t
, 0, &pw
, &buf
, &rc
);
309 if (!rc
&& result
&& result
->pw_uid
== uid
)
324 peer_is_invoker(struct client_s
*client
)
326 struct invoking_user_s
*user
;
329 if (client
->thd
->state
== CLIENT_STATE_UNKNOWN
)
330 return GPG_ERR_EACCES
;
332 for (user
= invoking_users
; user
; user
= user
->next
)
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;
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
);
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
;
362 acl_check_common (struct client_s
*client
, const char *user
, uid_t uid
,
363 gid_t gid
, int *allowed
)
374 if (*user
== '-' || *user
== '!')
380 if (*user
== '+') // not implemented yet
383 if (*user
== '#') // TLS fingerprint hash
386 if (*user
== '/') // client command name path
395 if (client
->thd
->remote
)
397 if (!strcasecmp (client
->thd
->tls
->fp
, user
))
408 else if (client
->thd
->remote
) // Remote client with no FP in the ACL
412 if (*user
== '@') // all users in group
413 return acl_check_group (user
+1, uid
, gid
, not, allowed
);
418 struct passwd
*pwd
= get_pwd_struct (user
, 0, &pw
, &buf
, &rc
);
420 if (!rc
&& pwd
&& pwd
->pw_uid
== uid
)
426 if (!rc
&& cmd
&& client
->thd
->cmdname
)
428 if (!strcmp (user
, client
->thd
->cmdname
))
438 validate_peer (struct client_s
*cl
)
445 return tls_validate_access (cl
, NULL
);
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
);
458 cl
->thd
->cmdname
= path
;