Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/plugins/sudoers/fmtsudoers.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2004-2005, 2007-2024 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <pwd.h>
25
#include <time.h>
26
27
#include <sudoers.h>
28
#include <sudo_lbuf.h>
29
#include <gram.h>
30
31
/*
32
* Write the contents of a struct member to the lbuf.
33
* If alias_type is not UNSPEC, expand aliases using that type with
34
* the specified separator (which must not be NULL in the UNSPEC case).
35
*/
36
static bool
37
sudoers_format_member_int(struct sudo_lbuf *lbuf,
38
const struct sudoers_parse_tree *parse_tree, const char *name, int type,
39
bool negated, const char *separator, short alias_type)
40
{
41
const struct sudoers_context *ctx = parse_tree->ctx;
42
struct alias *a;
43
const struct member *m;
44
const struct sudo_command *c;
45
const struct command_digest *digest;
46
debug_decl(sudoers_format_member_int, SUDOERS_DEBUG_UTIL);
47
48
switch (type) {
49
case MYSELF:
50
sudo_lbuf_append(lbuf, "%s%s", negated ? "!" : "",
51
ctx->runas.list_pw ? ctx->runas.list_pw->pw_name :
52
(ctx->user.name ? ctx->user.name : ""));
53
break;
54
case ALL:
55
if (name == NULL) {
56
sudo_lbuf_append(lbuf, "%sALL", negated ? "!" : "");
57
break;
58
}
59
FALLTHROUGH;
60
case COMMAND:
61
c = (struct sudo_command *) name;
62
TAILQ_FOREACH(digest, &c->digests, entries) {
63
sudo_lbuf_append(lbuf, "%s:%s%s ",
64
digest_type_to_name(digest->digest_type),
65
digest->digest_str, TAILQ_NEXT(digest, entries) ? "," : "");
66
}
67
if (negated)
68
sudo_lbuf_append(lbuf, "!");
69
if (c->cmnd == NULL || c->cmnd[0] == '^') {
70
/* No additional quoting of characters inside a regex. */
71
sudo_lbuf_append(lbuf, "%s", c->cmnd ? c->cmnd : "ALL");
72
} else {
73
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED_CMD, "%s",
74
c->cmnd);
75
}
76
if (c->args != NULL) {
77
sudo_lbuf_append(lbuf, " ");
78
if (c->args[0] == '^') {
79
/* No additional quoting of characters inside a regex. */
80
sudo_lbuf_append(lbuf, "%s", c->args);
81
} else {
82
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED_ARG, "%s",
83
c->args);
84
}
85
}
86
break;
87
case USERGROUP:
88
/* Special case for %#gid, %:non-unix-group, %:#non-unix-gid */
89
if (strpbrk(name, " \t") == NULL) {
90
if (*++name == ':') {
91
name++;
92
sudo_lbuf_append(lbuf, "%s", "%:");
93
} else {
94
sudo_lbuf_append(lbuf, "%s", "%");
95
}
96
}
97
goto print_word;
98
case ALIAS:
99
if (alias_type != UNSPEC) {
100
if ((a = alias_get(parse_tree, name, alias_type)) != NULL) {
101
TAILQ_FOREACH(m, &a->members, entries) {
102
if (m != TAILQ_FIRST(&a->members))
103
sudo_lbuf_append(lbuf, "%s", separator);
104
sudoers_format_member_int(lbuf, parse_tree,
105
m->name, m->type,
106
negated ? !m->negated : m->negated,
107
separator, alias_type);
108
}
109
alias_put(a);
110
break;
111
}
112
}
113
FALLTHROUGH;
114
default:
115
print_word:
116
/* Do not quote UID/GID, all others get quoted. */
117
if (name[0] == '#' &&
118
name[strspn(name + 1, "0123456789") + 1] == '\0') {
119
sudo_lbuf_append(lbuf, "%s%s", negated ? "!" : "", name);
120
} else {
121
if (strpbrk(name, " \t") != NULL) {
122
sudo_lbuf_append(lbuf, "%s\"", negated ? "!" : "");
123
sudo_lbuf_append_quoted(lbuf, "\"", "%s", name);
124
sudo_lbuf_append(lbuf, "\"");
125
} else {
126
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s%s",
127
negated ? "!" : "", name);
128
}
129
}
130
break;
131
}
132
debug_return_bool(!sudo_lbuf_error(lbuf));
133
}
134
135
bool
136
sudoers_format_member(struct sudo_lbuf *lbuf,
137
const struct sudoers_parse_tree *parse_tree, const struct member *m,
138
const char *separator, short alias_type)
139
{
140
return sudoers_format_member_int(lbuf, parse_tree, m->name, m->type,
141
m->negated, separator, alias_type);
142
}
143
144
/*
145
* Store a defaults entry as a command tag.
146
*/
147
bool
148
sudoers_defaults_to_tags(const char *var, const char *val, int op,
149
struct cmndtag *tags)
150
{
151
bool ret = true;
152
debug_decl(sudoers_defaults_to_tags, SUDOERS_DEBUG_UTIL);
153
154
if (op == true || op == false) {
155
if (strcmp(var, "authenticate") == 0) {
156
tags->nopasswd = op == false;
157
} else if (strcmp(var, "sudoedit_follow") == 0) {
158
tags->follow = op == true;
159
} else if (strcmp(var, "log_input") == 0) {
160
tags->log_input = op == true;
161
} else if (strcmp(var, "log_output") == 0) {
162
tags->log_output = op == true;
163
} else if (strcmp(var, "noexec") == 0) {
164
tags->noexec = op == true;
165
} else if (strcmp(var, "intercept") == 0) {
166
tags->intercept = op == true;
167
} else if (strcmp(var, "setenv") == 0) {
168
tags->setenv = op == true;
169
} else if (strcmp(var, "mail_all_cmnds") == 0 ||
170
strcmp(var, "mail_always") == 0 ||
171
strcmp(var, "mail_no_perms") == 0) {
172
tags->send_mail = op == true;
173
} else {
174
ret = false;
175
}
176
} else {
177
ret = false;
178
}
179
debug_return_bool(ret);
180
}
181
182
/*
183
* Convert a defaults list to command tags.
184
*/
185
bool
186
sudoers_defaults_list_to_tags(const struct defaults_list *defs,
187
struct cmndtag *tags)
188
{
189
const struct defaults *d;
190
bool ret = true;
191
debug_decl(sudoers_defaults_list_to_tags, SUDOERS_DEBUG_UTIL);
192
193
TAGS_INIT(tags);
194
if (defs != NULL) {
195
TAILQ_FOREACH(d, defs, entries) {
196
if (!sudoers_defaults_to_tags(d->var, d->val, d->op, tags)) {
197
if (d->val != NULL) {
198
sudo_debug_printf(SUDO_DEBUG_WARN,
199
"unable to convert defaults to tag: %s%s%s", d->var,
200
d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=", d->val);
201
} else {
202
sudo_debug_printf(SUDO_DEBUG_WARN,
203
"unable to convert defaults to tag: %s%s%s",
204
d->op == false ? "!" : "", d->var, "");
205
}
206
ret = false;
207
}
208
}
209
}
210
debug_return_bool(ret);
211
}
212
213
#define FIELD_CHANGED(ocs, ncs, fld) \
214
((ocs) == NULL || (ncs)->fld != (ocs)->fld)
215
216
#define TAG_CHANGED(ocs, ncs, t, tt) \
217
(TAG_SET((t).tt) && FIELD_CHANGED(ocs, ncs, tags.tt))
218
219
/*
220
* Write a cmndspec to lbuf in sudoers format.
221
*/
222
bool
223
sudoers_format_cmndspec(struct sudo_lbuf *lbuf,
224
const struct sudoers_parse_tree *parse_tree, const struct cmndspec *cs,
225
const struct cmndspec *prev_cs, struct cmndtag tags, bool expand_aliases)
226
{
227
debug_decl(sudoers_format_cmndspec, SUDOERS_DEBUG_UTIL);
228
229
/* Merge privilege-level tags with cmndspec tags. */
230
TAGS_MERGE(tags, cs->tags);
231
232
if (cs->privs != NULL && FIELD_CHANGED(prev_cs, cs, privs))
233
sudo_lbuf_append(lbuf, "PRIVS=\"%s\" ", cs->privs);
234
if (cs->limitprivs != NULL && FIELD_CHANGED(prev_cs, cs, limitprivs))
235
sudo_lbuf_append(lbuf, "LIMITPRIVS=\"%s\" ", cs->limitprivs);
236
if (cs->role != NULL && FIELD_CHANGED(prev_cs, cs, role))
237
sudo_lbuf_append(lbuf, "ROLE=%s ", cs->role);
238
if (cs->type != NULL && FIELD_CHANGED(prev_cs, cs, type))
239
sudo_lbuf_append(lbuf, "TYPE=%s ", cs->type);
240
if (cs->apparmor_profile != NULL && FIELD_CHANGED(prev_cs, cs, apparmor_profile))
241
sudo_lbuf_append(lbuf, "APPARMOR_PROFILE=%s ", cs->apparmor_profile);
242
if (cs->runchroot != NULL && FIELD_CHANGED(prev_cs, cs, runchroot))
243
sudo_lbuf_append(lbuf, "CHROOT=%s ", cs->runchroot);
244
if (cs->runcwd != NULL && FIELD_CHANGED(prev_cs, cs, runcwd))
245
sudo_lbuf_append(lbuf, "CWD=%s ", cs->runcwd);
246
if (cs->timeout > 0 && FIELD_CHANGED(prev_cs, cs, timeout)) {
247
char numbuf[STRLEN_MAX_SIGNED(int) + 1];
248
(void)snprintf(numbuf, sizeof(numbuf), "%d", cs->timeout);
249
sudo_lbuf_append(lbuf, "TIMEOUT=%s ", numbuf);
250
}
251
if (cs->notbefore != UNSPEC && FIELD_CHANGED(prev_cs, cs, notbefore)) {
252
char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
253
struct tm gmt;
254
if (gmtime_r(&cs->notbefore, &gmt) != NULL) {
255
size_t len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
256
if (len != 0 && buf[sizeof(buf) - 1] == '\0')
257
sudo_lbuf_append(lbuf, "NOTBEFORE=%s ", buf);
258
}
259
}
260
if (cs->notafter != UNSPEC && FIELD_CHANGED(prev_cs, cs, notafter)) {
261
char buf[sizeof("CCYYMMDDHHMMSSZ")] = "";
262
struct tm gmt;
263
if (gmtime_r(&cs->notafter, &gmt) != NULL) {
264
size_t len = strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &gmt);
265
if (len != 0 && buf[sizeof(buf) - 1] == '\0')
266
sudo_lbuf_append(lbuf, "NOTAFTER=%s ", buf);
267
}
268
}
269
if (TAG_CHANGED(prev_cs, cs, tags, setenv))
270
sudo_lbuf_append(lbuf, tags.setenv ? "SETENV: " : "NOSETENV: ");
271
if (TAG_CHANGED(prev_cs, cs, tags, intercept))
272
sudo_lbuf_append(lbuf, tags.intercept ? "INTERCEPT: " : "NOINTERCEPT: ");
273
if (TAG_CHANGED(prev_cs, cs, tags, noexec))
274
sudo_lbuf_append(lbuf, tags.noexec ? "NOEXEC: " : "EXEC: ");
275
if (TAG_CHANGED(prev_cs, cs, tags, nopasswd))
276
sudo_lbuf_append(lbuf, tags.nopasswd ? "NOPASSWD: " : "PASSWD: ");
277
if (TAG_CHANGED(prev_cs, cs, tags, log_input))
278
sudo_lbuf_append(lbuf, tags.log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
279
if (TAG_CHANGED(prev_cs, cs, tags, log_output))
280
sudo_lbuf_append(lbuf, tags.log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
281
if (TAG_CHANGED(prev_cs, cs, tags, send_mail))
282
sudo_lbuf_append(lbuf, tags.send_mail ? "MAIL: " : "NOMAIL: ");
283
if (TAG_CHANGED(prev_cs, cs, tags, follow))
284
sudo_lbuf_append(lbuf, tags.follow ? "FOLLOW: " : "NOFOLLOW: ");
285
sudoers_format_member(lbuf, parse_tree, cs->cmnd, ", ",
286
expand_aliases ? CMNDALIAS : UNSPEC);
287
debug_return_bool(!sudo_lbuf_error(lbuf));
288
}
289
290
/*
291
* Format and append a defaults entry to the specified lbuf.
292
*/
293
bool
294
sudoers_format_default(struct sudo_lbuf *lbuf, const struct defaults *d)
295
{
296
debug_decl(sudoers_format_default, SUDOERS_DEBUG_UTIL);
297
298
if (d->val != NULL) {
299
sudo_lbuf_append(lbuf, "%s%s", d->var,
300
d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=");
301
if (strpbrk(d->val, " \t") != NULL) {
302
sudo_lbuf_append(lbuf, "\"");
303
sudo_lbuf_append_quoted(lbuf, "\"", "%s", d->val);
304
sudo_lbuf_append(lbuf, "\"");
305
} else
306
sudo_lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", d->val);
307
} else {
308
sudo_lbuf_append(lbuf, "%s%s", d->op == false ? "!" : "", d->var);
309
}
310
debug_return_bool(!sudo_lbuf_error(lbuf));
311
}
312
313