~vorlon/ubuntu/natty/sudo/keep_home_by_default

« back to all changes in this revision

Viewing changes to parse.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-05-11 18:07:03 UTC
  • mfrom: (1.1.6 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090511180703-vl2t8cem14g6r61c
Tags: 1.7.0-1ubuntu1
* Merge from debian unstable, remaining changes:
 - debian/rules: Disable lecture, enable tty_tickets by default. (Ubuntu
   specific)
 - Add debian/sudo_root.8: Explanation of root handling through sudo.
   Install it in debian/rules. (Ubuntu specific)
 - sudo.c: If the user successfully authenticated and he is in the 'admin'
   group, then create a stamp ~/.sudo_as_admin_successful. Our default bash
   profile checks for this and displays a short intro about sudo if the
   flag is not present. (Ubuntu specific)
 - env.c: Add "http_proxy" to initial_keepenv_table, so that it is kept
   for "sudo apt-get ...". (Ubuntu specific EBW hack, should disappear at
   some point)
 - debian/{rules,postinst,sudo-ldap.postinst}: Disable init script
   installation. Debian reintroduced it because /var/run tmpfs is not the
   default there, but has been on Ubuntu for ages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (c) 1996, 1998-2005, 2007
3
 
 *      Todd C. Miller <Todd.Miller@courtesan.com>
 
2
 * Copyright (c) 2004-2005, 2007-2008 Todd C. Miller <Todd.Miller@courtesan.com>
4
3
 *
5
4
 * Permission to use, copy, modify, and distribute this software for any
6
5
 * purpose with or without fee is hereby granted, provided that the above
15
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
15
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17
16
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
 
 *
19
 
 * Sponsored in part by the Defense Advanced Research Projects
20
 
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21
 
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22
17
 */
23
18
 
24
19
#include <config.h>
25
20
 
26
21
#include <sys/types.h>
27
22
#include <sys/param.h>
28
 
#include <sys/socket.h>
29
 
#include <sys/stat.h>
30
23
#include <stdio.h>
31
24
#ifdef STDC_HEADERS
32
25
# include <stdlib.h>
46
39
#ifdef HAVE_UNISTD_H
47
40
# include <unistd.h>
48
41
#endif /* HAVE_UNISTD_H */
49
 
#ifdef HAVE_FNMATCH
50
 
# include <fnmatch.h>
51
 
#endif /* HAVE_FNMATCH */
52
 
#ifdef HAVE_EXTENDED_GLOB
53
 
# include <glob.h>
54
 
#endif /* HAVE_EXTENDED_GLOB */
55
 
#ifdef HAVE_NETGROUP_H
56
 
# include <netgroup.h>
57
 
#endif /* HAVE_NETGROUP_H */
58
42
#include <ctype.h>
59
43
#include <pwd.h>
60
44
#include <grp.h>
61
 
#include <netinet/in.h>
62
 
#include <arpa/inet.h>
63
 
#include <netdb.h>
64
 
#ifdef HAVE_DIRENT_H
65
 
# include <dirent.h>
66
 
# define NAMLEN(dirent) strlen((dirent)->d_name)
67
 
#else
68
 
# define dirent direct
69
 
# define NAMLEN(dirent) (dirent)->d_namlen
70
 
# ifdef HAVE_SYS_NDIR_H
71
 
#  include <sys/ndir.h>
72
 
# endif
73
 
# ifdef HAVE_SYS_DIR_H
74
 
#  include <sys/dir.h>
75
 
# endif
76
 
# ifdef HAVE_NDIR_H
77
 
#  include <ndir.h>
78
 
# endif
79
 
#endif
80
45
 
81
46
#include "sudo.h"
82
47
#include "parse.h"
83
 
#include "interfaces.h"
84
 
 
85
 
#ifndef HAVE_FNMATCH
86
 
# include "emul/fnmatch.h"
87
 
#endif /* HAVE_FNMATCH */
88
 
#ifndef HAVE_EXTENDED_GLOB
89
 
# include "emul/glob.h"
90
 
#endif /* HAVE_EXTENDED_GLOB */
 
48
#include "lbuf.h"
 
49
#include <gram.h>
91
50
 
92
51
#ifndef lint
93
 
__unused static const char rcsid[] = "$Sudo: parse.c,v 1.160.2.16 2008/02/09 14:44:48 millert Exp $";
 
52
__unused static const char rcsid[] = "$Sudo: parse.c,v 1.238 2008/12/09 13:49:55 millert Exp $";
94
53
#endif /* lint */
95
54
 
96
 
/*
97
 
 * Globals
98
 
 */
99
 
int parse_error = FALSE;
100
 
extern int keepall;
101
 
extern FILE *yyin, *yyout;
102
 
 
103
 
/*
104
 
 * Prototypes
105
 
 */
106
 
static int has_meta     __P((char *));
107
 
       void init_parser __P((void));
108
 
 
109
 
/*
110
 
 * Look up the user in the sudoers file and check to see if they are
 
55
/* Characters that must be quoted in sudoers */
 
56
#define SUDOERS_QUOTED  ":\\,=#\""
 
57
 
 
58
/* sudoers nsswitch routines */
 
59
struct sudo_nss sudo_nss_file = {
 
60
    &sudo_nss_file,
 
61
    NULL,
 
62
    sudo_file_open,
 
63
    sudo_file_close,
 
64
    sudo_file_parse,
 
65
    sudo_file_setdefs,
 
66
    sudo_file_lookup,
 
67
    sudo_file_display_cmnd,
 
68
    sudo_file_display_defaults,
 
69
    sudo_file_display_bound_defaults,
 
70
    sudo_file_display_privs
 
71
};
 
72
 
 
73
/*
 
74
 * Parser externs.
 
75
 */
 
76
extern FILE *yyin;
 
77
extern char *errorfile;
 
78
extern int errorlineno, parse_error;
 
79
 
 
80
/*
 
81
 * Local prototypes.
 
82
 */
 
83
static void print_member        __P((struct lbuf *, char *, int, int, int));
 
84
static int display_bound_defaults __P((int, struct lbuf *));
 
85
 
 
86
int
 
87
sudo_file_open(nss)
 
88
    struct sudo_nss *nss;
 
89
{
 
90
    if (def_ignore_local_sudoers)
 
91
        return(-1);
 
92
    nss->handle = open_sudoers(_PATH_SUDOERS, NULL);
 
93
    return(nss->handle ? 0 : -1);
 
94
}
 
95
 
 
96
int
 
97
sudo_file_close(nss)
 
98
    struct sudo_nss *nss;
 
99
{
 
100
    /* Free parser data structures and close sudoers file. */
 
101
    init_parser(NULL, 0);
 
102
    if (nss->handle != NULL) {
 
103
        fclose(nss->handle);
 
104
        nss->handle = NULL;
 
105
        yyin = NULL;
 
106
    }
 
107
    return(0);
 
108
}
 
109
 
 
110
/*
 
111
 * Parse the specified sudoers file.
 
112
 */
 
113
int
 
114
sudo_file_parse(nss)
 
115
    struct sudo_nss *nss;
 
116
{
 
117
    if (nss->handle == NULL)
 
118
        return(-1);
 
119
 
 
120
    init_parser(_PATH_SUDOERS, 0);
 
121
    yyin = nss->handle;
 
122
    if (yyparse() != 0 || parse_error) {
 
123
        log_error(NO_EXIT, "parse error in %s near line %d",
 
124
            errorfile, errorlineno);
 
125
        return(-1);
 
126
    }
 
127
    return(0);
 
128
}
 
129
 
 
130
/*
 
131
 * Wrapper around update_defaults() for nsswitch code.
 
132
 */
 
133
int
 
134
sudo_file_setdefs(nss)
 
135
    struct sudo_nss *nss;
 
136
{
 
137
    if (nss->handle == NULL)
 
138
        return(-1);
 
139
 
 
140
    if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
 
141
        return(-1);
 
142
    return(0);
 
143
}
 
144
 
 
145
/*
 
146
 * Look up the user in the parsed sudoers file and check to see if they are
111
147
 * allowed to run the specified command on this host as the target user.
112
148
 */
113
149
int
114
 
sudoers_lookup(pwflag)
 
150
sudo_file_lookup(nss, validated, pwflag)
 
151
    struct sudo_nss *nss;
 
152
    int validated;
115
153
    int pwflag;
116
154
{
117
 
    int error, nopass;
118
 
 
119
 
    /* We opened _PATH_SUDOERS in check_sudoers() so just rewind it. */
120
 
    rewind(sudoers_fp);
121
 
    yyin = sudoers_fp;
122
 
    yyout = stdout;
123
 
 
124
 
    /* Allocate space for data structures in the parser. */
125
 
    init_parser();
126
 
 
127
 
    /* Keep more state for pseudo-commands so that listpw and verifypw work */
128
 
    if (pwflag > 0)
129
 
        keepall = TRUE;
130
 
 
131
 
    /* Need to be runas user while stat'ing things in the parser. */
132
 
    set_perms(PERM_RUNAS);
133
 
    error = yyparse();
134
 
 
135
 
    /* Close the sudoers file now that we are done with it. */
136
 
    (void) fclose(sudoers_fp);
137
 
    sudoers_fp = NULL;
138
 
 
139
 
    if (error || parse_error) {
140
 
        set_perms(PERM_ROOT);
141
 
        return(VALIDATE_ERROR);
142
 
    }
143
 
 
144
 
    /*
145
 
     * Assume the worst.  If the stack is empty the user was
146
 
     * not mentioned at all.
147
 
     */
148
 
    if (def_authenticate)
149
 
        error = VALIDATE_NOT_OK;
150
 
    else
151
 
        error = VALIDATE_NOT_OK | FLAG_NOPASS;
152
 
    if (pwflag) {
153
 
        SET(error, FLAG_NO_CHECK);
154
 
    } else {
155
 
        SET(error, FLAG_NO_HOST);
156
 
        if (!top)
157
 
            SET(error, FLAG_NO_USER);
158
 
    }
 
155
    int match, host_match, runas_match, cmnd_match;
 
156
    struct cmndspec *cs;
 
157
    struct cmndtag *tags = NULL;
 
158
    struct privilege *priv;
 
159
    struct userspec *us;
 
160
 
 
161
    if (nss->handle == NULL)
 
162
        return(validated);
159
163
 
160
164
    /*
161
165
     * Only check the actual command if pwflag is not set.
162
166
     * It is set for the "validate", "list" and "kill" pseudo-commands.
163
167
     * Always check the host and user.
164
168
     */
165
 
    nopass = -1;
166
169
    if (pwflag) {
167
 
        int found;
 
170
        int nopass;
168
171
        enum def_tupple pwcheck;
169
172
 
170
173
        pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
 
174
        nopass = (pwcheck == all) ? TRUE : FALSE;
171
175
 
 
176
        if (list_pw == NULL)
 
177
            SET(validated, FLAG_NO_CHECK);
 
178
        CLR(validated, FLAG_NO_USER);
 
179
        CLR(validated, FLAG_NO_HOST);
 
180
        match = DENY;
 
181
        tq_foreach_fwd(&userspecs, us) {
 
182
            if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
 
183
                continue;
 
184
            tq_foreach_fwd(&us->privileges, priv) {
 
185
                if (hostlist_matches(&priv->hostlist) != ALLOW)
 
186
                    continue;
 
187
                tq_foreach_fwd(&priv->cmndlist, cs) {
 
188
                    /* Only check the command when listing another user. */
 
189
                    if (user_uid == 0 || list_pw == NULL ||
 
190
                        user_uid == list_pw->pw_uid ||
 
191
                        cmnd_matches(cs->cmnd) == ALLOW)
 
192
                            match = ALLOW;
 
193
                    if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
 
194
                        (pwcheck == all && cs->tags.nopasswd != TRUE))
 
195
                        nopass = cs->tags.nopasswd;
 
196
                }
 
197
            }
 
198
        }
 
199
        if (match == ALLOW || user_uid == 0) {
 
200
            /* User has an entry for this host. */
 
201
            SET(validated, VALIDATE_OK);
 
202
        } else if (match == DENY)
 
203
            SET(validated, VALIDATE_NOT_OK);
172
204
        if (pwcheck == always && def_authenticate)
173
 
            nopass = FLAG_CHECK_USER;
174
 
        else if (pwcheck == never || !def_authenticate)
175
 
            nopass = FLAG_NOPASS;
176
 
        found = 0;
177
 
        while (top) {
178
 
            if (host_matches == TRUE) {
179
 
                found = 1;
180
 
                if (pwcheck == any && no_passwd == TRUE)
181
 
                    nopass = FLAG_NOPASS;
182
 
                else if (pwcheck == all && nopass != 0)
183
 
                    nopass = (no_passwd == TRUE) ? FLAG_NOPASS : 0;
184
 
            }
185
 
            top--;
186
 
        }
187
 
        if (found) {
188
 
            set_perms(PERM_ROOT);
189
 
            if (nopass == -1)
190
 
                nopass = 0;
191
 
            return(VALIDATE_OK | nopass);
192
 
        }
193
 
    } else {
194
 
        while (top) {
195
 
            if (host_matches == TRUE) {
196
 
                CLR(error, FLAG_NO_HOST);
197
 
                if (runas_matches == TRUE && cmnd_matches == TRUE) {
198
 
                    /*
199
 
                     * User was granted access to cmnd on host as user.
200
 
                     */
 
205
            SET(validated, FLAG_CHECK_USER);
 
206
        else if (pwcheck == never || nopass == TRUE)
 
207
            def_authenticate = FALSE;
 
208
        return(validated);
 
209
    }
 
210
 
 
211
    /* Need to be runas user while stat'ing things. */
 
212
    set_perms(PERM_RUNAS);
 
213
 
 
214
    match = UNSPEC;
 
215
    tq_foreach_rev(&userspecs, us) {
 
216
        if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
 
217
            continue;
 
218
        CLR(validated, FLAG_NO_USER);
 
219
        tq_foreach_rev(&us->privileges, priv) {
 
220
            host_match = hostlist_matches(&priv->hostlist);
 
221
            if (host_match == ALLOW)
 
222
                CLR(validated, FLAG_NO_HOST);
 
223
            else
 
224
                continue;
 
225
            tq_foreach_rev(&priv->cmndlist, cs) {
 
226
                runas_match = runaslist_matches(&cs->runasuserlist,
 
227
                    &cs->runasgrouplist);
 
228
                if (runas_match == ALLOW) {
 
229
                    cmnd_match = cmnd_matches(cs->cmnd);
 
230
                    if (cmnd_match != UNSPEC) {
 
231
                        match = cmnd_match;
 
232
                        tags = &cs->tags;
201
233
#ifdef HAVE_SELINUX
202
 
                    /* Set role and type if not specified on command line. */
203
 
                    if (user_role == NULL) {
204
 
                        if (match[top-1].role != NULL)
205
 
                            user_role = match[top-1].role;
206
 
                        else
207
 
                            user_role = def_role;
208
 
                    }
209
 
                    if (user_type == NULL) {
210
 
                        if (match[top-1].type != NULL)
211
 
                            user_type = match[top-1].type;
212
 
                        else
213
 
                            user_type = def_type;
214
 
                    }
215
 
#endif
216
 
                    set_perms(PERM_ROOT);
217
 
                    return(VALIDATE_OK |
218
 
                        (no_passwd == TRUE ? FLAG_NOPASS : 0) |
219
 
                        (no_execve == TRUE ? FLAG_NOEXEC : 0) |
220
 
                        (setenv_ok >= TRUE ? FLAG_SETENV : 0));
221
 
                } else if ((runas_matches == TRUE && cmnd_matches == FALSE) ||
222
 
                    (runas_matches == FALSE && cmnd_matches == TRUE)) {
223
 
                    /*
224
 
                     * User was explicitly denied access to cmnd on host.
225
 
                     */
226
 
                    set_perms(PERM_ROOT);
227
 
                    return(VALIDATE_NOT_OK |
228
 
                        (no_passwd == TRUE ? FLAG_NOPASS : 0) |
229
 
                        (no_execve == TRUE ? FLAG_NOEXEC : 0) |
230
 
                        (setenv_ok >= TRUE ? FLAG_SETENV : 0));
 
234
                        /* Set role and type if not specified on command line. */
 
235
                        if (user_role == NULL)
 
236
                            user_role = cs->role ? estrdup(cs->role) : def_role;
 
237
                        if (user_type == NULL)
 
238
                            user_type = cs->type ? estrdup(cs->type) : def_type;
 
239
#endif /* HAVE_SELINUX */
 
240
                        goto matched2;
 
241
                    }
231
242
                }
232
243
            }
233
 
            top--;
234
 
        }
 
244
        }
 
245
    }
 
246
    matched2:
 
247
    if (match == ALLOW) {
 
248
        SET(validated, VALIDATE_OK);
 
249
        CLR(validated, VALIDATE_NOT_OK);
 
250
        if (tags != NULL) {
 
251
            if (tags->nopasswd != UNSPEC)
 
252
                def_authenticate = !tags->nopasswd;
 
253
            if (tags->noexec != UNSPEC)
 
254
                def_noexec = tags->noexec;
 
255
            if (tags->setenv != UNSPEC)
 
256
                def_setenv = tags->setenv;
 
257
        }
 
258
    } else if (match == DENY) {
 
259
        SET(validated, VALIDATE_NOT_OK);
 
260
        CLR(validated, VALIDATE_OK);
235
261
    }
236
262
    set_perms(PERM_ROOT);
237
 
 
238
 
    /*
239
 
     * The user was neither explicitly granted nor denied access.
240
 
     */
241
 
    if (nopass == -1)
242
 
        nopass = 0;
243
 
    return(error | nopass);
 
263
    return(validated);
 
264
}
 
265
 
 
266
#define TAG_CHANGED(t) \
 
267
        (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
 
268
 
 
269
static void
 
270
sudo_file_append_cmnd(cs, tags, lbuf)
 
271
    struct cmndspec *cs;
 
272
    struct cmndtag *tags;
 
273
    struct lbuf *lbuf;
 
274
{
 
275
    struct member *m;
 
276
 
 
277
#ifdef HAVE_SELINUX
 
278
    if (cs->role)
 
279
        lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
 
280
    if (cs->type)
 
281
        lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
 
282
#endif /* HAVE_SELINUX */
 
283
    if (TAG_CHANGED(setenv)) {
 
284
        lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
 
285
            "NOSETENV: ", NULL);
 
286
        tags->setenv = cs->tags.setenv;
 
287
    }
 
288
    if (TAG_CHANGED(noexec)) {
 
289
        lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " :
 
290
            "EXEC: ", NULL);
 
291
        tags->noexec = cs->tags.noexec;
 
292
    }
 
293
    if (TAG_CHANGED(nopasswd)) {
 
294
        lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " :
 
295
            "PASSWD: ", NULL);
 
296
        tags->nopasswd = cs->tags.nopasswd;
 
297
    }
 
298
    m = cs->cmnd;
 
299
    print_member(lbuf, m->name, m->type, m->negated,
 
300
        CMNDALIAS);
 
301
}
 
302
 
 
303
static int
 
304
sudo_file_display_priv_short(pw, us, lbuf)
 
305
    struct passwd *pw;
 
306
    struct userspec *us;
 
307
    struct lbuf *lbuf;
 
308
{
 
309
    struct cmndspec *cs;
 
310
    struct member *m;
 
311
    struct privilege *priv;
 
312
    struct cmndtag tags;
 
313
    int nfound = 0;
 
314
 
 
315
    tq_foreach_fwd(&us->privileges, priv) {
 
316
        tags.noexec = UNSPEC;
 
317
        tags.setenv = UNSPEC;
 
318
        tags.nopasswd = UNSPEC;
 
319
        lbuf_append(lbuf, "    ", NULL);
 
320
        tq_foreach_fwd(&priv->cmndlist, cs) {
 
321
            if (cs != tq_first(&priv->cmndlist))
 
322
                lbuf_append(lbuf, ", ", NULL);
 
323
            lbuf_append(lbuf, "(", NULL);
 
324
            if (!tq_empty(&cs->runasuserlist)) {
 
325
                tq_foreach_fwd(&cs->runasuserlist, m) {
 
326
                    if (m != tq_first(&cs->runasuserlist))
 
327
                        lbuf_append(lbuf, ", ", NULL);
 
328
                    print_member(lbuf, m->name, m->type, m->negated,
 
329
                        RUNASALIAS);
 
330
                }
 
331
            } else {
 
332
                lbuf_append(lbuf, def_runas_default, NULL);
 
333
            }
 
334
            if (!tq_empty(&cs->runasgrouplist)) {
 
335
                lbuf_append(lbuf, " : ", NULL);
 
336
                tq_foreach_fwd(&cs->runasgrouplist, m) {
 
337
                    if (m != tq_first(&cs->runasgrouplist))
 
338
                        lbuf_append(lbuf, ", ", NULL);
 
339
                    print_member(lbuf, m->name, m->type, m->negated,
 
340
                        RUNASALIAS);
 
341
                }
 
342
            }
 
343
            lbuf_append(lbuf, ") ", NULL);
 
344
            sudo_file_append_cmnd(cs, &tags, lbuf);
 
345
            nfound++;
 
346
        }
 
347
        lbuf_print(lbuf);               /* forces a newline */
 
348
    }
 
349
    return(nfound);
 
350
}
 
351
 
 
352
static int
 
353
sudo_file_display_priv_long(pw, us, lbuf)
 
354
    struct passwd *pw;
 
355
    struct userspec *us;
 
356
    struct lbuf *lbuf;
 
357
{
 
358
    struct cmndspec *cs;
 
359
    struct member *m;
 
360
    struct privilege *priv;
 
361
    struct cmndtag tags;
 
362
    int nfound = 0;
 
363
 
 
364
    tq_foreach_fwd(&us->privileges, priv) {
 
365
        tags.noexec = UNSPEC;
 
366
        tags.setenv = UNSPEC;
 
367
        tags.nopasswd = UNSPEC;
 
368
        lbuf_print(lbuf);       /* force a newline */
 
369
        lbuf_append(lbuf, "Sudoers entry:", NULL);
 
370
        lbuf_print(lbuf);
 
371
        tq_foreach_fwd(&priv->cmndlist, cs) {
 
372
            lbuf_append(lbuf, "    RunAsUsers: ", NULL);
 
373
            if (!tq_empty(&cs->runasuserlist)) {
 
374
                tq_foreach_fwd(&cs->runasuserlist, m) {
 
375
                    if (m != tq_first(&cs->runasuserlist))
 
376
                        lbuf_append(lbuf, ", ", NULL);
 
377
                    print_member(lbuf, m->name, m->type, m->negated,
 
378
                        RUNASALIAS);
 
379
                }
 
380
            } else {
 
381
                lbuf_append(lbuf, def_runas_default, NULL);
 
382
            }
 
383
            lbuf_print(lbuf);
 
384
            if (!tq_empty(&cs->runasgrouplist)) {
 
385
                lbuf_append(lbuf, "    RunAsGroups: ", NULL);
 
386
                tq_foreach_fwd(&cs->runasgrouplist, m) {
 
387
                    if (m != tq_first(&cs->runasgrouplist))
 
388
                        lbuf_append(lbuf, ", ", NULL);
 
389
                    print_member(lbuf, m->name, m->type, m->negated,
 
390
                        RUNASALIAS);
 
391
                }
 
392
                lbuf_print(lbuf);
 
393
            }
 
394
            lbuf_append(lbuf, "    Commands: ", NULL);
 
395
            lbuf_print(lbuf);
 
396
            lbuf_append(lbuf, "\t", NULL);
 
397
            sudo_file_append_cmnd(cs, &tags, lbuf);
 
398
            lbuf_print(lbuf);
 
399
            nfound++;
 
400
        }
 
401
    }
 
402
    return(nfound);
 
403
}
 
404
 
 
405
int
 
406
sudo_file_display_privs(nss, pw, lbuf)
 
407
    struct sudo_nss *nss;
 
408
    struct passwd *pw;
 
409
    struct lbuf *lbuf;
 
410
{
 
411
    struct userspec *us;
 
412
    int nfound = 0;
 
413
 
 
414
    if (nss->handle == NULL)
 
415
        return(-1);
 
416
 
 
417
    tq_foreach_fwd(&userspecs, us) {
 
418
        /* XXX - why only check the first privilege here? */
 
419
        if (userlist_matches(pw, &us->users) != ALLOW ||
 
420
            hostlist_matches(&us->privileges.first->hostlist) != ALLOW)
 
421
            continue;
 
422
 
 
423
        if (long_list)
 
424
            nfound += sudo_file_display_priv_long(pw, us, lbuf);
 
425
        else
 
426
            nfound += sudo_file_display_priv_short(pw, us, lbuf);
 
427
    }
 
428
    return(nfound);
244
429
}
245
430
 
246
431
/*
247
 
 * If path doesn't end in /, return TRUE iff cmnd & path name the same inode;
248
 
 * otherwise, return TRUE if user_cmnd names one of the inodes in path.
 
432
 * Display matching Defaults entries for the given user on this host.
249
433
 */
250
434
int
251
 
command_matches(sudoers_cmnd, sudoers_args)
252
 
    char *sudoers_cmnd;
253
 
    char *sudoers_args;
 
435
sudo_file_display_defaults(nss, pw, lbuf)
 
436
    struct sudo_nss *nss;
 
437
    struct passwd *pw;
 
438
    struct lbuf *lbuf;
254
439
{
255
 
    struct stat sudoers_stat;
256
 
    struct dirent *dent;
257
 
    char **ap, *base, buf[PATH_MAX];
258
 
    glob_t gl;
259
 
    DIR *dirp;
260
 
 
261
 
    /* Check for pseudo-commands */
262
 
    if (strchr(user_cmnd, '/') == NULL) {
263
 
        /*
264
 
         * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
265
 
         *  a) there are no args in sudoers OR
266
 
         *  b) there are no args on command line and none req by sudoers OR
267
 
         *  c) there are args in sudoers and on command line and they match
268
 
         */
269
 
        if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
270
 
            strcmp(user_cmnd, "sudoedit") != 0)
271
 
            return(FALSE);
272
 
        if (!sudoers_args ||
273
 
            (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
274
 
            (sudoers_args &&
275
 
             fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
276
 
            efree(safe_cmnd);
277
 
            safe_cmnd = estrdup(sudoers_cmnd);
278
 
            return(TRUE);
279
 
        } else
280
 
            return(FALSE);
281
 
    }
282
 
 
283
 
    /*
284
 
     * If sudoers_cmnd has meta characters in it, use fnmatch(3)
285
 
     * to do the matching.
286
 
     */
287
 
    if (has_meta(sudoers_cmnd)) {
288
 
        /*
289
 
         * Return true if we find a match in the glob(3) results AND
290
 
         *  a) there are no args in sudoers OR
291
 
         *  b) there are no args on command line and none required by sudoers OR
292
 
         *  c) there are args in sudoers and on command line and they match
293
 
         * else return false.
294
 
         *
295
 
         * Could optimize patterns ending in "/*" to "/user_base"
296
 
         */
297
 
#define GLOB_FLAGS      (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE)
298
 
        if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0) {
299
 
            globfree(&gl);
300
 
            return(FALSE);
301
 
        }
302
 
        /* For each glob match, compare basename, st_dev and st_ino. */
303
 
        for (ap = gl.gl_pathv; *ap != NULL; ap++) {
304
 
            /* only stat if basenames are the same */
305
 
            if ((base = strrchr(*ap, '/')) != NULL)
306
 
                base++;
307
 
            else
308
 
                base = *ap;
309
 
            if (strcmp(user_base, base) != 0 ||
310
 
                stat(*ap, &sudoers_stat) == -1)
 
440
    struct defaults *d;
 
441
    char *prefix = NULL;
 
442
    int nfound = 0;
 
443
 
 
444
    if (nss->handle == NULL)
 
445
        return(-1);
 
446
 
 
447
    if (lbuf->len == 0)
 
448
        prefix = "    ";
 
449
    else
 
450
        prefix = ", ";
 
451
 
 
452
    tq_foreach_fwd(&defaults, d) {
 
453
        switch (d->type) {
 
454
            case DEFAULTS_HOST:
 
455
                if (hostlist_matches(&d->binding) != ALLOW)
 
456
                    continue;
 
457
                break;
 
458
            case DEFAULTS_USER:
 
459
                if (userlist_matches(pw, &d->binding) != ALLOW)
 
460
                    continue;
 
461
                break;
 
462
            case DEFAULTS_RUNAS:
 
463
            case DEFAULTS_CMND:
311
464
                continue;
312
 
            if (user_stat->st_dev == sudoers_stat.st_dev &&
313
 
                user_stat->st_ino == sudoers_stat.st_ino) {
314
 
                efree(safe_cmnd);
315
 
                safe_cmnd = estrdup(*ap);
316
 
                break;
317
 
            }
318
465
        }
319
 
        globfree(&gl);
320
 
        if (*ap == NULL)
321
 
            return(FALSE);
322
 
 
323
 
        if (!sudoers_args ||
324
 
            (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
325
 
            (sudoers_args &&
326
 
             fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
327
 
            efree(safe_cmnd);
328
 
            safe_cmnd = estrdup(user_cmnd);
329
 
            return(TRUE);
330
 
        } else
331
 
            return(FALSE);
332
 
    } else {
333
 
        size_t dlen = strlen(sudoers_cmnd);
334
 
 
335
 
        /*
336
 
         * No meta characters
337
 
         * Check to make sure this is not a directory spec (doesn't end in '/')
338
 
         */
339
 
        if (sudoers_cmnd[dlen - 1] != '/') {
340
 
            /* Only proceed if user_base and basename(sudoers_cmnd) match */
341
 
            if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
342
 
                base = sudoers_cmnd;
343
 
            else
344
 
                base++;
345
 
            if (strcmp(user_base, base) != 0 ||
346
 
                stat(sudoers_cmnd, &sudoers_stat) == -1)
347
 
                return(FALSE);
348
 
 
349
 
            /*
350
 
             * Return true if inode/device matches AND
351
 
             *  a) there are no args in sudoers OR
352
 
             *  b) there are no args on command line and none req by sudoers OR
353
 
             *  c) there are args in sudoers and on command line and they match
354
 
             */
355
 
            if (user_stat->st_dev != sudoers_stat.st_dev ||
356
 
                user_stat->st_ino != sudoers_stat.st_ino)
357
 
                return(FALSE);
358
 
            if (!sudoers_args ||
359
 
                (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
360
 
                (sudoers_args &&
361
 
                 fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
362
 
                efree(safe_cmnd);
363
 
                safe_cmnd = estrdup(sudoers_cmnd);
364
 
                return(TRUE);
 
466
        lbuf_append(lbuf, prefix, NULL);
 
467
        if (d->val != NULL) {
 
468
            lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
 
469
                d->op == '-' ? "-=" : "=", NULL);
 
470
            if (strpbrk(d->val, " \t") != NULL) {
 
471
                lbuf_append(lbuf, "\"", NULL);
 
472
                lbuf_append_quoted(lbuf, "\"", d->val, NULL);
 
473
                lbuf_append(lbuf, "\"", NULL);
365
474
            } else
366
 
                return(FALSE);
367
 
        }
368
 
 
369
 
        /*
370
 
         * Grot through sudoers_cmnd's directory entries, looking for user_base.
371
 
         */
372
 
        dirp = opendir(sudoers_cmnd);
373
 
        if (dirp == NULL)
374
 
            return(FALSE);
375
 
 
376
 
        if (strlcpy(buf, sudoers_cmnd, sizeof(buf)) >= sizeof(buf))
377
 
            return(FALSE);
378
 
        while ((dent = readdir(dirp)) != NULL) {
379
 
            /* ignore paths > PATH_MAX (XXX - log) */
380
 
            buf[dlen] = '\0';
381
 
            if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
382
 
                continue;
383
 
 
384
 
            /* only stat if basenames are the same */
385
 
            if (strcmp(user_base, dent->d_name) != 0 ||
386
 
                stat(buf, &sudoers_stat) == -1)
387
 
                continue;
388
 
            if (user_stat->st_dev == sudoers_stat.st_dev &&
389
 
                user_stat->st_ino == sudoers_stat.st_ino) {
390
 
                efree(safe_cmnd);
391
 
                safe_cmnd = estrdup(buf);
392
 
                break;
393
 
            }
394
 
        }
395
 
 
396
 
        closedir(dirp);
397
 
        return(dent != NULL);
398
 
    }
399
 
}
400
 
 
401
 
static int
402
 
addr_matches_if(n)
403
 
    char *n;
404
 
{
405
 
    int i;
406
 
    struct in_addr addr;
407
 
    struct interface *ifp;
408
 
#ifdef HAVE_IN6_ADDR
409
 
    struct in6_addr addr6;
410
 
    int j;
411
 
#endif
412
 
    int family;
413
 
 
414
 
#ifdef HAVE_IN6_ADDR
415
 
    if (inet_pton(AF_INET6, n, &addr6) > 0) {
416
 
        family = AF_INET6;
417
 
    } else
418
 
#endif
419
 
    {
420
 
        family = AF_INET;
421
 
        addr.s_addr = inet_addr(n);
422
 
    }
423
 
 
424
 
    for (i = 0; i < num_interfaces; i++) {
425
 
        ifp = &interfaces[i];
426
 
        if (ifp->family != family)
427
 
            continue;
428
 
        switch(family) {
429
 
            case AF_INET:
430
 
                if (ifp->addr.ip4.s_addr == addr.s_addr ||
431
 
                    (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
432
 
                    == addr.s_addr)
433
 
                    return(TRUE);
434
 
                break;
435
 
#ifdef HAVE_IN6_ADDR
436
 
            case AF_INET6:
437
 
                if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,
438
 
                    sizeof(addr6.s6_addr)) == 0)
439
 
                    return(TRUE);
440
 
                for (j = 0; j < sizeof(addr6.s6_addr); j++) {
441
 
                    if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])
442
 
                        break;
443
 
                }
444
 
                if (j == sizeof(addr6.s6_addr))
445
 
                    return(TRUE);
446
 
#endif /* HAVE_IN6_ADDR */
447
 
        }
448
 
    }
449
 
 
450
 
    return(FALSE);
451
 
}
452
 
 
453
 
static int
454
 
addr_matches_if_netmask(n, m)
455
 
    char *n;
456
 
    char *m;
457
 
{
458
 
    int i;
459
 
    struct in_addr addr, mask;
460
 
    struct interface *ifp;
461
 
#ifdef HAVE_IN6_ADDR
462
 
    struct in6_addr addr6, mask6;
463
 
    int j;
464
 
#endif
465
 
    int family;
466
 
 
467
 
#ifdef HAVE_IN6_ADDR
468
 
    if (inet_pton(AF_INET6, n, &addr6) > 0)
469
 
        family = AF_INET6;
470
 
    else
471
 
#endif
472
 
    {
473
 
        family = AF_INET;
474
 
        addr.s_addr = inet_addr(n);
475
 
    }
476
 
 
477
 
    if (family == AF_INET) {
478
 
        if (strchr(m, '.'))
479
 
            mask.s_addr = inet_addr(m);
480
 
        else {
481
 
            i = 32 - atoi(m);
482
 
            mask.s_addr = 0xffffffff;
483
 
            mask.s_addr >>= i;
484
 
            mask.s_addr <<= i;
485
 
            mask.s_addr = htonl(mask.s_addr);
486
 
        }
487
 
    }
488
 
#ifdef HAVE_IN6_ADDR
489
 
    else {
490
 
        if (inet_pton(AF_INET6, m, &mask6) <= 0) {
491
 
            j = atoi(m);
492
 
            for (i = 0; i < 16; i++) {
493
 
                if (j < i * 8)
494
 
                    mask6.s6_addr[i] = 0;
495
 
                else if (i * 8 + 8 <= j)
496
 
                    mask6.s6_addr[i] = 0xff;
497
 
                else
498
 
                    mask6.s6_addr[i] = 0xff00 >> (j - i * 8);
499
 
            }
500
 
        }
501
 
    }
502
 
#endif /* HAVE_IN6_ADDR */
503
 
 
504
 
    for (i = 0; i < num_interfaces; i++) {
505
 
        ifp = &interfaces[i];
506
 
        if (ifp->family != family)
507
 
            continue;
508
 
        switch(family) {
509
 
            case AF_INET:
510
 
                if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
511
 
                    return(TRUE);
512
 
#ifdef HAVE_IN6_ADDR
513
 
            case AF_INET6:
514
 
                for (j = 0; j < sizeof(addr6.s6_addr); j++) {
515
 
                    if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])
516
 
                        break;
517
 
                }
518
 
                if (j == sizeof(addr6.s6_addr))
519
 
                    return(TRUE);
520
 
#endif /* HAVE_IN6_ADDR */
521
 
        }
522
 
    }
523
 
 
524
 
    return(FALSE);
525
 
}
526
 
 
527
 
/*
528
 
 * Returns TRUE if "n" is one of our ip addresses or if
529
 
 * "n" is a network that we are on, else returns FALSE.
530
 
 */
531
 
int
532
 
addr_matches(n)
533
 
    char *n;
534
 
{
535
 
    char *m;
536
 
    int retval;
537
 
 
538
 
    /* If there's an explicit netmask, use it. */
539
 
    if ((m = strchr(n, '/'))) {
540
 
        *m++ = '\0';
541
 
        retval = addr_matches_if_netmask(n, m);
542
 
        *(m - 1) = '/';
543
 
    } else
544
 
        retval = addr_matches_if(n);
545
 
 
546
 
    return(retval);
547
 
}
548
 
 
549
 
/*
550
 
 * Returns 0 if the hostname matches the pattern and non-zero otherwise.
551
 
 */
552
 
int
553
 
hostname_matches(shost, lhost, pattern)
554
 
    char *shost;
555
 
    char *lhost;
556
 
    char *pattern;
557
 
{
558
 
    if (has_meta(pattern)) {
559
 
        if (strchr(pattern, '.'))
560
 
            return(fnmatch(pattern, lhost, FNM_CASEFOLD));
561
 
        else
562
 
            return(fnmatch(pattern, shost, FNM_CASEFOLD));
563
 
    } else {
564
 
        if (strchr(pattern, '.'))
565
 
            return(strcasecmp(lhost, pattern));
566
 
        else
567
 
            return(strcasecmp(shost, pattern));
568
 
    }
569
 
}
570
 
 
571
 
/*
572
 
 *  Returns TRUE if the user/uid from sudoers matches the specified user/uid,
573
 
 *  else returns FALSE.
574
 
 */
575
 
int
576
 
userpw_matches(sudoers_user, user, pw)
577
 
    char *sudoers_user;
578
 
    char *user;
579
 
    struct passwd *pw;
580
 
{
581
 
    if (pw != NULL && *sudoers_user == '#') {
582
 
        uid_t uid = atoi(sudoers_user + 1);
583
 
        if (uid == pw->pw_uid)
584
 
            return(1);
585
 
    }
586
 
    return(strcmp(sudoers_user, user) == 0);
587
 
}
588
 
 
589
 
/*
590
 
 *  Returns TRUE if the given user belongs to the named group,
591
 
 *  else returns FALSE.
592
 
 *  XXX - reduce the number of passwd/group lookups
593
 
 */
594
 
int
595
 
usergr_matches(group, user, pw)
596
 
    char *group;
597
 
    char *user;
598
 
    struct passwd *pw;
599
 
{
600
 
    struct group *grp;
601
 
    gid_t pw_gid;
602
 
    char **cur;
603
 
    int i;
604
 
 
605
 
    /* make sure we have a valid usergroup, sudo style */
606
 
    if (*group++ != '%')
607
 
        return(FALSE);
608
 
 
609
 
    /* look up user's primary gid in the passwd file */
610
 
    if (pw == NULL && (pw = getpwnam(user)) == NULL)
611
 
        return(FALSE);
612
 
    pw_gid = pw->pw_gid;
613
 
 
614
 
    if ((grp = getgrnam(group)) == NULL)
615
 
        return(FALSE);
616
 
 
617
 
    /* check against user's primary (passwd file) gid */
618
 
    if (grp->gr_gid == pw_gid)
619
 
        return(TRUE);
620
 
 
621
 
    /*
622
 
     * If the user has a supplementary group vector, check it first.
623
 
     */
624
 
    if (strcmp(user, user_name) == 0) {
625
 
        for (i = 0; i < user_ngroups; i++) {
626
 
            if (grp->gr_gid == user_groups[i])
627
 
                return(TRUE);
628
 
        }
629
 
    }
630
 
    if (grp->gr_mem != NULL) {
631
 
        for (cur = grp->gr_mem; *cur; cur++) {
632
 
            if (strcmp(*cur, user) == 0)
633
 
                return(TRUE);
634
 
        }
635
 
    }
636
 
 
637
 
    return(FALSE);
638
 
}
639
 
 
640
 
/*
641
 
 * Returns TRUE if "host" and "user" belong to the netgroup "netgr",
642
 
 * else return FALSE.  Either of "host", "shost" or "user" may be NULL
643
 
 * in which case that argument is not checked...
644
 
 */
645
 
int
646
 
netgr_matches(netgr, host, shost, user)
647
 
    char *netgr;
648
 
    char *host;
649
 
    char *shost;
650
 
    char *user;
651
 
{
652
 
    static char *domain;
653
 
#ifdef HAVE_GETDOMAINNAME
654
 
    static int initialized;
655
 
#endif
656
 
 
657
 
    /* make sure we have a valid netgroup, sudo style */
658
 
    if (*netgr++ != '+')
659
 
        return(FALSE);
660
 
 
661
 
#ifdef HAVE_GETDOMAINNAME
662
 
    /* get the domain name (if any) */
663
 
    if (!initialized) {
664
 
        domain = (char *) emalloc(MAXHOSTNAMELEN);
665
 
        if (getdomainname(domain, MAXHOSTNAMELEN) == -1 || *domain == '\0') {
666
 
            efree(domain);
667
 
            domain = NULL;
668
 
        }
669
 
        initialized = 1;
670
 
    }
671
 
#endif /* HAVE_GETDOMAINNAME */
672
 
 
673
 
#ifdef HAVE_INNETGR
674
 
    if (innetgr(netgr, host, user, domain))
675
 
        return(TRUE);
676
 
    else if (host != shost && innetgr(netgr, shost, user, domain))
677
 
        return(TRUE);
678
 
#endif /* HAVE_INNETGR */
679
 
 
680
 
    return(FALSE);
681
 
}
682
 
 
683
 
/*
684
 
 * Returns TRUE if "s" has shell meta characters in it,
685
 
 * else returns FALSE.
686
 
 */
687
 
static int
688
 
has_meta(s)
689
 
    char *s;
690
 
{
691
 
    char *t;
692
 
 
693
 
    for (t = s; *t; t++) {
694
 
        if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
695
 
            return(TRUE);
696
 
    }
697
 
    return(FALSE);
 
475
                lbuf_append_quoted(lbuf, SUDOERS_QUOTED, d->val, NULL);
 
476
        } else
 
477
            lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
 
478
        prefix = ", ";
 
479
        nfound++;
 
480
    }
 
481
 
 
482
    return(nfound);
 
483
}
 
484
 
 
485
/*
 
486
 * Display Defaults entries that are per-runas or per-command
 
487
 */
 
488
int
 
489
sudo_file_display_bound_defaults(nss, pw, lbuf)
 
490
    struct sudo_nss *nss;
 
491
    struct passwd *pw;
 
492
    struct lbuf *lbuf;
 
493
{
 
494
    int nfound = 0;
 
495
 
 
496
    /* XXX - should only print ones that match what the user can do. */
 
497
    nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
 
498
    nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
 
499
 
 
500
    return(nfound);
 
501
}
 
502
 
 
503
/*
 
504
 * Display Defaults entries of the given type.
 
505
 */
 
506
static int
 
507
display_bound_defaults(dtype, lbuf)
 
508
    int dtype;
 
509
    struct lbuf *lbuf;
 
510
{
 
511
    struct defaults *d;
 
512
    struct member *m, *binding = NULL;
 
513
    char *dname, *dsep;
 
514
    int atype, nfound = 0;
 
515
 
 
516
    switch (dtype) {
 
517
        case DEFAULTS_HOST:
 
518
            atype = HOSTALIAS;
 
519
            dname = "host";
 
520
            dsep = "@";
 
521
            break;
 
522
        case DEFAULTS_USER:
 
523
            atype = USERALIAS;
 
524
            dname = "user";
 
525
            dsep = ":";
 
526
            break;
 
527
        case DEFAULTS_RUNAS:
 
528
            atype = RUNASALIAS;
 
529
            dname = "runas";
 
530
            dsep = ">";
 
531
            break;
 
532
        case DEFAULTS_CMND:
 
533
            atype = CMNDALIAS;
 
534
            dname = "cmnd";
 
535
            dsep = "!";
 
536
            break;
 
537
        default:
 
538
            return(-1);
 
539
    }
 
540
    /* printf("Per-%s Defaults entries:\n", dname); */
 
541
    tq_foreach_fwd(&defaults, d) {
 
542
        if (d->type != dtype)
 
543
            continue;
 
544
 
 
545
        nfound++;
 
546
        if (binding != tq_first(&d->binding)) {
 
547
            binding = tq_first(&d->binding);
 
548
            lbuf_append(lbuf, "    Defaults", dsep, NULL);
 
549
            for (m = binding; m != NULL; m = m->next) {
 
550
                if (m != binding)
 
551
                    lbuf_append(lbuf, ",", NULL);
 
552
                print_member(lbuf, m->name, m->type, m->negated, atype);
 
553
                lbuf_append(lbuf, " ", NULL);
 
554
            }
 
555
        } else
 
556
            lbuf_append(lbuf, ", ", NULL);
 
557
        if (d->val != NULL) {
 
558
            lbuf_append(lbuf, d->var, d->op == '+' ? "+=" :
 
559
                d->op == '-' ? "-=" : "=", d->val, NULL);
 
560
        } else
 
561
            lbuf_append(lbuf, d->op == FALSE ? "!" : "", d->var, NULL);
 
562
    }
 
563
 
 
564
    return(nfound);
 
565
}
 
566
 
 
567
int
 
568
sudo_file_display_cmnd(nss, pw)
 
569
    struct sudo_nss *nss;
 
570
    struct passwd *pw;
 
571
{
 
572
    struct cmndspec *cs;
 
573
    struct member *match;
 
574
    struct privilege *priv;
 
575
    struct userspec *us;
 
576
    int rval = 1;
 
577
    int host_match, runas_match, cmnd_match;
 
578
 
 
579
    if (nss->handle == NULL)
 
580
        return(rval);
 
581
 
 
582
    match = NULL;
 
583
    tq_foreach_rev(&userspecs, us) {
 
584
        if (userlist_matches(pw, &us->users) != ALLOW)
 
585
            continue;
 
586
 
 
587
        tq_foreach_rev(&us->privileges, priv) {
 
588
            host_match = hostlist_matches(&priv->hostlist);
 
589
            if (host_match != ALLOW)
 
590
                continue;
 
591
            tq_foreach_rev(&priv->cmndlist, cs) {
 
592
                runas_match = runaslist_matches(&cs->runasuserlist,
 
593
                    &cs->runasgrouplist);
 
594
                if (runas_match == ALLOW) {
 
595
                    cmnd_match = cmnd_matches(cs->cmnd);
 
596
                    if (cmnd_match != UNSPEC) {
 
597
                        match = host_match && runas_match ?
 
598
                            cs->cmnd : NULL;
 
599
                        goto matched;
 
600
                    }
 
601
                }
 
602
            }
 
603
        }
 
604
    }
 
605
    matched:
 
606
    if (match != NULL && !match->negated) {
 
607
        printf("%s%s%s\n", safe_cmnd, user_args ? " " : "",
 
608
            user_args ? user_args : "");
 
609
        rval = 0;
 
610
    }
 
611
    return(rval);
 
612
}
 
613
 
 
614
/*
 
615
 * Print the contents of a struct member to stdout
 
616
 */
 
617
static void
 
618
_print_member(lbuf, name, type, negated, alias_type)
 
619
    struct lbuf *lbuf;
 
620
    char *name;
 
621
    int type, negated, alias_type;
 
622
{
 
623
    struct alias *a;
 
624
    struct member *m;
 
625
    struct sudo_command *c;
 
626
 
 
627
    switch (type) {
 
628
        case ALL:
 
629
            lbuf_append(lbuf, negated ? "!ALL" : "ALL", NULL);
 
630
            break;
 
631
        case COMMAND:
 
632
            c = (struct sudo_command *) name;
 
633
            if (negated)
 
634
                lbuf_append(lbuf, "!", NULL);
 
635
            lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->cmnd, NULL);
 
636
            if (c->args) {
 
637
                lbuf_append(lbuf, " ", NULL);
 
638
                lbuf_append_quoted(lbuf, SUDOERS_QUOTED, c->args, NULL);
 
639
            }
 
640
            break;
 
641
        case ALIAS:
 
642
            if ((a = find_alias(name, alias_type)) != NULL) {
 
643
                tq_foreach_fwd(&a->members, m) {
 
644
                    if (m != tq_first(&a->members))
 
645
                        lbuf_append(lbuf, ", ", NULL);
 
646
                    _print_member(lbuf, m->name, m->type,
 
647
                        negated ? !m->negated : m->negated, alias_type);
 
648
                }
 
649
                break;
 
650
            }
 
651
            /* FALLTHROUGH */
 
652
        default:
 
653
            lbuf_append(lbuf, negated ? "!" : "", name, NULL);
 
654
            break;
 
655
    }
 
656
}
 
657
 
 
658
static void
 
659
print_member(lbuf, name, type, negated, alias_type)
 
660
    struct lbuf *lbuf;
 
661
    char *name;
 
662
    int type, negated, alias_type;
 
663
{
 
664
    alias_seqno++;
 
665
    _print_member(lbuf, name, type, negated, alias_type);
698
666
}