1 /*
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2004-2005, 2007-2020 Todd C. Miller <Todd.Miller@sudo.ws>
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 /*
20 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
22 */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "sudoers.h"
32 #include "sudo_lbuf.h"
33 #include <gram.h>
34
35 /*
36 * Write a privilege to lbuf in sudoers format.
37 */
38 bool
sudoers_format_privilege(struct sudo_lbuf * lbuf,struct sudoers_parse_tree * parse_tree,struct privilege * priv,bool expand_aliases)39 sudoers_format_privilege(struct sudo_lbuf *lbuf,
40 struct sudoers_parse_tree *parse_tree, struct privilege *priv,
41 bool expand_aliases)
42 {
43 struct cmndspec *cs, *prev_cs;
44 struct cmndtag tags;
45 struct member *m;
46 debug_decl(sudoers_format_privilege, SUDOERS_DEBUG_UTIL);
47
48 /* Convert per-privilege defaults to tags. */
49 sudoers_defaults_list_to_tags(&priv->defaults, &tags);
50
51 /* Print hosts list. */
52 TAILQ_FOREACH(m, &priv->hostlist, entries) {
53 if (m != TAILQ_FIRST(&priv->hostlist))
54 sudo_lbuf_append(lbuf, ", ");
55 sudoers_format_member(lbuf, parse_tree, m, ", ",
56 expand_aliases ? HOSTALIAS : UNSPEC);
57 }
58
59 /* Print commands. */
60 sudo_lbuf_append(lbuf, " = ");
61 prev_cs = NULL;
62 TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
63 if (prev_cs == NULL || RUNAS_CHANGED(cs, prev_cs)) {
64 if (cs != TAILQ_FIRST(&priv->cmndlist))
65 sudo_lbuf_append(lbuf, ", ");
66 if (cs->runasuserlist != NULL || cs->runasgrouplist != NULL)
67 sudo_lbuf_append(lbuf, "(");
68 if (cs->runasuserlist != NULL) {
69 TAILQ_FOREACH(m, cs->runasuserlist, entries) {
70 if (m != TAILQ_FIRST(cs->runasuserlist))
71 sudo_lbuf_append(lbuf, ", ");
72 sudoers_format_member(lbuf, parse_tree, m, ", ",
73 expand_aliases ? RUNASALIAS : UNSPEC);
74 }
75 }
76 if (cs->runasgrouplist != NULL) {
77 sudo_lbuf_append(lbuf, " : ");
78 TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
79 if (m != TAILQ_FIRST(cs->runasgrouplist))
80 sudo_lbuf_append(lbuf, ", ");
81 sudoers_format_member(lbuf, parse_tree, m, ", ",
82 expand_aliases ? RUNASALIAS : UNSPEC);
83 }
84 }
85 if (cs->runasuserlist != NULL || cs->runasgrouplist != NULL)
86 sudo_lbuf_append(lbuf, ") ");
87 } else if (cs != TAILQ_FIRST(&priv->cmndlist)) {
88 sudo_lbuf_append(lbuf, ", ");
89 }
90 sudoers_format_cmndspec(lbuf, parse_tree, cs, prev_cs, tags,
91 expand_aliases);
92 prev_cs = cs;
93 }
94
95 debug_return_bool(!sudo_lbuf_error(lbuf));
96 }
97
98 /*
99 * Write a userspec to lbuf in sudoers format.
100 */
101 bool
sudoers_format_userspec(struct sudo_lbuf * lbuf,struct sudoers_parse_tree * parse_tree,struct userspec * us,bool expand_aliases)102 sudoers_format_userspec(struct sudo_lbuf *lbuf,
103 struct sudoers_parse_tree *parse_tree,
104 struct userspec *us, bool expand_aliases)
105 {
106 struct privilege *priv;
107 struct sudoers_comment *comment;
108 struct member *m;
109 debug_decl(sudoers_format_userspec, SUDOERS_DEBUG_UTIL);
110
111 /* Print comments (if any). */
112 STAILQ_FOREACH(comment, &us->comments, entries) {
113 sudo_lbuf_append(lbuf, "# %s\n", comment->str);
114 }
115
116 /* Print users list. */
117 TAILQ_FOREACH(m, &us->users, entries) {
118 if (m != TAILQ_FIRST(&us->users))
119 sudo_lbuf_append(lbuf, ", ");
120 sudoers_format_member(lbuf, parse_tree, m, ", ",
121 expand_aliases ? USERALIAS : UNSPEC);
122 }
123
124 TAILQ_FOREACH(priv, &us->privileges, entries) {
125 if (priv != TAILQ_FIRST(&us->privileges))
126 sudo_lbuf_append(lbuf, " : ");
127 else
128 sudo_lbuf_append(lbuf, " ");
129 if (!sudoers_format_privilege(lbuf, parse_tree, priv, expand_aliases))
130 break;
131 }
132 sudo_lbuf_append(lbuf, "\n");
133
134 debug_return_bool(!sudo_lbuf_error(lbuf));
135 }
136
137 /*
138 * Write a userspec_list to lbuf in sudoers format.
139 */
140 bool
sudoers_format_userspecs(struct sudo_lbuf * lbuf,struct sudoers_parse_tree * parse_tree,const char * separator,bool expand_aliases,bool flush)141 sudoers_format_userspecs(struct sudo_lbuf *lbuf,
142 struct sudoers_parse_tree *parse_tree, const char *separator,
143 bool expand_aliases, bool flush)
144 {
145 struct userspec *us;
146 debug_decl(sudoers_format_userspecs, SUDOERS_DEBUG_UTIL);
147
148 TAILQ_FOREACH(us, &parse_tree->userspecs, entries) {
149 if (separator != NULL && us != TAILQ_FIRST(&parse_tree->userspecs))
150 sudo_lbuf_append(lbuf, "%s", separator);
151 if (!sudoers_format_userspec(lbuf, parse_tree, us, expand_aliases))
152 break;
153 sudo_lbuf_print(lbuf);
154 }
155
156 debug_return_bool(!sudo_lbuf_error(lbuf));
157 }
158
159 /*
160 * Format and append a defaults line to the specified lbuf.
161 * If next, is specified, it must point to the next defaults
162 * entry in the list; this is used to print multiple defaults
163 * entries with the same binding on a single line.
164 */
165 bool
sudoers_format_default_line(struct sudo_lbuf * lbuf,struct sudoers_parse_tree * parse_tree,struct defaults * d,struct defaults ** next,bool expand_aliases)166 sudoers_format_default_line(struct sudo_lbuf *lbuf,
167 struct sudoers_parse_tree *parse_tree, struct defaults *d,
168 struct defaults **next, bool expand_aliases)
169 {
170 struct member *m;
171 int alias_type;
172 debug_decl(sudoers_format_default_line, SUDOERS_DEBUG_UTIL);
173
174 /* Print Defaults type and binding (if present) */
175 switch (d->type) {
176 case DEFAULTS_HOST:
177 sudo_lbuf_append(lbuf, "Defaults@");
178 alias_type = expand_aliases ? HOSTALIAS : UNSPEC;
179 break;
180 case DEFAULTS_USER:
181 sudo_lbuf_append(lbuf, "Defaults:");
182 alias_type = expand_aliases ? USERALIAS : UNSPEC;
183 break;
184 case DEFAULTS_RUNAS:
185 sudo_lbuf_append(lbuf, "Defaults>");
186 alias_type = expand_aliases ? RUNASALIAS : UNSPEC;
187 break;
188 case DEFAULTS_CMND:
189 sudo_lbuf_append(lbuf, "Defaults!");
190 alias_type = expand_aliases ? CMNDALIAS : UNSPEC;
191 break;
192 default:
193 sudo_lbuf_append(lbuf, "Defaults");
194 alias_type = UNSPEC;
195 break;
196 }
197 TAILQ_FOREACH(m, d->binding, entries) {
198 if (m != TAILQ_FIRST(d->binding))
199 sudo_lbuf_append(lbuf, ", ");
200 sudoers_format_member(lbuf, parse_tree, m, ", ", alias_type);
201 }
202
203 sudo_lbuf_append(lbuf, " ");
204 sudoers_format_default(lbuf, d);
205
206 if (next != NULL) {
207 /* Merge Defaults with the same binding, there may be multiple. */
208 struct defaults *n;
209 while ((n = TAILQ_NEXT(d, entries)) && d->binding == n->binding) {
210 sudo_lbuf_append(lbuf, ", ");
211 sudoers_format_default(lbuf, n);
212 d = n;
213 }
214 *next = n;
215 }
216 sudo_lbuf_append(lbuf, "\n");
217
218 debug_return_bool(!sudo_lbuf_error(lbuf));
219 }
220