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