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