1/*++
2/* NAME
3/*	user_acl 3
4/* SUMMARY
5/*	user name based access control
6/* SYNOPSIS
7/*	#include <user_acl.h>
8/*
9/*	const char *check_user_acl_byuid(acl, uid)
10/*	const char *acl;
11/*	uid_t	uid;
12/* DESCRIPTION
13/*	check_user_acl_byuid() converts the given uid into a user
14/*	name, and checks the result against a user name matchlist.
15/*	If the uid cannot be resolved to a user name, "unknown"
16/*	is used as the lookup key instead.
17/*	The result is NULL on success, the username upon failure.
18/*	The error result lives in static storage and must be saved
19/*	if it is to be used to across multiple check_user_acl_byuid()
20/*	calls.
21/*
22/*	Arguments:
23/* .IP acl
24/*	Authorized user name list suitable for input to string_list_init(3).
25/* .IP uid
26/*	The uid to be checked against the access list.
27/* LICENSE
28/* .ad
29/* .fi
30/*	The Secure Mailer license must be distributed with this software.
31/* AUTHOR(S)
32/*	Wietse Venema
33/*	IBM T.J. Watson Research
34/*	P.O. Box 704
35/*	Yorktown Heights, NY 10598, USA
36/*
37/*	Victor Duchovni
38/*	Morgan Stanley
39/*--*/
40
41/* System library. */
42
43#include <sys_defs.h>
44#include <string.h>
45
46/* Utility library. */
47
48#include <vstring.h>
49#include <dict_static.h>
50
51/* Global library. */
52
53#include <string_list.h>
54#include <mypwd.h>
55
56/* Application-specific. */
57
58#include "user_acl.h"
59
60/* check_user_acl_byuid - check user authorization */
61
62const char *check_user_acl_byuid(char *acl, uid_t uid)
63{
64    struct mypasswd *mypwd;
65    STRING_LIST *list;
66    static VSTRING *who = 0;
67    int     matched;
68    const char *name;
69
70    /*
71     * Optimize for the most common case. This also makes Postfix a little
72     * more robust in the face of local infrastructure failures. Note that we
73     * only need to match the "static:" substring, not the result value.
74     */
75    if (strncmp(acl, DICT_TYPE_STATIC ":", sizeof(DICT_TYPE_STATIC)) == 0)
76	return (0);
77
78    /*
79     * XXX: Substitute "unknown" for UIDs without username, so that
80     * static:anyone results in "permit" even when the uid is not found in
81     * the password file, and so that a pattern of !unknown can be used to
82     * block non-existent accounts.
83     *
84     * The alternative is to use the UID as a surrogate lookup key for
85     * non-existent accounts. There are several reasons why this is not a
86     * good idea. 1) An ACL with a numerical UID should work regardless of
87     * whether or not an account has a password file entry. Therefore we
88     * would always have search on the numerical UID whenever the username
89     * fails to produce a match. 2) The string-list infrastructure is not
90     * really suitable for mixing numerical and non-numerical user
91     * information, because the numerical match is done in a separate pass
92     * from the non-numerical match. This breaks when the ! operator is used.
93     *
94     * XXX To avoid waiting until the lookup completes (e.g., LDAP or NIS down)
95     * invoke mypwuid_err(), and either change the user_acl() API to
96     * propagate the error to the caller, or treat lookup errors as fatal.
97     */
98    if ((mypwd = mypwuid(uid)) == 0) {
99	name = "unknown";
100    } else {
101	name = mypwd->pw_name;
102    }
103
104    list = string_list_init(MATCH_FLAG_NONE, acl);
105    if ((matched = string_list_match(list, name)) == 0) {
106	if (!who)
107	    who = vstring_alloc(10);
108	vstring_strcpy(who, name);
109    }
110    string_list_free(list);
111    if (mypwd)
112	mypwfree(mypwd);
113
114    return (matched ? 0 : vstring_str(who));
115}
116