2 Copyright (C) 2006-2021 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 #ifndef __linux__ // FIXME: portability (BSD kvm, solaris, etc)
52 snprintf (buf
, sizeof (buf
), "/proc/%lu/exe", (long)pid
);
53 n
= readlink (buf
, path
, sizeof (path
)-1);
57 *rc
= gpg_error_from_syserror ();
58 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
)
126 struct client_s
*client
= assuan_get_pointer (ctx
);
129 return GPG_ERR_FORBIDDEN
;
132 if (client
->thd
->remote
)
133 return tls_validate_access (client
, section
);
136 rc
= assuan_get_peercred (ctx
, peer
);
140 users
= config_get_list (section
, "allowed");
143 for (char **p
= users
; !rc
&& *p
; p
++)
145 rc
= acl_check_common(client
, *p
, (*peer
)->uid
, (*peer
)->gid
,
149 /* Skip getting process name from a UID that is not our own to prevent
150 * an ENOENT error since most modern systems hide process details from
151 * other UID's. Access will be allowed based on other tests (filesystem
152 * ACL to the socket, configuration, etc). */
153 if (allowed
&& !rc
&& (*peer
)->uid
== getuid ())
155 int exec_allowed
= 0;
156 char *path
= acl_get_proc ((*peer
)->pid
, &rc
);
159 for (char **p
= users
; !rc
&& path
&& *p
; p
++)
161 exec_allowed
= acl_check_proc (users
, *p
, path
, &have_path
);
162 if (!rc
&& exec_allowed
== 1)
172 allowed
= exec_allowed
== 1;
178 return allowed
&& !rc
? 0 : rc
? rc
: GPG_ERR_FORBIDDEN
;
181 /* Test if uid is a member of group 'name'. Invert when 'not' is true. */
182 #ifdef HAVE_GETGRNAM_R
184 acl_check_group (const char *name
, uid_t uid
, gid_t gid
, int not, int *allowed
)
187 struct group gr
, *gresult
;
188 size_t len
= sysconf (_SC_GETGR_R_SIZE_MAX
);
197 return GPG_ERR_ENOMEM
;
199 err
= getgrnam_r (name
, &gr
, buf
, len
, &gresult
);
202 if (gresult
->gr_gid
== gid
)
209 for (char **t
= gresult
->gr_mem
; !rc
&& *t
; t
++)
213 struct passwd
*result
= get_pwd_struct (*t
, 0, &pw
, &tbuf
, &rc
);
215 if (!rc
&& result
&& result
->pw_uid
== uid
)
229 rc
= gpg_error_from_errno (err
);
232 return rc
? rc
: !gresult
? 0 : GPG_ERR_EACCES
;
236 acl_check_group (const char *name
, uid_t uid
, gid_t gid
, int not, int *allowed
)
238 struct group
*gresult
;
242 gresult
= getgrnam (name
);
243 if (!errno
&& gresult
&& gresult
->gr_gid
== gid
)
249 rc
= gpg_error_from_syserror ();
253 for (char **t
= gresult
->gr_mem
; !rc
&& *t
; t
++)
257 struct passwd
*result
= get_pwd_struct (*t
, 0, &pw
, &buf
, &rc
);
259 if (!rc
&& result
&& result
->pw_uid
== uid
)
274 peer_is_invoker(struct client_s
*client
)
276 struct invoking_user_s
*user
;
279 if (client
->thd
->state
== CLIENT_STATE_UNKNOWN
)
280 return GPG_ERR_EACCES
;
282 for (user
= invoking_users
; user
; user
= user
->next
)
285 if (client
->thd
->remote
)
287 if (user
->type
== INVOKING_TLS
288 && !strcmp(client
->thd
->tls
->fp
, user
->id
))
289 allowed
= user
->not ? 0 : 1;
295 if (user
->type
== INVOKING_GID
)
297 gpg_error_t rc
= acl_check_group (user
->id
,
298 client
->thd
->peer
->uid
,
299 client
->thd
->peer
->gid
,
300 user
->not, &allowed
);
304 else if (user
->type
== INVOKING_UID
&& client
->thd
->peer
->uid
== user
->uid
)
305 allowed
= user
->not ? 0 : 1;
308 return allowed
? 0 : GPG_ERR_EACCES
;
311 #ifdef HAVE_GETGRNAM_R
313 acl_check_common (struct client_s
*client
, const char *user
, uid_t uid
,
314 gid_t gid
, int *allowed
)
324 if (*user
== '-' || *user
== '!')
327 if (*user
== '+') // not implemented yet
330 if (*user
== '#') // TLS fingerprint hash
333 if (not || rw
|| tls
)
339 if (client
->thd
->remote
)
341 if (!strcasecmp (client
->thd
->tls
->fp
, user
))
352 else if (client
->thd
->remote
) // Remote client with no FP in the ACL
356 if (*user
== '@') // all users in group
357 return acl_check_group (user
+1, uid
, gid
, not, allowed
);
362 struct passwd
*pwd
= get_pwd_struct (user
, 0, &pw
, &buf
, &rc
);
364 if (!rc
&& pwd
&& pwd
->pw_uid
== uid
)
374 acl_check_common (struct client_s
*client
, const char *user
, uid_t uid
,
375 gid_t gid
, int *allowed
)
385 if (*user
== '-' || *user
== '!')
388 if (*user
== '+') // not implemented yet
391 if (*user
== '#') // TLS fingerprint hash
394 if (not || rw
|| tls
)
400 if (client
->thd
->remote
)
402 if (!strcasecmp (client
->thd
->tls
->fp
, user
))
412 if (*user
== '@') // all users in group
413 return acl_check_group (user
+1, uid
, gid
, not, allowed
);
418 struct passwd
*result
= get_pwd_struct (user
, 0, &pw
, &buf
, &rc
);
420 if (!rc
&& result
&& result
->pw_uid
== uid
)
431 validate_peer (struct client_s
*cl
)
438 return tls_validate_access (cl
, NULL
);
441 MUTEX_LOCK (&cn_mutex
);
442 pthread_cleanup_push (release_mutex_cb
, &cn_mutex
);
443 rc
= do_validate_peer (cl
->ctx
, "global", &cl
->thd
->peer
, &path
);
444 pthread_cleanup_pop (1);
445 log_write ("peer %s: path=%s, uid=%i, gid=%i, pid=%i, rc=%u",
446 !rc
? _("accepted") : _("rejected"), path
,
447 cl
->thd
->peer
->uid
, cl
->thd
->peer
->gid
,
448 cl
->thd
->peer
->pid
, rc
);