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

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