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 as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 Pwmd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
31 #include "util-misc.h"
32 #include "util-string.h"
40 acl_get_proc (pid_t pid
, gpg_error_t
*rc
)
42 #ifndef __linux__ // FIXME: portability (BSD kvm, solaris, etc)
51 snprintf (buf
, sizeof (buf
), "/proc/%lu/exe", (long)pid
);
52 n
= readlink (buf
, path
, sizeof (path
)-1);
56 *rc
= gpg_error_from_syserror ();
57 log_write ("%s: %s", __FUNCTION__
, gpg_strerror (*rc
));
72 /* Check for further matches of a pathname which may be negated. */
74 acl_check_proc_dup (char **users
, const char *path
)
79 for (p
= users
; p
&& *p
; p
++)
84 if (*s
== '!' || *s
== '-')
93 if (!strcmp (s
, path
))
101 acl_check_proc (char **users
, char *user
, const char *path
, int *have_path
)
105 if (*p
== '!' || *p
== '-')
112 if (!strcmp (user
, path
))
113 return acl_check_proc_dup (users
, user
);
119 do_validate_peer (assuan_context_t ctx
, const char *section
,
120 assuan_peercred_t
*peer
, char **rpath
)
125 struct client_s
*client
= assuan_get_pointer (ctx
);
128 return GPG_ERR_FORBIDDEN
;
131 if (client
->thd
->remote
)
132 return tls_validate_access (client
, section
);
135 rc
= assuan_get_peercred (ctx
, peer
);
139 users
= config_get_list (section
, "allowed");
142 for (char **p
= users
; !rc
&& *p
; p
++)
144 rc
= acl_check_common(client
, *p
, (*peer
)->uid
, (*peer
)->gid
,
148 /* Skip getting process name from a UID that is not our own to prevent
149 * an ENOENT error since most modern systems hide process details from
150 * other UID's. Access will be allowed based on other tests (filesystem
151 * ACL to the socket, configuration, etc). */
152 if (allowed
&& !rc
&& (*peer
)->uid
== getuid ())
154 int exec_allowed
= 0;
155 char *path
= acl_get_proc ((*peer
)->pid
, &rc
);
158 for (char **p
= users
; !rc
&& path
&& *p
; p
++)
160 exec_allowed
= acl_check_proc (users
, *p
, path
, &have_path
);
161 if (!rc
&& exec_allowed
== 1)
171 allowed
= exec_allowed
== 1;
177 return allowed
&& !rc
? 0 : rc
? rc
: GPG_ERR_FORBIDDEN
;
180 /* Test if uid is a member of group 'name'. Invert when 'not' is true. */
181 #ifdef HAVE_GETGRNAM_R
183 acl_check_group (const char *name
, uid_t uid
, gid_t gid
, int not, int *allowed
)
186 struct group gr
, *gresult
;
187 size_t len
= sysconf (_SC_GETGR_R_SIZE_MAX
);
196 return GPG_ERR_ENOMEM
;
198 err
= getgrnam_r (name
, &gr
, buf
, len
, &gresult
);
201 if (gresult
->gr_gid
== gid
)
208 for (char **t
= gresult
->gr_mem
; !rc
&& *t
; t
++)
212 struct passwd
*result
= get_pwd_struct (*t
, 0, &pw
, &tbuf
, &rc
);
214 if (!rc
&& result
&& result
->pw_uid
== uid
)
228 rc
= gpg_error_from_errno (err
);
231 return rc
? rc
: !gresult
? 0 : GPG_ERR_EACCES
;
235 acl_check_group (const char *name
, uid_t uid
, gid_t gid
, int not, int *allowed
)
237 struct group
*gresult
;
241 gresult
= getgrnam (name
);
242 if (!errno
&& gresult
&& gresult
->gr_gid
== gid
)
248 rc
= gpg_error_from_syserror ();
252 for (char **t
= gresult
->gr_mem
; !rc
&& *t
; t
++)
256 struct passwd
*result
= get_pwd_struct (*t
, 0, &pw
, &buf
, &rc
);
258 if (!rc
&& result
&& result
->pw_uid
== uid
)
273 peer_is_invoker(struct client_s
*client
)
275 struct invoking_user_s
*user
;
278 if (client
->thd
->state
== CLIENT_STATE_UNKNOWN
)
279 return GPG_ERR_EACCES
;
281 for (user
= invoking_users
; user
; user
= user
->next
)
284 if (client
->thd
->remote
)
286 if (user
->type
== INVOKING_TLS
287 && !strcmp(client
->thd
->tls
->fp
, user
->id
))
288 allowed
= user
->not ? 0 : 1;
294 if (user
->type
== INVOKING_GID
)
296 gpg_error_t rc
= acl_check_group (user
->id
,
297 client
->thd
->peer
->uid
,
298 client
->thd
->peer
->gid
,
299 user
->not, &allowed
);
303 else if (user
->type
== INVOKING_UID
&& client
->thd
->peer
->uid
== user
->uid
)
304 allowed
= user
->not ? 0 : 1;
307 return allowed
? 0 : GPG_ERR_EACCES
;
310 #ifdef HAVE_GETGRNAM_R
312 acl_check_common (struct client_s
*client
, const char *user
, uid_t uid
,
313 gid_t gid
, int *allowed
)
323 if (*user
== '-' || *user
== '!')
326 if (*user
== '+') // not implemented yet
329 if (*user
== '#') // TLS fingerprint hash
332 if (not || rw
|| tls
)
338 if (client
->thd
->remote
)
340 if (!strcasecmp (client
->thd
->tls
->fp
, user
))
351 else if (client
->thd
->remote
) // Remote client with no FP in the ACL
355 if (*user
== '@') // all users in group
356 return acl_check_group (user
+1, uid
, gid
, not, allowed
);
361 struct passwd
*pwd
= get_pwd_struct (user
, 0, &pw
, &buf
, &rc
);
363 if (!rc
&& pwd
&& pwd
->pw_uid
== uid
)
373 acl_check_common (struct client_s
*client
, const char *user
, uid_t uid
,
374 gid_t gid
, int *allowed
)
384 if (*user
== '-' || *user
== '!')
387 if (*user
== '+') // not implemented yet
390 if (*user
== '#') // TLS fingerprint hash
393 if (not || rw
|| tls
)
399 if (client
->thd
->remote
)
401 if (!strcasecmp (client
->thd
->tls
->fp
, user
))
411 if (*user
== '@') // all users in group
412 return acl_check_group (user
+1, uid
, gid
, not, allowed
);
417 struct passwd
*result
= get_pwd_struct (user
, 0, &pw
, &buf
, &rc
);
419 if (!rc
&& result
&& result
->pw_uid
== uid
)
430 validate_peer (struct client_s
*cl
)
437 return tls_validate_access (cl
, NULL
);
440 MUTEX_LOCK (&cn_mutex
);
441 pthread_cleanup_push (release_mutex_cb
, &cn_mutex
);
442 rc
= do_validate_peer (cl
->ctx
, "global", &cl
->thd
->peer
, &path
);
443 pthread_cleanup_pop (1);
444 log_write ("peer %s: path=%s, uid=%i, gid=%i, pid=%i, rc=%u",
445 !rc
? _("accepted") : _("rejected"), path
,
446 cl
->thd
->peer
->uid
, cl
->thd
->peer
->gid
,
447 cl
->thd
->peer
->pid
, rc
);