1: /*
2: * Copyright (c) 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com>
3: *
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.
7: *
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: */
16:
17: #include <config.h>
18:
19: #include <sys/types.h>
20: #include <sys/param.h>
21: #include <sys/stat.h>
22:
23: #include <stdio.h>
24: #ifdef STDC_HEADERS
25: # include <stdlib.h>
26: # include <stddef.h>
27: #else
28: # ifdef HAVE_STDLIB_H
29: # include <stdlib.h>
30: # endif
31: #endif /* STDC_HEADERS */
32: #ifdef HAVE_STRING_H
33: # include <string.h>
34: #endif /* HAVE_STRING_H */
35: #ifdef HAVE_STRINGS_H
36: # include <strings.h>
37: #endif /* HAVE_STRINGS_H */
38: #ifdef HAVE_UNISTD_H
39: # include <unistd.h>
40: #endif /* HAVE_UNISTD_H */
41: #include <pwd.h>
42: #include <grp.h>
43: #include <ctype.h>
44:
45: #include "sudoers.h"
46: #include "lbuf.h"
47:
48: extern struct sudo_nss sudo_nss_file;
49: #ifdef HAVE_LDAP
50: extern struct sudo_nss sudo_nss_ldap;
51: #endif
52: #ifdef HAVE_SSSD
53: extern struct sudo_nss sudo_nss_sss;
54: #endif
55:
56: #if (defined(HAVE_LDAP) || defined(HAVE_SSSD)) && defined(_PATH_NSSWITCH_CONF)
57: /*
58: * Read in /etc/nsswitch.conf
59: * Returns a tail queue of matches.
60: */
61: struct sudo_nss_list *
62: sudo_read_nss(void)
63: {
64: FILE *fp;
65: char *cp;
66: #ifdef HAVE_SSSD
67: bool saw_sss = false;
68: #endif
69: bool saw_files = false;
70: bool saw_ldap = false;
71: bool got_match = false;
72: static struct sudo_nss_list snl;
73: debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
74:
75: if ((fp = fopen(_PATH_NSSWITCH_CONF, "r")) == NULL)
76: goto nomatch;
77:
78: while ((cp = sudo_parseln(fp)) != NULL) {
79: /* Skip blank or comment lines */
80: if (*cp == '\0')
81: continue;
82:
83: /* Look for a line starting with "sudoers:" */
84: if (strncasecmp(cp, "sudoers:", 8) != 0)
85: continue;
86:
87: /* Parse line */
88: for ((cp = strtok(cp + 8, " \t")); cp != NULL; (cp = strtok(NULL, " \t"))) {
89: if (strcasecmp(cp, "files") == 0 && !saw_files) {
90: tq_append(&snl, &sudo_nss_file);
91: got_match = true;
92: } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) {
93: tq_append(&snl, &sudo_nss_ldap);
94: got_match = true;
95: #ifdef HAVE_SSSD
96: } else if (strcasecmp(cp, "sss") == 0 && !saw_sss) {
97: tq_append(&snl, &sudo_nss_sss);
98: got_match = true;
99: #endif
100: } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) {
101: /* NOTFOUND affects the most recent entry */
102: tq_last(&snl)->ret_if_notfound = true;
103: got_match = false;
104: } else if (strcasecmp(cp, "[SUCCESS=return]") == 0 && got_match) {
105: /* SUCCESS affects the most recent entry */
106: tq_last(&snl)->ret_if_found = true;
107: got_match = false;
108: } else
109: got_match = false;
110: }
111: /* Only parse the first "sudoers:" line */
112: break;
113: }
114: fclose(fp);
115:
116: nomatch:
117: /* Default to files only if no matches */
118: if (tq_empty(&snl))
119: tq_append(&snl, &sudo_nss_file);
120:
121: debug_return_ptr(&snl);
122: }
123:
124: #else /* (HAVE_LDAP || HAVE_SSSD) && _PATH_NSSWITCH_CONF */
125:
126: # if (defined(HAVE_LDAP) || defined(HAVE_SSSD)) && defined(_PATH_NETSVC_CONF)
127:
128: /*
129: * Read in /etc/netsvc.conf (like nsswitch.conf on AIX)
130: * Returns a tail queue of matches.
131: */
132: struct sudo_nss_list *
133: sudo_read_nss(void)
134: {
135: FILE *fp;
136: char *cp, *ep;
137: #ifdef HAVE_SSSD
138: bool saw_sss = false;
139: #endif
140: bool saw_files = false;
141: bool saw_ldap = false;
142: bool got_match = false;
143: static struct sudo_nss_list snl;
144: debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
145:
146: if ((fp = fopen(_PATH_NETSVC_CONF, "r")) == NULL)
147: goto nomatch;
148:
149: while ((cp = sudo_parseln(fp)) != NULL) {
150: /* Skip blank or comment lines */
151: if (*cp == '\0')
152: continue;
153:
154: /* Look for a line starting with "sudoers = " */
155: if (strncasecmp(cp, "sudoers", 7) != 0)
156: continue;
157: cp += 7;
158: while (isspace((unsigned char)*cp))
159: cp++;
160: if (*cp++ != '=')
161: continue;
162:
163: /* Parse line */
164: for ((cp = strtok(cp, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
165: /* Trim leading whitespace. */
166: while (isspace((unsigned char)*cp))
167: cp++;
168:
169: if (!saw_files && strncasecmp(cp, "files", 5) == 0 &&
170: (isspace((unsigned char)cp[5]) || cp[5] == '\0')) {
171: tq_append(&snl, &sudo_nss_file);
172: got_match = true;
173: ep = &cp[5];
174: } else if (!saw_ldap && strncasecmp(cp, "ldap", 4) == 0 &&
175: (isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
176: tq_append(&snl, &sudo_nss_ldap);
177: got_match = true;
178: ep = &cp[4];
179: #ifdef HAVE_SSSD
180: } else if (!saw_sss && strncasecmp(cp, "sss", 3) == 0 &&
181: (isspace((unsigned char)cp[3]) || cp[3] == '\0')) {
182: tq_append(&snl, &sudo_nss_sss);
183: got_match = true;
184: ep = &cp[3];
185: #endif
186: } else {
187: got_match = false;
188: }
189:
190: /* check for = auth qualifier */
191: if (got_match && *ep) {
192: cp = ep;
193: while (isspace((unsigned char)*cp) || *cp == '=')
194: cp++;
195: if (strncasecmp(cp, "auth", 4) == 0 &&
196: (isspace((unsigned char)cp[4]) || cp[4] == '\0')) {
197: tq_last(&snl)->ret_if_found = true;
198: }
199: }
200: }
201: /* Only parse the first "sudoers" line */
202: break;
203: }
204: fclose(fp);
205:
206: nomatch:
207: /* Default to files only if no matches */
208: if (tq_empty(&snl))
209: tq_append(&snl, &sudo_nss_file);
210:
211: debug_return_ptr(&snl);
212: }
213:
214: # else /* !_PATH_NETSVC_CONF && !_PATH_NSSWITCH_CONF */
215:
216: /*
217: * Non-nsswitch.conf version with hard-coded order.
218: */
219: struct sudo_nss_list *
220: sudo_read_nss(void)
221: {
222: static struct sudo_nss_list snl;
223: debug_decl(sudo_read_nss, SUDO_DEBUG_NSS)
224:
225: # ifdef HAVE_SSSD
226: tq_append(&snl, &sudo_nss_sss);
227: # endif
228: # ifdef HAVE_LDAP
229: tq_append(&snl, &sudo_nss_ldap);
230: # endif
231: tq_append(&snl, &sudo_nss_file);
232:
233: debug_return_ptr(&snl);
234: }
235:
236: # endif /* !HAVE_LDAP || !_PATH_NETSVC_CONF */
237:
238: #endif /* HAVE_LDAP && _PATH_NSSWITCH_CONF */
239:
240: static int
241: output(const char *buf)
242: {
243: struct sudo_conv_message msg;
244: struct sudo_conv_reply repl;
245: debug_decl(output, SUDO_DEBUG_NSS)
246:
247: /* Call conversation function */
248: memset(&msg, 0, sizeof(msg));
249: msg.msg_type = SUDO_CONV_INFO_MSG;
250: msg.msg = buf;
251: memset(&repl, 0, sizeof(repl));
252: if (sudo_conv(1, &msg, &repl) == -1)
253: debug_return_int(0);
254: debug_return_int(strlen(buf));
255: }
256:
257: /*
258: * Print out privileges for the specified user.
259: * We only get here if the user is allowed to run something on this host.
260: */
261: void
262: display_privs(struct sudo_nss_list *snl, struct passwd *pw)
263: {
264: struct sudo_nss *nss;
265: struct lbuf defs, privs;
266: struct stat sb;
267: int cols, count, olen;
268: debug_decl(display_privs, SUDO_DEBUG_NSS)
269:
270: cols = sudo_user.cols;
271: if (fstat(STDOUT_FILENO, &sb) == 0 && S_ISFIFO(sb.st_mode))
272: cols = 0;
273: lbuf_init(&defs, output, 4, NULL, cols);
274: lbuf_init(&privs, output, 4, NULL, cols);
275:
276: /* Display defaults from all sources. */
277: lbuf_append(&defs, _("Matching Defaults entries for %s on this host:\n"),
278: pw->pw_name);
279: count = 0;
280: tq_foreach_fwd(snl, nss) {
281: count += nss->display_defaults(nss, pw, &defs);
282: }
283: if (count)
284: lbuf_append(&defs, "\n\n");
285: else
286: defs.len = 0;
287:
288: /* Display Runas and Cmnd-specific defaults from all sources. */
289: olen = defs.len;
290: lbuf_append(&defs, _("Runas and Command-specific defaults for %s:\n"),
291: pw->pw_name);
292: count = 0;
293: tq_foreach_fwd(snl, nss) {
294: count += nss->display_bound_defaults(nss, pw, &defs);
295: }
296: if (count)
297: lbuf_append(&defs, "\n\n");
298: else
299: defs.len = olen;
300:
301: /* Display privileges from all sources. */
302: lbuf_append(&privs,
303: _("User %s may run the following commands on this host:\n"),
304: pw->pw_name);
305: count = 0;
306: tq_foreach_fwd(snl, nss) {
307: count += nss->display_privs(nss, pw, &privs);
308: }
309: if (count == 0) {
310: defs.len = 0;
311: privs.len = 0;
312: lbuf_append(&privs, _("User %s is not allowed to run sudo on %s.\n"),
313: pw->pw_name, user_shost);
314: }
315: lbuf_print(&defs);
316: lbuf_print(&privs);
317:
318: lbuf_destroy(&defs);
319: lbuf_destroy(&privs);
320:
321: debug_return;
322: }
323:
324: /*
325: * Check user_cmnd against sudoers and print the matching entry if the
326: * command is allowed.
327: * Returns true if the command is allowed, else false.
328: */
329: bool
330: display_cmnd(struct sudo_nss_list *snl, struct passwd *pw)
331: {
332: struct sudo_nss *nss;
333: debug_decl(display_cmnd, SUDO_DEBUG_NSS)
334:
335: tq_foreach_fwd(snl, nss) {
336: if (nss->display_cmnd(nss, pw) == 0)
337: debug_return_bool(true);
338: }
339: debug_return_bool(false);
340: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>