2
* Copyright (c) 2004-2005, 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com>
4
* Permission to use, copy, modify, and distribute this software for any
5
* purpose with or without fee is hereby granted, provided that the above
6
* copyright notice and this permission notice appear in all copies.
8
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
16
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21
#include <sys/types.h>
22
#include <sys/param.h>
31
#endif /* STDC_HEADERS */
34
#endif /* HAVE_STRING_H */
37
#endif /* HAVE_STRINGS_H */
40
#endif /* HAVE_UNISTD_H */
50
/* Characters that must be quoted in sudoers */
51
#define SUDOERS_QUOTED ":\\,=#\""
53
/* sudoers nsswitch routines */
54
struct sudo_nss sudo_nss_file = {
62
sudo_file_display_cmnd,
63
sudo_file_display_defaults,
64
sudo_file_display_bound_defaults,
65
sudo_file_display_privs
72
extern char *errorfile;
73
extern int errorlineno, parse_error;
78
static void print_member(struct lbuf *, char *, int, int, int);
79
static int display_bound_defaults(int, struct lbuf *);
82
sudo_file_open(struct sudo_nss *nss)
84
if (def_ignore_local_sudoers)
86
nss->handle = open_sudoers(sudoers_file, FALSE, NULL);
87
return nss->handle ? 0 : -1;
91
sudo_file_close(struct sudo_nss *nss)
93
/* Free parser data structures and close sudoers file. */
95
if (nss->handle != NULL) {
104
* Parse the specified sudoers file.
107
sudo_file_parse(struct sudo_nss *nss)
109
if (nss->handle == NULL)
112
init_parser(sudoers_file, 0);
114
if (yyparse() != 0 || parse_error) {
115
log_error(NO_EXIT, _("parse error in %s near line %d"),
116
errorfile, errorlineno);
123
* Wrapper around update_defaults() for nsswitch code.
126
sudo_file_setdefs(struct sudo_nss *nss)
128
if (nss->handle == NULL)
131
if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
137
* Look up the user in the parsed sudoers file and check to see if they are
138
* allowed to run the specified command on this host as the target user.
141
sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag)
143
int match, host_match, runas_match, cmnd_match;
145
struct cmndtag *tags = NULL;
146
struct privilege *priv;
149
if (nss->handle == NULL)
153
* Only check the actual command if pwflag is not set.
154
* It is set for the "validate", "list" and "kill" pseudo-commands.
155
* Always check the host and user.
159
enum def_tuple pwcheck;
161
pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
162
nopass = (pwcheck == all) ? TRUE : FALSE;
165
SET(validated, FLAG_NO_CHECK);
166
CLR(validated, FLAG_NO_USER);
167
CLR(validated, FLAG_NO_HOST);
169
tq_foreach_fwd(&userspecs, us) {
170
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
172
tq_foreach_fwd(&us->privileges, priv) {
173
if (hostlist_matches(&priv->hostlist) != ALLOW)
175
tq_foreach_fwd(&priv->cmndlist, cs) {
176
/* Only check the command when listing another user. */
177
if (user_uid == 0 || list_pw == NULL ||
178
user_uid == list_pw->pw_uid ||
179
cmnd_matches(cs->cmnd) == ALLOW)
181
if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
182
(pwcheck == all && cs->tags.nopasswd != TRUE))
183
nopass = cs->tags.nopasswd;
187
if (match == ALLOW || user_uid == 0) {
188
/* User has an entry for this host. */
189
SET(validated, VALIDATE_OK);
190
} else if (match == DENY)
191
SET(validated, VALIDATE_NOT_OK);
192
if (pwcheck == always && def_authenticate)
193
SET(validated, FLAG_CHECK_USER);
194
else if (pwcheck == never || nopass == TRUE)
195
def_authenticate = FALSE;
199
/* Need to be runas user while stat'ing things. */
200
set_perms(PERM_RUNAS);
203
tq_foreach_rev(&userspecs, us) {
204
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
206
CLR(validated, FLAG_NO_USER);
207
tq_foreach_rev(&us->privileges, priv) {
208
host_match = hostlist_matches(&priv->hostlist);
209
if (host_match == ALLOW)
210
CLR(validated, FLAG_NO_HOST);
213
tq_foreach_rev(&priv->cmndlist, cs) {
214
runas_match = runaslist_matches(&cs->runasuserlist,
215
&cs->runasgrouplist);
216
if (runas_match == ALLOW) {
217
cmnd_match = cmnd_matches(cs->cmnd);
218
if (cmnd_match != UNSPEC) {
222
/* Set role and type if not specified on command line. */
223
if (user_role == NULL)
224
user_role = cs->role ? estrdup(cs->role) : def_role;
225
if (user_type == NULL)
226
user_type = cs->type ? estrdup(cs->type) : def_type;
227
#endif /* HAVE_SELINUX */
235
if (match == ALLOW) {
236
SET(validated, VALIDATE_OK);
237
CLR(validated, VALIDATE_NOT_OK);
239
if (tags->nopasswd != UNSPEC)
240
def_authenticate = !tags->nopasswd;
241
if (tags->noexec != UNSPEC)
242
def_noexec = tags->noexec;
243
if (tags->setenv != UNSPEC)
244
def_setenv = tags->setenv;
245
if (tags->log_input != UNSPEC)
246
def_log_input = tags->log_input;
247
if (tags->log_output != UNSPEC)
248
def_log_output = tags->log_output;
250
} else if (match == DENY) {
251
SET(validated, VALIDATE_NOT_OK);
252
CLR(validated, VALIDATE_OK);
253
if (tags != NULL && tags->nopasswd != UNSPEC)
254
def_authenticate = !tags->nopasswd;
260
#define TAG_CHANGED(t) \
261
(cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
264
sudo_file_append_cmnd(struct cmndspec *cs, struct cmndtag *tags,
271
lbuf_append(lbuf, "ROLE=%s ", cs->role);
273
lbuf_append(lbuf, "TYPE=%s ", cs->type);
274
#endif /* HAVE_SELINUX */
275
if (TAG_CHANGED(setenv)) {
276
lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " : "NOSETENV: ");
277
tags->setenv = cs->tags.setenv;
279
if (TAG_CHANGED(noexec)) {
280
lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " : "EXEC: ");
281
tags->noexec = cs->tags.noexec;
283
if (TAG_CHANGED(nopasswd)) {
284
lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " : "PASSWD: ");
285
tags->nopasswd = cs->tags.nopasswd;
287
if (TAG_CHANGED(log_input)) {
288
lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
289
tags->log_input = cs->tags.log_input;
291
if (TAG_CHANGED(log_output)) {
292
lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
293
tags->log_output = cs->tags.log_output;
296
print_member(lbuf, m->name, m->type, m->negated,
301
sudo_file_display_priv_short(struct passwd *pw, struct userspec *us,
306
struct privilege *priv;
310
tq_foreach_fwd(&us->privileges, priv) {
311
if (hostlist_matches(&priv->hostlist) != ALLOW)
313
tags.noexec = UNSPEC;
314
tags.setenv = UNSPEC;
315
tags.nopasswd = UNSPEC;
316
tags.log_input = UNSPEC;
317
tags.log_output = UNSPEC;
318
lbuf_append(lbuf, " ");
319
tq_foreach_fwd(&priv->cmndlist, cs) {
320
if (cs != tq_first(&priv->cmndlist))
321
lbuf_append(lbuf, ", ");
322
lbuf_append(lbuf, "(");
323
if (!tq_empty(&cs->runasuserlist)) {
324
tq_foreach_fwd(&cs->runasuserlist, m) {
325
if (m != tq_first(&cs->runasuserlist))
326
lbuf_append(lbuf, ", ");
327
print_member(lbuf, m->name, m->type, m->negated,
330
} else if (tq_empty(&cs->runasgrouplist)) {
331
lbuf_append(lbuf, "%s", def_runas_default);
333
lbuf_append(lbuf, "%s", pw->pw_name);
335
if (!tq_empty(&cs->runasgrouplist)) {
336
lbuf_append(lbuf, " : ");
337
tq_foreach_fwd(&cs->runasgrouplist, m) {
338
if (m != tq_first(&cs->runasgrouplist))
339
lbuf_append(lbuf, ", ");
340
print_member(lbuf, m->name, m->type, m->negated,
344
lbuf_append(lbuf, ") ");
345
sudo_file_append_cmnd(cs, &tags, lbuf);
348
lbuf_append(lbuf, "\n");
354
sudo_file_display_priv_long(struct passwd *pw, struct userspec *us,
359
struct privilege *priv;
363
tq_foreach_fwd(&us->privileges, priv) {
364
if (hostlist_matches(&priv->hostlist) != ALLOW)
366
tags.noexec = UNSPEC;
367
tags.setenv = UNSPEC;
368
tags.nopasswd = UNSPEC;
369
tags.log_input = UNSPEC;
370
tags.log_output = UNSPEC;
371
lbuf_append(lbuf, _("\nSudoers entry:\n"));
372
tq_foreach_fwd(&priv->cmndlist, cs) {
373
lbuf_append(lbuf, _(" RunAsUsers: "));
374
if (!tq_empty(&cs->runasuserlist)) {
375
tq_foreach_fwd(&cs->runasuserlist, m) {
376
if (m != tq_first(&cs->runasuserlist))
377
lbuf_append(lbuf, ", ");
378
print_member(lbuf, m->name, m->type, m->negated,
381
} else if (tq_empty(&cs->runasgrouplist)) {
382
lbuf_append(lbuf, "%s", def_runas_default);
384
lbuf_append(lbuf, "%s", pw->pw_name);
386
lbuf_append(lbuf, "\n");
387
if (!tq_empty(&cs->runasgrouplist)) {
388
lbuf_append(lbuf, _(" RunAsGroups: "));
389
tq_foreach_fwd(&cs->runasgrouplist, m) {
390
if (m != tq_first(&cs->runasgrouplist))
391
lbuf_append(lbuf, ", ");
392
print_member(lbuf, m->name, m->type, m->negated,
395
lbuf_append(lbuf, "\n");
397
lbuf_append(lbuf, _(" Commands:\n\t"));
398
sudo_file_append_cmnd(cs, &tags, lbuf);
399
lbuf_append(lbuf, "\n");
407
sudo_file_display_privs(struct sudo_nss *nss, struct passwd *pw,
413
if (nss->handle == NULL)
416
tq_foreach_fwd(&userspecs, us) {
417
if (userlist_matches(pw, &us->users) != ALLOW)
421
nfound += sudo_file_display_priv_long(pw, us, lbuf);
423
nfound += sudo_file_display_priv_short(pw, us, lbuf);
430
* Display matching Defaults entries for the given user on this host.
433
sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw,
440
if (nss->handle == NULL)
443
if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
448
tq_foreach_fwd(&defaults, d) {
451
if (hostlist_matches(&d->binding) != ALLOW)
455
if (userlist_matches(pw, &d->binding) != ALLOW)
462
if (d->val != NULL) {
463
lbuf_append(lbuf, "%s%s%s", prefix, d->var,
464
d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=");
465
if (strpbrk(d->val, " \t") != NULL) {
466
lbuf_append(lbuf, "\"");
467
lbuf_append_quoted(lbuf, "\"", "%s", d->val);
468
lbuf_append(lbuf, "\"");
470
lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", d->val);
472
lbuf_append(lbuf, "%s%s%s", prefix,
473
d->op == FALSE ? "!" : "", d->var);
482
* Display Defaults entries that are per-runas or per-command
485
sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
490
/* XXX - should only print ones that match what the user can do. */
491
nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
492
nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
498
* Display Defaults entries of the given type.
501
display_bound_defaults(int dtype, struct lbuf *lbuf)
504
struct member *m, *binding = NULL;
506
int atype, nfound = 0;
528
tq_foreach_fwd(&defaults, d) {
529
if (d->type != dtype)
533
if (binding != tq_first(&d->binding)) {
534
binding = tq_first(&d->binding);
536
lbuf_append(lbuf, "\n");
537
lbuf_append(lbuf, " Defaults%s", dsep);
538
for (m = binding; m != NULL; m = m->next) {
540
lbuf_append(lbuf, ",");
541
print_member(lbuf, m->name, m->type, m->negated, atype);
542
lbuf_append(lbuf, " ");
545
lbuf_append(lbuf, ", ");
546
if (d->val != NULL) {
547
lbuf_append(lbuf, "%s%s%s", d->var, d->op == '+' ? "+=" :
548
d->op == '-' ? "-=" : "=", d->val);
550
lbuf_append(lbuf, "%s%s", d->op == FALSE ? "!" : "", d->var);
557
sudo_file_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
560
struct member *match;
561
struct privilege *priv;
564
int host_match, runas_match, cmnd_match;
566
if (nss->handle == NULL)
570
tq_foreach_rev(&userspecs, us) {
571
if (userlist_matches(pw, &us->users) != ALLOW)
574
tq_foreach_rev(&us->privileges, priv) {
575
host_match = hostlist_matches(&priv->hostlist);
576
if (host_match != ALLOW)
578
tq_foreach_rev(&priv->cmndlist, cs) {
579
runas_match = runaslist_matches(&cs->runasuserlist,
580
&cs->runasgrouplist);
581
if (runas_match == ALLOW) {
582
cmnd_match = cmnd_matches(cs->cmnd);
583
if (cmnd_match != UNSPEC) {
584
match = host_match && runas_match ? cs->cmnd : NULL;
592
if (match != NULL && !match->negated) {
593
sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
594
safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
602
* Print the contents of a struct member to stdout
605
_print_member(struct lbuf *lbuf, char *name, int type, int negated,
610
struct sudo_command *c;
614
lbuf_append(lbuf, "%sALL", negated ? "!" : "");
617
c = (struct sudo_command *) name;
619
lbuf_append(lbuf, "!");
620
lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
622
lbuf_append(lbuf, " ");
623
lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->args);
627
if ((a = alias_find(name, alias_type)) != NULL) {
628
tq_foreach_fwd(&a->members, m) {
629
if (m != tq_first(&a->members))
630
lbuf_append(lbuf, ", ");
631
_print_member(lbuf, m->name, m->type,
632
negated ? !m->negated : m->negated, alias_type);
638
lbuf_append(lbuf, "%s%s", negated ? "!" : "", name);
644
print_member(struct lbuf *lbuf, char *name, int type, int negated,
648
_print_member(lbuf, name, type, negated, alias_type);