source: server/common/oursrc/accountadm/admof.c @ 544

Last change on this file since 544 was 544, checked in by andersk, 16 years ago
Replace Perl admof with a C version that is six times faster, and correctly handles negative ACLs and cross-realm principals.
File size: 5.1 KB
RevLine 
[544]1/* admof
2 * Version 2.0, released 2007-12-30
3 * Anders Kaseorg <andersk@mit.edu>
4 * replacing Perl version by Jeff Arnold <jbarnold@mit.edu>
5 *
6 * Usage:
7 *   admof scripts andersk/root@ATHENA.MIT.EDU
8 * Outputs "yes" and exits with status 33 if the given principal is an
9 * administrator of the locker.
10 */
11
12#include <stdio.h>
13#include <limits.h>
14#include <string.h>
15#include <sys/types.h>
16#include <pwd.h>
17#include <netinet/in.h>
18#include <afs/vice.h>
19#include <afs/venus.h>
20#include <afs/ptclient.h>
21#include <afs/ptuser.h>
22#include <afs/prs_fs.h>
23#include <afs/ptint.h>
24#include <afs/cellconfig.h>
25#include <afs/afsutil.h>
26#include <krb5.h>
27#include <kerberosIV/krb.h>
28
29extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
30
31#define die(args...) do { fprintf(stderr, args); pr_End(); exit(1); } while(0)
32#define _STR(x) #x
33#define STR(x) _STR(x)
34
35#define OVERLORDS "system:scripts-root"
36
37static int
38ismember(const char *user, const char *group)
39{
40    int flag;
41    if (pr_IsAMemberOf((char *)user, (char *)group, &flag) == 0)
42        return flag;
43    else
44        return 0;
45}
46
47/* Parse an ACL of n entries, returning the rights for user. */
48static int
49parse_rights(int n, const char **p, const char *user)
50{
51    int rights = 0;
52
53    int i;
54    for (i = 0; i < n; ++i) {
55        char tname[PR_MAXNAMELEN];
56        int trights;
57
58        int off;
59        if (sscanf(*p, "%" STR(PR_MAXNAMELEN) "s %d\n%n", tname, &trights, &off) < 2)
60            die("internal error: can't parse output from pioctl\n");
61        *p += off;
62
63        if (~rights & trights &&
64            (strcasecmp(tname, user) == 0 ||
65             (strchr(tname, ':') != 0 && ismember(user, tname))))
66            rights |= trights;
67    }
68
69    return rights;
70}
71
72int
73main(int argc, const char *argv[])
74{
75    /* Get arguments. */
76    if (argc != 3)
77        die("Usage: %s LOCKER PRINCIPAL\n", argv[0]);
78    const char *locker = argv[1], *name = argv[2];
79
80    /* Convert the locker into a directory. */
81    char dir[PATH_MAX];
82    int n;
83    struct passwd *pwd = getpwnam(locker);
84    if (pwd != NULL)
85        n = snprintf(dir, sizeof dir, pwd->pw_dir);
86    else
87        n = snprintf(dir, sizeof dir, "/mit/%s", locker);
88    if (n < 0 || n >= sizeof dir)
89        die("internal error\n");
90
91    /* Get the locker's cell. */
92    char cell[MAXCELLCHARS];
93    struct ViceIoctl vi;
94    vi.in = NULL;
95    vi.in_size = 0;
96    vi.out = cell;
97    vi.out_size = sizeof cell;
98    if (pioctl(dir, VIOC_FILE_CELL_NAME, &vi, 1) != 0)
99        die("internal error: pioctl: %m\n");
100
101    if (pr_Initialize(0, (char *)AFSDIR_CLIENT_ETC_DIRPATH, cell) != 0)
102        die("internal error: pr_Initialize failed\n");
103
104    /* Get the cell configuration. */
105    struct afsconf_dir *configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
106    if (configdir == NULL)
107        die("internal error: afsconf_Open failed\n");
108    struct afsconf_cell cellconfig;
109    if (afsconf_GetCellInfo(configdir, cell, NULL, &cellconfig) != 0)
110        die("internal error: afsconf_GetCellInfo failed\n");
111    afsconf_Close(configdir);
112
113    /* Figure out the cell's realm. */
114    krb5_context context;
115    krb5_init_context(&context);
116
117    char **realm_list;
118    if (krb5_get_host_realm(context, cellconfig.hostName[0], &realm_list) != 0 ||
119        realm_list[0] == NULL)
120        die("internal error: krb5_get_host_realm failed");
121
122    /* Convert the Kerberos 5 principal into a (Kerberos IV-style) AFS
123       name, omitting the realm if it equals the cell's realm. */
124    krb5_principal principal;
125    if (krb5_parse_name(context, name, &principal) != 0)
126        die("internal error: krb5_parse_name failed");
127    char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
128    if (krb5_524_conv_principal(context, principal, pname, pinst, prealm) != 0)
129        die("internal error: krb5_524_conv_principal failed\n");
130    char user[MAX(PR_MAXNAMELEN, MAX_K_NAME_SZ)];
131    if (kname_unparse(user, pname, pinst,
132                      strcmp(prealm, realm_list[0]) == 0 ? NULL : prealm) != 0)
133        die("internal error: kname_unparse failed\n");
134
135    krb5_free_principal(context, principal);
136    krb5_free_host_realm(context, realm_list);
137    krb5_free_context(context);
138
139    /* Instead of canonicalizing the name as below, we just use
140       strcasecmp above. */
141#if 0
142    afs_int32 id;
143    if (pr_SNameToId((char *)user, &id) != 0)
144        die("bad principal\n");
145    if (id == ANONYMOUSID)
146        die("anonymous\n");
147    if (pr_SIdToName(id, user) != 0)
148        die("internal error: pr_SIdToName failed\n");
149#endif
150
151    /* Read the locker ACL. */
152    char acl[2048];
153    vi.in = NULL;
154    vi.in_size = 0;
155    vi.out = acl;
156    vi.out_size = sizeof acl;
157    if (pioctl(dir, VIOCGETAL, &vi, 1) != 0)
158        die("internal error: pioctl: %m\n");
159
160    /* Parse the locker ACL to compute the user's rights. */
161    const char *p = acl;
162
163    int nplus, nminus;
164    int off;
165    if (sscanf(p, "%d\n%d\n%n", &nplus, &nminus, &off) < 2)
166        die("internal error: can't parse output from pioctl\n");
167    p += off;
168
169    int rights = parse_rights(nplus, &p, user);
170    rights &= ~parse_rights(nminus, &p, user);
171#ifdef OVERLORDS
172    if (~rights & PRSFS_ADMINISTER && ismember(user, OVERLORDS))
173        rights |= PRSFS_ADMINISTER;
174#endif
175
176    pr_End();
177
178    /* Output whether the user is an administrator. */
179    if (rights & PRSFS_ADMINISTER) {
180        printf("yes\n");
181        exit(33);
182    } else {
183        printf("no\n");
184        exit(1);
185    }
186}
Note: See TracBrowser for help on using the repository browser.