1993-11-27 23:42:49 +00:00
|
|
|
/*
|
2019-04-29 07:21:51 -06:00
|
|
|
* SPDX-License-Identifier: ISC
|
|
|
|
*
|
2024-05-01 08:04:00 -06:00
|
|
|
* Copyright (c) 2004-2005, 2007-2024 Todd C. Miller <Todd.Miller@sudo.ws>
|
1993-06-11 22:03:45 +00:00
|
|
|
*
|
2004-02-13 21:36:43 +00:00
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
1993-10-04 19:10:33 +00:00
|
|
|
*/
|
|
|
|
|
2018-10-26 08:39:09 -06:00
|
|
|
/*
|
|
|
|
* This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
|
|
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
|
|
|
*/
|
2018-10-21 08:46:05 -06:00
|
|
|
|
2004-11-19 18:39:14 +00:00
|
|
|
#include <config.h>
|
1994-03-12 18:36:53 +00:00
|
|
|
|
1993-06-11 22:03:45 +00:00
|
|
|
#include <stdio.h>
|
2015-06-19 14:29:27 -06:00
|
|
|
#include <stdlib.h>
|
2020-05-18 07:59:24 -06:00
|
|
|
#include <string.h>
|
2015-07-02 09:08:28 -06:00
|
|
|
#include <unistd.h>
|
2017-02-18 15:35:48 -07:00
|
|
|
#include <pwd.h>
|
1993-06-11 22:03:45 +00:00
|
|
|
|
2023-09-25 10:13:28 -06:00
|
|
|
#include <sudoers.h>
|
2011-11-12 12:18:44 -05:00
|
|
|
#include <gram.h>
|
1999-08-28 10:00:22 +00:00
|
|
|
|
2022-12-07 10:25:00 -07:00
|
|
|
static int
|
|
|
|
runas_matches_pw(struct sudoers_parse_tree *parse_tree,
|
|
|
|
const struct cmndspec *cs, const struct passwd *pw)
|
|
|
|
{
|
|
|
|
debug_decl(runas_matches_pw, SUDOERS_DEBUG_PARSER);
|
|
|
|
|
|
|
|
if (cs->runasuserlist != NULL)
|
|
|
|
debug_return_int(userlist_matches(parse_tree, pw, cs->runasuserlist));
|
|
|
|
|
|
|
|
if (cs->runasgrouplist == NULL) {
|
|
|
|
/* No explicit runas user or group, use default. */
|
2023-09-09 14:07:06 -06:00
|
|
|
if (userpw_matches(def_runas_default, pw->pw_name, pw) == ALLOW)
|
2022-12-07 10:25:00 -07:00
|
|
|
debug_return_int(ALLOW);
|
|
|
|
}
|
|
|
|
debug_return_int(UNSPEC);
|
|
|
|
}
|
|
|
|
|
2014-09-15 15:11:30 -06:00
|
|
|
/*
|
2019-05-22 08:58:51 -06:00
|
|
|
* Look up the user in the sudoers parse tree for pseudo-commands like
|
2018-05-14 09:05:03 -06:00
|
|
|
* list, verify and kill.
|
2014-09-15 15:11:30 -06:00
|
|
|
*/
|
2023-07-10 11:06:23 -06:00
|
|
|
static unsigned int
|
2023-08-21 09:21:49 -06:00
|
|
|
sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct sudoers_context *ctx,
|
|
|
|
time_t now, sudoers_lookup_callback_fn_t callback, void *cb_data,
|
|
|
|
int pwflag)
|
1993-06-11 22:03:45 +00:00
|
|
|
{
|
2022-12-11 13:46:00 -07:00
|
|
|
char *saved_runchroot;
|
2022-02-14 13:09:55 -07:00
|
|
|
struct passwd *root_pw = NULL;
|
2018-05-14 09:05:03 -06:00
|
|
|
struct sudo_nss *nss;
|
2004-10-26 22:10:55 +00:00
|
|
|
struct cmndspec *cs;
|
|
|
|
struct privilege *priv;
|
|
|
|
struct userspec *us;
|
2018-05-14 09:05:03 -06:00
|
|
|
struct defaults *def;
|
2023-12-15 10:45:22 -07:00
|
|
|
int nopass, match = UNSPEC;
|
2023-07-10 11:06:23 -06:00
|
|
|
unsigned int validated = 0;
|
2018-05-14 09:05:03 -06:00
|
|
|
enum def_tuple pwcheck;
|
2019-12-22 08:48:16 -07:00
|
|
|
debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER);
|
2018-05-14 09:05:03 -06:00
|
|
|
|
|
|
|
pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
|
2019-10-15 07:23:51 -06:00
|
|
|
nopass = (pwcheck == never || pwcheck == all) ? true : false;
|
2018-05-14 09:05:03 -06:00
|
|
|
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->runas.list_pw != NULL) {
|
2022-02-14 13:09:55 -07:00
|
|
|
root_pw = sudo_getpwuid(ROOT_UID);
|
2022-02-15 19:41:31 -07:00
|
|
|
if (root_pw == NULL)
|
2023-06-29 17:30:39 -06:00
|
|
|
sudo_warnx(U_("unknown uid %u"), ROOT_UID);
|
2022-02-14 13:09:55 -07:00
|
|
|
} else {
|
|
|
|
SET(validated, FLAG_NO_CHECK);
|
|
|
|
}
|
2022-12-11 13:46:00 -07:00
|
|
|
|
|
|
|
/* Don't use chroot setting for pseudo-commands. */
|
|
|
|
saved_runchroot = def_runchroot;
|
|
|
|
def_runchroot = NULL;
|
|
|
|
|
2018-05-14 09:05:03 -06:00
|
|
|
TAILQ_FOREACH(nss, snl, entries) {
|
2023-08-21 09:21:49 -06:00
|
|
|
if (nss->query(ctx, nss, ctx->user.pw) == -1) {
|
2018-05-14 09:05:03 -06:00
|
|
|
/* The query function should have printed an error message. */
|
|
|
|
SET(validated, VALIDATE_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
2023-07-25 15:57:20 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to traverse the policy forwards, not in reverse,
|
|
|
|
* to support the "pwcheck == all" case.
|
|
|
|
*/
|
2018-07-26 15:12:33 -06:00
|
|
|
TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) {
|
2023-09-09 14:07:06 -06:00
|
|
|
const int user_match = userlist_matches(nss->parse_tree,
|
|
|
|
ctx->user.pw, &us->users);
|
2023-08-07 15:06:19 -06:00
|
|
|
if (user_match != ALLOW) {
|
Try to make sudo less vulnerable to ROWHAMMER attacks.
We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS,
AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we
explicitly test for expected values instead of using a negated test
against an error value. In the parser match functions this means
explicitly checking for ALLOW or DENY instead of accepting anything
that is not set to UNSPEC.
Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk
Sunar, all affiliated with the Vernam Applied Cryptography and
Cybersecurity Lab at Worcester Polytechnic Institute, for the report.
Paper preprint: https://arxiv.org/abs/2309.02545
2023-09-09 14:07:04 -06:00
|
|
|
if (callback != NULL && user_match == DENY) {
|
2023-08-09 10:16:10 -06:00
|
|
|
callback(nss->parse_tree, us, user_match, NULL, UNSPEC,
|
|
|
|
NULL, UNSPEC, UNSPEC, UNSPEC, cb_data);
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2007-07-06 00:20:51 +00:00
|
|
|
continue;
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2013-10-22 09:08:38 -06:00
|
|
|
TAILQ_FOREACH(priv, &us->privileges, entries) {
|
2018-05-14 09:05:03 -06:00
|
|
|
int priv_nopass = UNSPEC;
|
2023-09-09 14:07:06 -06:00
|
|
|
const int host_match = hostlist_matches(nss->parse_tree,
|
|
|
|
ctx->user.pw, &priv->hostlist);
|
2023-08-07 15:06:19 -06:00
|
|
|
if (host_match != ALLOW) {
|
|
|
|
if (callback != NULL) {
|
2023-08-09 10:16:10 -06:00
|
|
|
callback(nss->parse_tree, us, user_match, priv,
|
|
|
|
host_match, NULL, UNSPEC, UNSPEC, UNSPEC, cb_data);
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2007-07-06 00:20:51 +00:00
|
|
|
continue;
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
TAILQ_FOREACH(def, &priv->defaults, entries) {
|
2023-07-25 15:57:20 -06:00
|
|
|
if (strcmp(def->var, "authenticate") == 0) {
|
2018-05-16 12:14:14 -06:00
|
|
|
priv_nopass = !def->op;
|
2023-07-25 15:57:20 -06:00
|
|
|
break;
|
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
}
|
2013-10-22 09:08:38 -06:00
|
|
|
TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
|
2023-07-25 15:57:20 -06:00
|
|
|
int cmnd_match = UNSPEC;
|
|
|
|
int date_match = UNSPEC;
|
|
|
|
int runas_match = UNSPEC;
|
|
|
|
|
2018-05-14 09:05:03 -06:00
|
|
|
if (pwcheck == any) {
|
|
|
|
if (cs->tags.nopasswd == true || priv_nopass == true)
|
|
|
|
nopass = true;
|
|
|
|
} else if (pwcheck == all) {
|
|
|
|
if (cs->tags.nopasswd != true && priv_nopass != true)
|
|
|
|
nopass = false;
|
|
|
|
}
|
2022-02-14 13:09:55 -07:00
|
|
|
|
2023-07-25 15:57:20 -06:00
|
|
|
if (cs->notbefore != UNSPEC) {
|
|
|
|
date_match = now < cs->notbefore ? DENY : ALLOW;
|
|
|
|
}
|
|
|
|
if (cs->notafter != UNSPEC) {
|
|
|
|
date_match = now > cs->notafter ? DENY : ALLOW;
|
|
|
|
}
|
2022-12-11 13:46:00 -07:00
|
|
|
/*
|
|
|
|
* Root can list any user's privileges.
|
|
|
|
* A user may always list their own privileges.
|
|
|
|
*/
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->user.uid == 0 || ctx->runas.list_pw == NULL ||
|
|
|
|
ctx->user.uid == ctx->runas.list_pw->pw_uid) {
|
2023-07-25 15:57:20 -06:00
|
|
|
cmnd_match = ALLOW;
|
|
|
|
runas_match = ALLOW;
|
|
|
|
} else if (date_match != DENY) {
|
2023-03-03 13:57:27 -07:00
|
|
|
/*
|
2023-11-28 01:55:57 -05:00
|
|
|
* To list another user's privileges, the runas
|
2023-07-25 15:57:20 -06:00
|
|
|
* user must match the list user or root.
|
2023-03-03 13:57:27 -07:00
|
|
|
*/
|
2023-07-25 15:57:20 -06:00
|
|
|
runas_match = runas_matches_pw(nss->parse_tree, cs,
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.list_pw);
|
2023-07-25 15:57:20 -06:00
|
|
|
switch (runas_match) {
|
|
|
|
case DENY:
|
|
|
|
break;
|
|
|
|
case ALLOW:
|
|
|
|
/*
|
|
|
|
* RunAs user matches list user.
|
|
|
|
* Match on command "list" or ALL.
|
|
|
|
*/
|
|
|
|
cmnd_match = cmnd_matches(nss->parse_tree,
|
2023-03-03 13:57:27 -07:00
|
|
|
cs->cmnd, cs->runchroot, NULL);
|
2023-07-25 15:57:20 -06:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* RunAs user doesn't match list user.
|
|
|
|
* Only allow listing if the user has
|
|
|
|
* "sudo ALL" for root.
|
|
|
|
*/
|
|
|
|
if (root_pw != NULL &&
|
|
|
|
runas_matches_pw(nss->parse_tree, cs,
|
|
|
|
root_pw) == ALLOW) {
|
|
|
|
runas_match = ALLOW;
|
|
|
|
cmnd_match = cmnd_matches_all(nss->parse_tree,
|
|
|
|
cs->cmnd, cs->runchroot, NULL);
|
2023-03-03 13:57:27 -07:00
|
|
|
}
|
2023-07-25 15:57:20 -06:00
|
|
|
break;
|
2022-12-11 13:46:00 -07:00
|
|
|
}
|
2023-07-25 15:57:20 -06:00
|
|
|
}
|
2023-08-07 15:06:19 -06:00
|
|
|
if (callback != NULL) {
|
2023-08-09 10:16:10 -06:00
|
|
|
callback(nss->parse_tree, us, user_match, priv,
|
|
|
|
host_match, cs, date_match, runas_match,
|
|
|
|
cmnd_match, cb_data);
|
2023-07-25 15:57:20 -06:00
|
|
|
}
|
Try to make sudo less vulnerable to ROWHAMMER attacks.
We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS,
AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we
explicitly test for expected values instead of using a negated test
against an error value. In the parser match functions this means
explicitly checking for ALLOW or DENY instead of accepting anything
that is not set to UNSPEC.
Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk
Sunar, all affiliated with the Vernam Applied Cryptography and
Cybersecurity Lab at Worcester Polytechnic Institute, for the report.
Paper preprint: https://arxiv.org/abs/2309.02545
2023-09-09 14:07:04 -06:00
|
|
|
if (SPECIFIED(cmnd_match)) {
|
2023-07-25 15:57:20 -06:00
|
|
|
/*
|
|
|
|
* We take the last match but must process
|
|
|
|
* the entire policy for pwcheck == all.
|
|
|
|
*/
|
|
|
|
match = cmnd_match;
|
2022-02-15 19:41:31 -07:00
|
|
|
}
|
1996-07-26 17:23:40 +00:00
|
|
|
}
|
|
|
|
}
|
1995-03-28 20:26:55 +00:00
|
|
|
}
|
2023-07-25 15:57:20 -06:00
|
|
|
if (!sudo_nss_can_continue(nss, match))
|
|
|
|
break;
|
2004-10-26 22:10:55 +00:00
|
|
|
}
|
2022-02-14 13:09:55 -07:00
|
|
|
if (root_pw != NULL)
|
|
|
|
sudo_pw_delref(root_pw);
|
2023-08-21 09:21:49 -06:00
|
|
|
if (match == ALLOW || ctx->user.uid == 0) {
|
2018-05-14 09:05:03 -06:00
|
|
|
/* User has an entry for this host. */
|
|
|
|
SET(validated, VALIDATE_SUCCESS);
|
2023-12-15 10:45:22 -07:00
|
|
|
} else {
|
|
|
|
/* No entry or user is not allowed to list other users. */
|
2018-05-14 09:05:03 -06:00
|
|
|
SET(validated, VALIDATE_FAILURE);
|
2023-12-15 10:45:22 -07:00
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
if (pwcheck == always && def_authenticate)
|
|
|
|
SET(validated, FLAG_CHECK_USER);
|
|
|
|
else if (nopass == true)
|
|
|
|
def_authenticate = false;
|
2022-12-11 13:46:00 -07:00
|
|
|
|
|
|
|
/* Restore original def_runchroot. */
|
|
|
|
def_runchroot = saved_runchroot;
|
|
|
|
|
2023-07-10 11:06:23 -06:00
|
|
|
debug_return_uint(validated);
|
2018-05-14 09:05:03 -06:00
|
|
|
}
|
1995-08-14 04:06:27 +00:00
|
|
|
|
2018-05-14 09:05:03 -06:00
|
|
|
static int
|
2023-08-21 09:21:49 -06:00
|
|
|
sudoers_lookup_check(struct sudo_nss *nss, struct sudoers_context *ctx,
|
2023-07-10 11:06:23 -06:00
|
|
|
unsigned int *validated, struct cmnd_info *info, time_t now,
|
2023-08-07 15:06:19 -06:00
|
|
|
sudoers_lookup_callback_fn_t callback, void *cb_data,
|
|
|
|
struct cmndspec **matching_cs, struct defaults_list **defs)
|
2018-05-14 09:05:03 -06:00
|
|
|
{
|
|
|
|
struct cmndspec *cs;
|
|
|
|
struct privilege *priv;
|
|
|
|
struct userspec *us;
|
|
|
|
struct member *matching_user;
|
2019-12-22 08:48:16 -07:00
|
|
|
debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER);
|
1998-10-15 03:57:14 +00:00
|
|
|
|
2023-11-02 13:44:17 -06:00
|
|
|
memset(info, 0, sizeof(*info));
|
2020-09-09 15:26:45 -06:00
|
|
|
|
2018-07-26 15:12:33 -06:00
|
|
|
TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) {
|
2023-09-09 14:07:06 -06:00
|
|
|
const int user_match = userlist_matches(nss->parse_tree, ctx->user.pw,
|
|
|
|
&us->users);
|
2023-08-07 15:06:19 -06:00
|
|
|
if (user_match != ALLOW) {
|
Try to make sudo less vulnerable to ROWHAMMER attacks.
We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS,
AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we
explicitly test for expected values instead of using a negated test
against an error value. In the parser match functions this means
explicitly checking for ALLOW or DENY instead of accepting anything
that is not set to UNSPEC.
Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk
Sunar, all affiliated with the Vernam Applied Cryptography and
Cybersecurity Lab at Worcester Polytechnic Institute, for the report.
Paper preprint: https://arxiv.org/abs/2309.02545
2023-09-09 14:07:04 -06:00
|
|
|
if (callback != NULL && user_match == DENY) {
|
2023-08-09 10:16:10 -06:00
|
|
|
callback(nss->parse_tree, us, user_match, NULL, UNSPEC, NULL,
|
|
|
|
UNSPEC, UNSPEC, UNSPEC, cb_data);
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2007-07-06 00:20:51 +00:00
|
|
|
continue;
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
CLR(*validated, FLAG_NO_USER);
|
2013-10-22 09:08:38 -06:00
|
|
|
TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
|
2023-09-09 14:07:06 -06:00
|
|
|
const int host_match = hostlist_matches(nss->parse_tree,
|
|
|
|
ctx->user.pw, &priv->hostlist);
|
2023-08-07 15:06:19 -06:00
|
|
|
if (host_match == ALLOW) {
|
2018-05-14 09:05:03 -06:00
|
|
|
CLR(*validated, FLAG_NO_HOST);
|
2023-08-07 15:06:19 -06:00
|
|
|
} else {
|
|
|
|
if (callback != NULL) {
|
2023-08-09 10:16:10 -06:00
|
|
|
callback(nss->parse_tree, us, user_match, priv, host_match,
|
|
|
|
NULL, UNSPEC, UNSPEC, UNSPEC, cb_data);
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2007-08-31 01:21:26 +00:00
|
|
|
continue;
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
2013-10-22 09:08:38 -06:00
|
|
|
TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
|
2023-06-29 17:30:39 -06:00
|
|
|
int cmnd_match = UNSPEC;
|
|
|
|
int date_match = UNSPEC;
|
|
|
|
int runas_match = UNSPEC;
|
|
|
|
|
2017-02-18 15:35:48 -07:00
|
|
|
if (cs->notbefore != UNSPEC) {
|
2023-06-29 17:30:39 -06:00
|
|
|
date_match = now < cs->notbefore ? DENY : ALLOW;
|
2017-02-18 15:35:48 -07:00
|
|
|
}
|
|
|
|
if (cs->notafter != UNSPEC) {
|
2023-06-29 17:30:39 -06:00
|
|
|
date_match = now > cs->notafter ? DENY : ALLOW;
|
2017-02-18 15:35:48 -07:00
|
|
|
}
|
2023-06-29 17:30:39 -06:00
|
|
|
if (date_match != DENY) {
|
|
|
|
matching_user = NULL;
|
|
|
|
runas_match = runaslist_matches(nss->parse_tree,
|
|
|
|
cs->runasuserlist, cs->runasgrouplist, &matching_user,
|
|
|
|
NULL);
|
|
|
|
if (runas_match == ALLOW) {
|
|
|
|
cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd,
|
|
|
|
cs->runchroot, info);
|
|
|
|
}
|
|
|
|
}
|
2023-08-07 15:06:19 -06:00
|
|
|
if (callback != NULL) {
|
2023-08-09 10:16:10 -06:00
|
|
|
callback(nss->parse_tree, us, user_match, priv, host_match,
|
|
|
|
cs, date_match, runas_match, cmnd_match, cb_data);
|
2023-08-07 15:06:19 -06:00
|
|
|
}
|
|
|
|
|
Try to make sudo less vulnerable to ROWHAMMER attacks.
We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS,
AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we
explicitly test for expected values instead of using a negated test
against an error value. In the parser match functions this means
explicitly checking for ALLOW or DENY instead of accepting anything
that is not set to UNSPEC.
Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk
Sunar, all affiliated with the Vernam Applied Cryptography and
Cybersecurity Lab at Worcester Polytechnic Institute, for the report.
Paper preprint: https://arxiv.org/abs/2309.02545
2023-09-09 14:07:04 -06:00
|
|
|
if (SPECIFIED(cmnd_match)) {
|
2023-06-29 17:30:39 -06:00
|
|
|
/*
|
|
|
|
* If user is running command as themselves,
|
2023-08-21 09:21:49 -06:00
|
|
|
* set ctx->runas.pw = ctx->user.pw.
|
2023-06-29 17:30:39 -06:00
|
|
|
* XXX - hack, want more general solution
|
|
|
|
*/
|
|
|
|
if (matching_user && matching_user->type == MYSELF) {
|
2023-08-21 09:21:49 -06:00
|
|
|
sudo_pw_delref(ctx->runas.pw);
|
|
|
|
sudo_pw_addref(ctx->user.pw);
|
|
|
|
ctx->runas.pw = ctx->user.pw;
|
2007-08-31 01:21:26 +00:00
|
|
|
}
|
2023-06-29 17:30:39 -06:00
|
|
|
*matching_cs = cs;
|
|
|
|
*defs = &priv->defaults;
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
|
|
|
|
"userspec matched @ %s:%d:%d: %s",
|
|
|
|
us->file ? us->file : "???", us->line, us->column,
|
|
|
|
cmnd_match ? "allowed" : "denied");
|
|
|
|
debug_return_int(cmnd_match);
|
2004-10-26 22:10:55 +00:00
|
|
|
}
|
2023-06-29 17:30:39 -06:00
|
|
|
free(info->cmnd_path);
|
2023-11-02 13:44:17 -06:00
|
|
|
memset(info, 0, sizeof(*info));
|
1999-04-10 04:10:01 +00:00
|
|
|
}
|
1996-05-27 23:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
debug_return_int(UNSPEC);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-11-21 14:50:22 +08:00
|
|
|
* Apply cmndspec-specific settings including SELinux role/type,
|
2024-05-03 08:15:19 -06:00
|
|
|
* AppArmor profile, Solaris privs, and command tags.
|
2018-05-14 09:05:03 -06:00
|
|
|
*/
|
|
|
|
static bool
|
2023-08-21 09:21:49 -06:00
|
|
|
apply_cmndspec(struct sudoers_context *ctx, struct cmndspec *cs)
|
2018-05-14 09:05:03 -06:00
|
|
|
{
|
2019-12-22 08:48:16 -07:00
|
|
|
debug_decl(apply_cmndspec, SUDOERS_DEBUG_PARSER);
|
2018-05-14 09:05:03 -06:00
|
|
|
|
|
|
|
if (cs != NULL) {
|
|
|
|
/* Set role and type if not specified on command line. */
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->runas.role == NULL) {
|
2018-05-14 09:05:03 -06:00
|
|
|
if (cs->role != NULL) {
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.role = strdup(cs->role);
|
|
|
|
if (ctx->runas.role == NULL) {
|
2018-05-14 09:05:03 -06:00
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
2015-02-19 10:02:20 -07:00
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
} else {
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.role = def_role;
|
2021-02-14 07:47:48 -07:00
|
|
|
def_role = NULL;
|
2015-02-19 10:02:20 -07:00
|
|
|
}
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->runas.role != NULL) {
|
2021-02-18 06:09:08 -07:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
2023-08-21 09:21:49 -06:00
|
|
|
"ctx->runas.role -> %s", ctx->runas.role);
|
2021-02-18 06:09:08 -07:00
|
|
|
}
|
2001-12-13 01:07:25 +00:00
|
|
|
}
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->runas.type == NULL) {
|
2018-05-14 09:05:03 -06:00
|
|
|
if (cs->type != NULL) {
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.type = strdup(cs->type);
|
|
|
|
if (ctx->runas.type == NULL) {
|
2018-05-14 09:05:03 -06:00
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
2015-02-19 10:02:20 -07:00
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
} else {
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.type = def_type;
|
2021-02-14 07:47:48 -07:00
|
|
|
def_type = NULL;
|
2015-02-19 10:02:20 -07:00
|
|
|
}
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->runas.type != NULL) {
|
2021-02-18 06:09:08 -07:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
2023-08-21 09:21:49 -06:00
|
|
|
"ctx->runas.type -> %s", ctx->runas.type);
|
2021-02-18 06:09:08 -07:00
|
|
|
}
|
2015-02-19 10:02:20 -07:00
|
|
|
}
|
2022-05-23 13:16:10 -06:00
|
|
|
/* Set AppArmor profile, if specified */
|
|
|
|
if (cs->apparmor_profile != NULL) {
|
2024-05-03 08:15:19 -06:00
|
|
|
free(ctx->runas.apparmor_profile);
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.apparmor_profile = strdup(cs->apparmor_profile);
|
|
|
|
if (ctx->runas.apparmor_profile == NULL) {
|
2022-07-09 11:21:17 -06:00
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
2022-05-23 13:16:10 -06:00
|
|
|
} else {
|
2024-05-06 13:04:00 -06:00
|
|
|
free(ctx->runas.apparmor_profile);
|
2023-08-21 09:21:49 -06:00
|
|
|
ctx->runas.apparmor_profile = def_apparmor_profile;
|
2022-07-09 11:21:17 -06:00
|
|
|
def_apparmor_profile = NULL;
|
2022-05-23 13:16:10 -06:00
|
|
|
}
|
2023-08-21 09:21:49 -06:00
|
|
|
if (ctx->runas.apparmor_profile != NULL) {
|
2022-07-09 11:21:17 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
2023-08-21 09:21:49 -06:00
|
|
|
"ctx->runas.apparmor_profile -> %s", ctx->runas.apparmor_profile);
|
2022-05-23 13:16:10 -06:00
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
/* Set Solaris privilege sets */
|
2024-05-03 08:15:19 -06:00
|
|
|
if (cs->privs != NULL) {
|
|
|
|
free(ctx->runas.privs);
|
|
|
|
ctx->runas.privs = strdup(cs->privs);
|
|
|
|
if (ctx->runas.privs == NULL) {
|
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
2021-02-18 06:09:08 -07:00
|
|
|
}
|
2024-05-03 08:15:19 -06:00
|
|
|
} else {
|
2024-05-06 13:04:00 -06:00
|
|
|
free(ctx->runas.privs);
|
2024-05-03 08:15:19 -06:00
|
|
|
ctx->runas.privs = def_privs;
|
|
|
|
def_privs = NULL;
|
2018-05-14 09:05:03 -06:00
|
|
|
}
|
2024-05-03 08:15:19 -06:00
|
|
|
if (ctx->runas.privs != NULL) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"ctx->runas.privs -> %s", ctx->runas.privs);
|
|
|
|
}
|
|
|
|
if (cs->limitprivs != NULL) {
|
|
|
|
free(ctx->runas.limitprivs);
|
|
|
|
ctx->runas.limitprivs = strdup(cs->limitprivs);
|
|
|
|
if (ctx->runas.limitprivs == NULL) {
|
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
2021-02-18 06:09:08 -07:00
|
|
|
}
|
2024-05-03 08:15:19 -06:00
|
|
|
} else {
|
2024-05-06 13:04:00 -06:00
|
|
|
free(ctx->runas.limitprivs);
|
2024-05-03 08:15:19 -06:00
|
|
|
ctx->runas.limitprivs = def_limitprivs;
|
|
|
|
def_limitprivs = NULL;
|
|
|
|
}
|
|
|
|
if (ctx->runas.limitprivs != NULL) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"ctx->runas.limitprivs -> %s", ctx->runas.limitprivs);
|
2018-05-14 09:05:03 -06:00
|
|
|
}
|
2020-09-09 15:26:44 -06:00
|
|
|
if (cs->timeout > 0) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_command_timeout = cs->timeout;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_command_timeout -> %d", def_command_timeout);
|
|
|
|
}
|
2020-09-01 06:26:00 -06:00
|
|
|
if (cs->runcwd != NULL) {
|
|
|
|
free(def_runcwd);
|
|
|
|
def_runcwd = strdup(cs->runcwd);
|
|
|
|
if (def_runcwd == NULL) {
|
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_runcwd -> %s", def_runcwd);
|
2020-09-01 06:26:00 -06:00
|
|
|
}
|
|
|
|
if (cs->runchroot != NULL) {
|
|
|
|
free(def_runchroot);
|
|
|
|
def_runchroot = strdup(cs->runchroot);
|
|
|
|
if (def_runchroot == NULL) {
|
|
|
|
sudo_warnx(U_("%s: %s"), __func__,
|
|
|
|
U_("unable to allocate memory"));
|
|
|
|
debug_return_bool(false);
|
|
|
|
}
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_runchroot -> %s", def_runchroot);
|
2020-09-01 06:26:00 -06:00
|
|
|
}
|
2020-09-09 15:26:44 -06:00
|
|
|
if (cs->tags.nopasswd != UNSPEC) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_authenticate = !cs->tags.nopasswd;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_authenticate -> %s", def_authenticate ? "true" : "false");
|
|
|
|
}
|
|
|
|
if (cs->tags.noexec != UNSPEC) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_noexec = cs->tags.noexec;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_noexec -> %s", def_noexec ? "true" : "false");
|
|
|
|
}
|
2021-08-09 15:50:25 -06:00
|
|
|
if (cs->tags.intercept != UNSPEC) {
|
|
|
|
def_intercept = cs->tags.intercept;
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_intercept -> %s", def_intercept ? "true" : "false");
|
|
|
|
}
|
2020-09-09 15:26:44 -06:00
|
|
|
if (cs->tags.setenv != UNSPEC) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_setenv = cs->tags.setenv;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_setenv -> %s", def_setenv ? "true" : "false");
|
|
|
|
}
|
|
|
|
if (cs->tags.log_input != UNSPEC) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_log_input = cs->tags.log_input;
|
2023-08-21 09:21:49 -06:00
|
|
|
cb_log_input(ctx, NULL, 0, 0, NULL, cs->tags.log_input);
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_log_input -> %s", def_log_input ? "true" : "false");
|
|
|
|
}
|
|
|
|
if (cs->tags.log_output != UNSPEC) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_log_output = cs->tags.log_output;
|
2023-08-21 09:21:49 -06:00
|
|
|
cb_log_output(ctx, NULL, 0, 0, NULL, cs->tags.log_output);
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_log_output -> %s", def_log_output ? "true" : "false");
|
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
if (cs->tags.send_mail != UNSPEC) {
|
|
|
|
if (cs->tags.send_mail) {
|
|
|
|
def_mail_all_cmnds = true;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_mail_all_cmnds -> true");
|
2018-05-14 09:05:03 -06:00
|
|
|
} else {
|
|
|
|
def_mail_all_cmnds = false;
|
|
|
|
def_mail_always = false;
|
|
|
|
def_mail_no_perms = false;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_mail_all_cmnds -> false, def_mail_always -> false, "
|
|
|
|
"def_mail_no_perms -> false");
|
2018-05-14 09:05:03 -06:00
|
|
|
}
|
|
|
|
}
|
2020-09-09 15:26:44 -06:00
|
|
|
if (cs->tags.follow != UNSPEC) {
|
2018-05-14 09:05:03 -06:00
|
|
|
def_sudoedit_follow = cs->tags.follow;
|
2020-09-09 15:26:44 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
|
|
|
"def_sudoedit_follow -> %s", def_sudoedit_follow ? "true" : "false");
|
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
debug_return_bool(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-05-22 08:58:51 -06:00
|
|
|
* Look up the user in the sudoers parse tree and check to see if they are
|
2018-05-14 09:05:03 -06:00
|
|
|
* allowed to run the specified command on this host as the target user.
|
|
|
|
*/
|
2023-07-10 11:06:23 -06:00
|
|
|
unsigned int
|
2023-08-21 09:21:49 -06:00
|
|
|
sudoers_lookup(struct sudo_nss_list *snl, struct sudoers_context *ctx,
|
|
|
|
time_t now, sudoers_lookup_callback_fn_t callback, void *cb_data,
|
|
|
|
int *cmnd_status, int pwflag)
|
2018-05-14 09:05:03 -06:00
|
|
|
{
|
|
|
|
struct defaults_list *defs = NULL;
|
2018-07-26 15:12:33 -06:00
|
|
|
struct sudoers_parse_tree *parse_tree = NULL;
|
2018-05-14 09:05:03 -06:00
|
|
|
struct cmndspec *cs = NULL;
|
|
|
|
struct sudo_nss *nss;
|
2020-09-09 15:26:45 -06:00
|
|
|
struct cmnd_info info;
|
2023-07-10 11:06:23 -06:00
|
|
|
unsigned int validated = FLAG_NO_USER | FLAG_NO_HOST;
|
2018-05-14 09:05:03 -06:00
|
|
|
int m, match = UNSPEC;
|
2019-12-22 08:48:16 -07:00
|
|
|
debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER);
|
2018-05-14 09:05:03 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Special case checking the "validate", "list" and "kill" pseudo-commands.
|
|
|
|
*/
|
2023-08-07 15:06:19 -06:00
|
|
|
if (pwflag) {
|
2023-08-21 09:21:49 -06:00
|
|
|
debug_return_uint(sudoers_lookup_pseudo(snl, ctx, now, callback,
|
2023-08-07 15:06:19 -06:00
|
|
|
cb_data, pwflag));
|
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
|
|
|
|
/* Need to be runas user while stat'ing things. */
|
2023-08-21 09:21:49 -06:00
|
|
|
if (!set_perms(ctx, PERM_RUNAS))
|
2023-07-10 11:06:23 -06:00
|
|
|
debug_return_uint(validated);
|
2018-05-14 09:05:03 -06:00
|
|
|
|
|
|
|
/* Query each sudoers source and check the user. */
|
|
|
|
TAILQ_FOREACH(nss, snl, entries) {
|
2023-08-21 09:21:49 -06:00
|
|
|
if (nss->query(ctx, nss, ctx->user.pw) == -1) {
|
2018-05-14 09:05:03 -06:00
|
|
|
/* The query function should have printed an error message. */
|
|
|
|
SET(validated, VALIDATE_ERROR);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-21 09:21:49 -06:00
|
|
|
m = sudoers_lookup_check(nss, ctx, &validated, &info, now, callback,
|
2023-08-07 15:06:19 -06:00
|
|
|
cb_data, &cs, &defs);
|
Try to make sudo less vulnerable to ROWHAMMER attacks.
We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS,
AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we
explicitly test for expected values instead of using a negated test
against an error value. In the parser match functions this means
explicitly checking for ALLOW or DENY instead of accepting anything
that is not set to UNSPEC.
Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk
Sunar, all affiliated with the Vernam Applied Cryptography and
Cybersecurity Lab at Worcester Polytechnic Institute, for the report.
Paper preprint: https://arxiv.org/abs/2309.02545
2023-09-09 14:07:04 -06:00
|
|
|
if (SPECIFIED(m)) {
|
2018-05-14 09:05:03 -06:00
|
|
|
match = m;
|
2018-07-26 15:12:33 -06:00
|
|
|
parse_tree = nss->parse_tree;
|
|
|
|
}
|
2018-05-14 09:05:03 -06:00
|
|
|
|
|
|
|
if (!sudo_nss_can_continue(nss, m))
|
|
|
|
break;
|
|
|
|
}
|
Try to make sudo less vulnerable to ROWHAMMER attacks.
We now use ROWHAMMER-resistent values for ALLOW, DENY, AUTH_SUCCESS,
AUTH_FAILURE, AUTH_ERROR and AUTH_NONINTERACTIVE. In addition, we
explicitly test for expected values instead of using a negated test
against an error value. In the parser match functions this means
explicitly checking for ALLOW or DENY instead of accepting anything
that is not set to UNSPEC.
Thanks to Andrew J. Adiletta, M. Caner Tol, Yarkin Doroz, and Berk
Sunar, all affiliated with the Vernam Applied Cryptography and
Cybersecurity Lab at Worcester Polytechnic Institute, for the report.
Paper preprint: https://arxiv.org/abs/2309.02545
2023-09-09 14:07:04 -06:00
|
|
|
if (SPECIFIED(match)) {
|
2020-09-09 15:26:45 -06:00
|
|
|
if (info.cmnd_path != NULL) {
|
2023-08-12 10:39:59 -06:00
|
|
|
/* Update cmnd, cmnd_stat, cmnd_status from matching entry. */
|
2023-08-21 09:21:49 -06:00
|
|
|
free(ctx->user.cmnd);
|
|
|
|
ctx->user.cmnd = info.cmnd_path;
|
|
|
|
if (ctx->user.cmnd_stat != NULL)
|
|
|
|
*ctx->user.cmnd_stat = info.cmnd_stat;
|
2020-09-09 15:26:45 -06:00
|
|
|
*cmnd_status = info.status;
|
|
|
|
}
|
2018-07-26 15:12:33 -06:00
|
|
|
if (defs != NULL)
|
2023-08-21 09:21:49 -06:00
|
|
|
(void)update_defaults(ctx, parse_tree, defs, SETDEF_GENERIC, false);
|
|
|
|
if (!apply_cmndspec(ctx, cs))
|
2018-05-14 09:05:03 -06:00
|
|
|
SET(validated, VALIDATE_ERROR);
|
|
|
|
else if (match == ALLOW)
|
|
|
|
SET(validated, VALIDATE_SUCCESS);
|
|
|
|
else
|
|
|
|
SET(validated, VALIDATE_FAILURE);
|
2004-03-24 23:06:34 +00:00
|
|
|
}
|
2015-06-25 11:12:36 -06:00
|
|
|
if (!restore_perms())
|
|
|
|
SET(validated, VALIDATE_ERROR);
|
2023-07-10 11:06:23 -06:00
|
|
|
debug_return_uint(validated);
|
2004-03-24 23:06:34 +00:00
|
|
|
}
|