source: trunk/server/common/oursrc/accountadm/admof.c @ 1464

Last change on this file since 1464 was 1347, checked in by geofft, 14 years ago
admof: Add a -noauth flag.
File size: 6.5 KB
Line 
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 * Requires tokens (to authenticate/encrypt the connection to the
12 * ptserver) unless -noauth is given.
13 */
14
15#include <stdio.h>
16#include <limits.h>
17#include <string.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <pwd.h>
21#include <unistd.h>
22#include <netinet/in.h>
23#include <afs/vice.h>
24#include <afs/venus.h>
25#include <afs/ptclient.h>
26#include <afs/ptuser.h>
27#include <afs/prs_fs.h>
28#include <afs/ptint.h>
29#include <afs/cellconfig.h>
30#include <afs/afsutil.h>
31#include <krb5.h>
32#include <kerberosIV/krb.h>
33#include <stdbool.h>
34#include <syslog.h>
35
36extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
37
38#define die(args...) do { fprintf(stderr, args); pr_End(); exit(1); } while(0)
39#define _STR(x) #x
40#define STR(x) _STR(x)
41
42#define OVERLORDS "system:scripts-root"
43
44static bool
45ismember(const char *user, const char *group)
46{
47    int flag;
48    if (pr_IsAMemberOf((char *)user, (char *)group, &flag) == 0)
49        return flag;
50    else
51        return 0;
52}
53
54/* Parse an ACL of n entries, returning the rights for user. */
55static int
56parse_rights(int n, const char **p, const char *user)
57{
58    int rights = 0;
59
60    int i;
61    for (i = 0; i < n; ++i) {
62        char tname[PR_MAXNAMELEN];
63        int trights;
64
65        int off;
66        if (sscanf(*p, "%" STR(PR_MAXNAMELEN) "s %d\n%n", tname, &trights, &off) < 2)
67            die("internal error: can't parse output from pioctl\n");
68        *p += off;
69
70        if (~rights & trights &&
71            (strcasecmp(tname, user) == 0 ||
72             (strchr(tname, ':') != 0 && ismember(user, tname))))
73            rights |= trights;
74    }
75
76    return rights;
77}
78
79int
80main(int argc, const char *argv[])
81{
82    /* Get arguments. */
83    const char *locker, *name;
84    afs_int32 secLevel;
85
86    if (argc == 3) {
87        locker = argv[1];
88        name = argv[2];
89        secLevel = 3;
90    } else if (argc == 4 && strcmp("-noauth", argv[1]) == 0) {
91        locker = argv[2];
92        name = argv[3];
93        secLevel = 0;
94    } else {
95        die("Usage: %s [-noauth] LOCKER PRINCIPAL\n", argv[0]);
96    }
97
98    /* Convert the locker into a directory. */
99    char dir[PATH_MAX];
100    int n;
101    struct passwd *pwd = getpwnam(locker);
102    if (pwd != NULL)
103        n = snprintf(dir, sizeof dir, "%s", pwd->pw_dir);
104    else
105        n = snprintf(dir, sizeof dir, "/mit/%s", locker);
106    if (n < 0 || n >= sizeof dir)
107        die("internal error\n");
108
109    /* For non-AFS homedirs, read the .k5login file. */
110    if (strncmp(dir, "/afs/", 5) != 0 && strncmp(dir, "/mit/", 5) != 0) {
111        if (chdir(dir) != 0)
112            die("internal error: chdir: %m\n");
113        FILE *fp = fopen(".k5login", "r");
114        if (fp == NULL)
115            die("internal error: .k5login: %m\n");
116        struct stat st;
117        if (fstat(fileno(fp), &st) != 0)
118            die("internal error: fstat: %m\n");
119        if (st.st_uid != pwd->pw_uid && st.st_uid != 0) {
120            fclose(fp);
121            die("internal error: bad .k5login permissions\n");
122        }
123        bool found = false;
124        char *line = NULL;
125        size_t len = 0;
126        ssize_t read;
127        while ((read = getline(&line, &len, fp)) != -1) {
128            if (read > 0 && line[read - 1] == '\n')
129                line[read - 1] = '\0';
130            if (strcmp(name, line) == 0) {
131                found = true;
132                break;
133            }
134        }
135        if (line)
136            free(line);
137        fclose(fp);
138        if (found) {
139            printf("yes\n");
140            exit(33);
141        } else {
142            printf("no\n");
143            exit(1);
144        }
145    }
146
147    /* Get the locker's cell. */
148    char cell[MAXCELLCHARS];
149    struct ViceIoctl vi;
150    vi.in = NULL;
151    vi.in_size = 0;
152    vi.out = cell;
153    vi.out_size = sizeof cell;
154    if (pioctl(dir, VIOC_FILE_CELL_NAME, &vi, 1) != 0)
155        die("internal error: pioctl: %m\n");
156
157    if (pr_Initialize(secLevel, (char *)AFSDIR_CLIENT_ETC_DIRPATH, cell) != 0)
158        die("internal error: pr_Initialize failed\n");
159
160    /* Get the cell configuration. */
161    struct afsconf_dir *configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
162    if (configdir == NULL)
163        die("internal error: afsconf_Open failed\n");
164    struct afsconf_cell cellconfig;
165    if (afsconf_GetCellInfo(configdir, cell, NULL, &cellconfig) != 0)
166        die("internal error: afsconf_GetCellInfo failed\n");
167    afsconf_Close(configdir);
168
169    /* Figure out the cell's realm. */
170    krb5_context context;
171    krb5_init_context(&context);
172
173    char **realm_list;
174    if (krb5_get_host_realm(context, cellconfig.hostName[0], &realm_list) != 0 ||
175        realm_list[0] == NULL)
176        die("internal error: krb5_get_host_realm failed");
177
178    /* Convert the Kerberos 5 principal into a (Kerberos IV-style) AFS
179       name, omitting the realm if it equals the cell's realm. */
180    krb5_principal principal;
181    if (krb5_parse_name(context, name, &principal) != 0)
182        die("internal error: krb5_parse_name failed");
183    char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
184    if (krb5_524_conv_principal(context, principal, pname, pinst, prealm) != 0)
185        die("internal error: krb5_524_conv_principal failed\n");
186    char user[MAX(PR_MAXNAMELEN, MAX_K_NAME_SZ)];
187    if (kname_unparse(user, pname, pinst,
188                      strcmp(prealm, realm_list[0]) == 0 ? NULL : prealm) != 0)
189        die("internal error: kname_unparse failed\n");
190
191    krb5_free_principal(context, principal);
192    krb5_free_host_realm(context, realm_list);
193    krb5_free_context(context);
194
195    /* Instead of canonicalizing the name as below, we just use
196       strcasecmp above. */
197#if 0
198    afs_int32 id;
199    if (pr_SNameToId((char *)user, &id) != 0)
200        die("bad principal\n");
201    if (id == ANONYMOUSID)
202        die("anonymous\n");
203    if (pr_SIdToName(id, user) != 0)
204        die("internal error: pr_SIdToName failed\n");
205#endif
206
207    /* Read the locker ACL. */
208    char acl[2048];
209    vi.in = NULL;
210    vi.in_size = 0;
211    vi.out = acl;
212    vi.out_size = sizeof acl;
213    if (pioctl(dir, VIOCGETAL, &vi, 1) != 0)
214        die("internal error: pioctl: %m\n");
215
216    /* Parse the locker ACL to compute the user's rights. */
217    const char *p = acl;
218
219    int nplus, nminus;
220    int off;
221    if (sscanf(p, "%d\n%d\n%n", &nplus, &nminus, &off) < 2)
222        die("internal error: can't parse output from pioctl\n");
223    p += off;
224
225    int rights = parse_rights(nplus, &p, user);
226    rights &= ~parse_rights(nminus, &p, user);
227#ifdef OVERLORDS
228    if (~rights & PRSFS_ADMINISTER && ismember(user, OVERLORDS)) {
229        openlog("admof", 0, LOG_AUTHPRIV);
230        syslog(LOG_NOTICE, "giving %s admin rights on %s", user, locker);
231        closelog();
232        rights |= PRSFS_ADMINISTER;
233    }
234#endif
235
236    pr_End();
237
238    /* Output whether the user is an administrator. */
239    if (rights & PRSFS_ADMINISTER) {
240        printf("yes\n");
241        exit(33);
242    } else {
243        printf("no\n");
244        exit(1);
245    }
246}
Note: See TracBrowser for help on using the repository browser.