1 /* 2 * SPDX-License-Identifier: ISC 3 * 4 * Copyright (c) 1996, 1998-2000, 2004, 2007-2021 5 * Todd C. Miller <Todd.Miller@sudo.ws> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #ifndef SUDOERS_PARSE_H 21 #define SUDOERS_PARSE_H 22 23 #include <sys/stat.h> 24 #include "sudo_queue.h" 25 26 /* Characters that must be quoted in sudoers. */ 27 #define SUDOERS_QUOTED ":\\,=#\"" 28 29 /* Returns true if string 's' contains meta characters. */ 30 #define has_meta(s) (strpbrk(s, "\\?*[]") != NULL) 31 32 /* Match by name, not inode, when fuzzing. */ 33 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 34 # define SUDOERS_NAME_MATCH 35 #endif 36 37 #undef UNSPEC 38 #define UNSPEC -1 39 #undef DENY 40 #define DENY 0 41 #undef ALLOW 42 #define ALLOW 1 43 #undef IMPLIED 44 #define IMPLIED 2 45 46 /* 47 * Initialize all tags to UNSPEC. 48 */ 49 #define TAGS_INIT(t) do { \ 50 (t)->follow = UNSPEC; \ 51 (t)->intercept = UNSPEC; \ 52 (t)->log_input = UNSPEC; \ 53 (t)->log_output = UNSPEC; \ 54 (t)->noexec = UNSPEC; \ 55 (t)->nopasswd = UNSPEC; \ 56 (t)->send_mail = UNSPEC; \ 57 (t)->setenv = UNSPEC; \ 58 } while (0) 59 60 /* 61 * Copy any tags set in t2 into t, overriding the value in t. 62 */ 63 #define TAGS_MERGE(t, t2) do { \ 64 if ((t2).follow != UNSPEC) \ 65 (t).follow = (t2).follow; \ 66 if ((t2).intercept != UNSPEC) \ 67 (t).intercept = (t2).intercept; \ 68 if ((t2).log_input != UNSPEC) \ 69 (t).log_input = (t2).log_input; \ 70 if ((t2).log_output != UNSPEC) \ 71 (t).log_output = (t2).log_output; \ 72 if ((t2).noexec != UNSPEC) \ 73 (t).noexec = (t2).noexec; \ 74 if ((t2).nopasswd != UNSPEC) \ 75 (t).nopasswd = (t2).nopasswd; \ 76 if ((t2).send_mail != UNSPEC) \ 77 (t).send_mail = (t2).send_mail; \ 78 if ((t2).setenv != UNSPEC) \ 79 (t).setenv = (t2).setenv; \ 80 } while (0) 81 82 /* 83 * Returns true if any tag are not UNSPEC, else false. 84 */ 85 #define TAGS_SET(t) \ 86 ((t).follow != UNSPEC || (t).intercept != UNSPEC || \ 87 (t).log_input != UNSPEC || (t).log_output != UNSPEC || \ 88 (t).noexec != UNSPEC || (t).nopasswd != UNSPEC || \ 89 (t).send_mail != UNSPEC || (t).setenv != UNSPEC) 90 91 /* 92 * Returns true if the specified tag is not UNSPEC or IMPLIED, else false. 93 */ 94 #define TAG_SET(tt) \ 95 ((tt) != UNSPEC && (tt) != IMPLIED) 96 97 /* 98 * Returns true if any tags set in nt differ between ot and nt, else false. 99 */ 100 #define TAGS_CHANGED(ot, nt) \ 101 ((TAG_SET((nt).follow) && (nt).follow != (ot).follow) || \ 102 (TAG_SET((nt).intercept) && (nt).intercept != (ot).intercept) || \ 103 (TAG_SET((nt).log_input) && (nt).log_input != (ot).log_input) || \ 104 (TAG_SET((nt).log_output) && (nt).log_output != (ot).log_output) || \ 105 (TAG_SET((nt).noexec) && (nt).noexec != (ot).noexec) || \ 106 (TAG_SET((nt).nopasswd) && (nt).nopasswd != (ot).nopasswd) || \ 107 (TAG_SET((nt).setenv) && (nt).setenv != (ot).setenv) || \ 108 (TAG_SET((nt).send_mail) && (nt).send_mail != (ot).send_mail)) 109 110 /* 111 * Returns true if the runas user and group lists match, else false. 112 */ 113 #define RUNAS_CHANGED(cs1, cs2) \ 114 ((cs1)->runasuserlist != (cs2)->runasuserlist || \ 115 (cs1)->runasgrouplist != (cs2)->runasgrouplist) 116 117 struct command_digest { 118 TAILQ_ENTRY(command_digest) entries; 119 unsigned int digest_type; 120 char *digest_str; 121 }; 122 123 /* 124 * Tags associated with a command. 125 * Possible values: true, false, IMPLIED, UNSPEC. 126 */ 127 struct cmndtag { 128 signed int follow: 3; 129 signed int intercept: 3; 130 signed int log_input: 3; 131 signed int log_output: 3; 132 signed int noexec: 3; 133 signed int nopasswd: 3; 134 signed int send_mail: 3; 135 signed int setenv: 3; 136 }; 137 138 /* 139 * Per-command option container struct. 140 */ 141 struct command_options { 142 time_t notbefore; /* time restriction */ 143 time_t notafter; /* time restriction */ 144 int timeout; /* command timeout */ 145 char *runcwd; /* working directory */ 146 char *runchroot; /* root directory */ 147 #ifdef HAVE_SELINUX 148 char *role, *type; /* SELinux role and type */ 149 #endif 150 #ifdef HAVE_PRIV_SET 151 char *privs, *limitprivs; /* Solaris privilege sets */ 152 #endif 153 }; 154 155 /* 156 * The parsed sudoers file is stored as a collection of linked lists, 157 * modelled after the yacc grammar. 158 * 159 * Other than the alias struct, which is stored in a red-black tree, 160 * the data structure used is a doubly-linked tail queue. While sudoers 161 * is being parsed, a headless tail queue is used where the first entry 162 * acts as the head and the prev pointer does double duty as the tail pointer. 163 * This makes it possible to trivially append sub-lists. In addition, the prev 164 * pointer is always valid (even if it points to itself). Unlike a circle 165 * queue, the next pointer of the last entry is NULL and does not point back 166 * to the head. When the tail queue is finalized, it is converted to a 167 * normal BSD tail queue. 168 */ 169 170 /* 171 * Tail queue list head structures. 172 */ 173 TAILQ_HEAD(defaults_list, defaults); 174 TAILQ_HEAD(userspec_list, userspec); 175 TAILQ_HEAD(member_list, member); 176 TAILQ_HEAD(privilege_list, privilege); 177 TAILQ_HEAD(cmndspec_list, cmndspec); 178 TAILQ_HEAD(command_digest_list, command_digest); 179 STAILQ_HEAD(comment_list, sudoers_comment); 180 181 /* 182 * Structure describing a user specification and list thereof. 183 */ 184 struct userspec { 185 TAILQ_ENTRY(userspec) entries; 186 struct member_list users; /* list of users */ 187 struct privilege_list privileges; /* list of privileges */ 188 struct comment_list comments; /* optional comments */ 189 int line; /* line number in sudoers */ 190 int column; /* column number in sudoers */ 191 char *file; /* name of sudoers file */ 192 }; 193 194 /* 195 * Structure describing a privilege specification. 196 */ 197 struct privilege { 198 TAILQ_ENTRY(privilege) entries; 199 char *ldap_role; /* LDAP sudoRole */ 200 struct member_list hostlist; /* list of hosts */ 201 struct cmndspec_list cmndlist; /* list of Cmnd_Specs */ 202 struct defaults_list defaults; /* list of sudoOptions */ 203 }; 204 205 /* 206 * A command with option args and digest. 207 * XXX - merge into struct member 208 */ 209 struct sudo_command { 210 char *cmnd; 211 char *args; 212 struct command_digest_list digests; 213 }; 214 215 /* 216 * Structure describing a linked list of Cmnd_Specs. 217 * XXX - include struct command_options instead of its contents inline 218 */ 219 struct cmndspec { 220 TAILQ_ENTRY(cmndspec) entries; 221 struct member_list *runasuserlist; /* list of runas users */ 222 struct member_list *runasgrouplist; /* list of runas groups */ 223 struct member *cmnd; /* command to allow/deny */ 224 struct cmndtag tags; /* tag specificaion */ 225 int timeout; /* command timeout */ 226 time_t notbefore; /* time restriction */ 227 time_t notafter; /* time restriction */ 228 char *runcwd; /* working directory */ 229 char *runchroot; /* root directory */ 230 #ifdef HAVE_SELINUX 231 char *role, *type; /* SELinux role and type */ 232 #endif 233 #ifdef HAVE_PRIV_SET 234 char *privs, *limitprivs; /* Solaris privilege sets */ 235 #endif 236 }; 237 238 /* 239 * Generic structure to hold users, hosts, commands. 240 */ 241 struct member { 242 TAILQ_ENTRY(member) entries; 243 char *name; /* member name */ 244 short type; /* type (see gram.h) */ 245 short negated; /* negated via '!'? */ 246 }; 247 248 struct runascontainer { 249 struct member *runasusers; 250 struct member *runasgroups; 251 }; 252 253 struct sudoers_comment { 254 STAILQ_ENTRY(sudoers_comment) entries; 255 char *str; 256 }; 257 258 /* 259 * Generic structure to hold {User,Host,Runas,Cmnd}_Alias 260 * Aliases are stored in a red-black tree, sorted by name and type. 261 */ 262 struct alias { 263 char *name; /* alias name */ 264 unsigned short type; /* {USER,HOST,RUNAS,CMND}ALIAS */ 265 short used; /* "used" flag for cycle detection */ 266 int line; /* line number of alias entry */ 267 int column; /* column number of alias entry */ 268 char *file; /* file the alias entry was in */ 269 struct member_list members; /* list of alias members */ 270 }; 271 272 /* 273 * Structure describing a Defaults entry in sudoers. 274 */ 275 struct defaults { 276 TAILQ_ENTRY(defaults) entries; 277 char *var; /* variable name */ 278 char *val; /* variable value */ 279 struct member_list *binding; /* user/host/runas binding */ 280 char *file; /* file Defaults entry was in */ 281 short type; /* DEFAULTS{,_USER,_RUNAS,_HOST} */ 282 char op; /* true, false, '+', '-' */ 283 char error; /* parse error flag */ 284 int line; /* line number of Defaults entry */ 285 int column; /* column number of Defaults entry */ 286 }; 287 288 /* 289 * Parsed sudoers policy. 290 */ 291 struct sudoers_parse_tree { 292 struct userspec_list userspecs; 293 struct defaults_list defaults; 294 struct rbtree *aliases; 295 const char *shost, *lhost; 296 }; 297 298 /* 299 * Info about the command being resolved. 300 */ 301 struct cmnd_info { 302 struct stat cmnd_stat; 303 char *cmnd_path; 304 int status; 305 bool intercepted; 306 }; 307 308 /* 309 * The parser passes pointers to data structures that are not stored anywhere. 310 * We add them to the leak list at allocation time and remove them from 311 * the list when they are stored in another data structure. 312 * This makes it possible to free data on error that would otherwise be leaked. 313 */ 314 enum parser_leak_types { 315 LEAK_UNKNOWN, 316 LEAK_PRIVILEGE, 317 LEAK_CMNDSPEC, 318 LEAK_DEFAULTS, 319 LEAK_MEMBER, 320 LEAK_DIGEST, 321 LEAK_RUNAS, 322 LEAK_PTR 323 }; 324 struct parser_leak_entry { 325 SLIST_ENTRY(parser_leak_entry) entries; 326 enum parser_leak_types type; 327 union { 328 struct command_digest *dig; 329 struct privilege *p; 330 struct cmndspec *cs; 331 struct defaults *d; 332 struct member *m; 333 struct runascontainer *rc; 334 void *ptr; 335 } u; 336 }; 337 SLIST_HEAD(parser_leak_list, parser_leak_entry); 338 339 /* alias.c */ 340 struct rbtree *alloc_aliases(void); 341 void free_aliases(struct rbtree *aliases); 342 bool no_aliases(struct sudoers_parse_tree *parse_tree); 343 bool alias_add(struct sudoers_parse_tree *parse_tree, char *name, int type, char *file, int line, int column, struct member *members); 344 const char *alias_type_to_string(int alias_type); 345 struct alias *alias_get(struct sudoers_parse_tree *parse_tree, const char *name, int type); 346 struct alias *alias_remove(struct sudoers_parse_tree *parse_tree, char *name, int type); 347 bool alias_find_used(struct sudoers_parse_tree *parse_tree, struct rbtree *used_aliases); 348 void alias_apply(struct sudoers_parse_tree *parse_tree, int (*func)(struct sudoers_parse_tree *, struct alias *, void *), void *cookie); 349 void alias_free(void *a); 350 void alias_put(struct alias *a); 351 352 /* check_aliases.c */ 353 int check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet, int (*cb_unused)(struct sudoers_parse_tree *, struct alias *, void *)); 354 355 /* gram.c */ 356 extern struct sudoers_parse_tree parsed_policy; 357 bool init_parser(const char *path, bool quiet, bool strict); 358 void free_member(struct member *m); 359 void free_members(struct member_list *members); 360 void free_cmndspecs(struct cmndspec_list *csl); 361 void free_privilege(struct privilege *priv); 362 void free_userspec(struct userspec *us); 363 void free_userspecs(struct userspec_list *usl); 364 void free_default(struct defaults *def, struct member_list **binding); 365 void free_defaults(struct defaults_list *defs); 366 void init_parse_tree(struct sudoers_parse_tree *parse_tree, const char *lhost, const char *shost); 367 void free_parse_tree(struct sudoers_parse_tree *parse_tree); 368 void reparent_parse_tree(struct sudoers_parse_tree *new_tree); 369 bool parser_leak_add(enum parser_leak_types type, void *v); 370 bool parser_leak_remove(enum parser_leak_types type, void *v); 371 void parser_leak_init(void); 372 373 /* match_addr.c */ 374 bool addr_matches(char *n); 375 376 /* match_command.c */ 377 bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const char *runchroot, struct cmnd_info *info, const struct command_digest_list *digests); 378 379 /* match_digest.c */ 380 bool digest_matches(int fd, const char *path, const char *runchroot, const struct command_digest_list *digests); 381 382 /* match.c */ 383 struct group; 384 struct passwd; 385 bool group_matches(const char *sudoers_group, const struct group *gr); 386 bool hostname_matches(const char *shost, const char *lhost, const char *pattern); 387 bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user); 388 bool usergr_matches(const char *group, const char *user, const struct passwd *pw); 389 bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw); 390 int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot, struct cmnd_info *info); 391 int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list, const char *runchroot, struct cmnd_info *info); 392 int host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m); 393 int hostlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list); 394 int runaslist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group); 395 int user_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member *m); 396 int userlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list); 397 const char *sudo_getdomainname(void); 398 struct gid_list *runas_getgroups(void); 399 400 /* toke.c */ 401 void init_lexer(void); 402 403 /* hexchar.c */ 404 int hexchar(const char *s); 405 406 /* base64.c */ 407 size_t base64_decode(const char *str, unsigned char *dst, size_t dsize); 408 size_t base64_encode(const unsigned char *in, size_t in_len, char *out, size_t out_len); 409 410 /* timeout.c */ 411 int parse_timeout(const char *timestr); 412 413 /* gmtoff.c */ 414 long get_gmtoff(time_t *clock); 415 416 /* gentime.c */ 417 time_t parse_gentime(const char *expstr); 418 419 /* filedigest.c */ 420 unsigned char *sudo_filedigest(int fd, const char *file, int digest_type, size_t *digest_len); 421 422 /* digestname.c */ 423 const char *digest_type_to_name(int digest_type); 424 425 /* parse.c */ 426 struct sudo_nss_list; 427 int sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status, int pwflag); 428 int display_privs(struct sudo_nss_list *snl, struct passwd *pw, bool verbose); 429 int display_cmnd(struct sudo_nss_list *snl, struct passwd *pw); 430 431 /* parse_ldif.c */ 432 bool sudoers_parse_ldif(struct sudoers_parse_tree *parse_tree, FILE *fp, const char *sudoers_base, bool store_options); 433 434 /* fmtsudoers.c */ 435 struct sudo_lbuf; 436 bool sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct cmndspec *cs, struct cmndspec *prev_cs, struct cmndtag tags, bool expand_aliases); 437 bool sudoers_format_default(struct sudo_lbuf *lbuf, struct defaults *d); 438 bool sudoers_format_default_line(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct defaults *d, struct defaults **next, bool expand_aliases); 439 bool sudoers_format_member(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct member *m, const char *separator, int alias_type); 440 bool sudoers_format_privilege(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct privilege *priv, bool expand_aliases); 441 bool sudoers_format_userspec(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, struct userspec *us, bool expand_aliases); 442 bool sudoers_format_userspecs(struct sudo_lbuf *lbuf, struct sudoers_parse_tree *parse_tree, const char *separator, bool expand_aliases, bool flush); 443 bool sudoers_defaults_to_tags(const char *var, const char *val, int op, struct cmndtag *tags); 444 bool sudoers_defaults_list_to_tags(struct defaults_list *defs, struct cmndtag *tags); 445 446 #endif /* SUDOERS_PARSE_H */ 447