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