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

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