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

Last change on this file since 1601 was 1601, checked in by geofft, 11 years ago
admof: Re-resolve the potential sysadmin's name within SYSADMIN_CELL While it's true that foreign cells will generally resolve the username to e.g. geofft.root@athena.mit.edu, which the athena cell ptservers are okay with, it's more correct to just use the name as resolved in the athena cell.
File size: 7.7 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 SYSADMINS "system:scripts-root"
43#define SYSADMIN_CELL "athena.mit.edu"
44
45static bool
46ismember(char *user, char *group)
47{
48    int flag;
49    if (pr_IsAMemberOf(user, group, &flag) == 0)
50        return flag;
51    else
52        return 0;
53}
54
55/* Parse an ACL of n entries, returning the rights for user. */
56static int
57parse_rights(int n, const char **p, char *user)
58{
59    int rights = 0, *trights = malloc(n * sizeof(int)), i;
60    namelist tnames = {.namelist_len = n,
61                       .namelist_val = malloc(n * PR_MAXNAMELEN)};
62    idlist tids = {.idlist_len = 0,
63                   .idlist_val = NULL};
64
65    if (trights == NULL || tnames.namelist_val == NULL)
66        die("internal error: malloc failed: %m");
67
68    for (i = 0; i < n; ++i) {
69        int off;
70        if (sscanf(*p, "%" STR(PR_MAXNAMELEN) "s %d\n%n",
71                   tnames.namelist_val[i], &trights[i], &off) < 2)
72            die("internal error: can't parse output from pioctl\n");
73        *p += off;
74    }
75
76    if (pr_NameToId(&tnames, &tids) != 0)
77        die("internal error: pr_NameToId failed");
78    if (tids.idlist_len < n)
79        die("internal error: pr_NameToId did not return enough ids");
80
81    for (i = 0; i < n; ++i) {
82        if (~rights & trights[i] &&
83            (strcasecmp(tnames.namelist_val[i], user) == 0 ||
84             (tids.idlist_val[i] < 0 && ismember(user, tnames.namelist_val[i]))))
85            rights |= trights[i];
86    }
87
88    /* Note: this first free probably should be xdr_free in OpenAFS 1.5.
89     * See commits b40b606 and f02f2e8 */
90    free(tids.idlist_val);
91    tids.idlist_val = NULL;
92    free(tnames.namelist_val);
93    free(trights);
94
95    return rights;
96}
97
98/* Resolve a Kerberos principal to a name usable by the AFS PTS. */
99void
100resolve_principal(const char *name, const char *cell, char *user)
101{
102    /* Figure out the cell's realm. */
103    krb5_context context;
104    krb5_init_context(&context);
105
106    char **realm_list;
107    if (krb5_get_host_realm(context, cell, &realm_list) != 0 ||
108        realm_list[0] == NULL)
109        die("internal error: krb5_get_host_realm failed");
110
111    /* Convert the Kerberos 5 principal into a (Kerberos IV-style) AFS
112       name, omitting the realm if it equals the cell's realm. */
113    krb5_principal principal;
114    if (krb5_parse_name(context, name, &principal) != 0)
115        die("internal error: krb5_parse_name failed");
116    char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
117    if (krb5_524_conv_principal(context, principal, pname, pinst, prealm) != 0)
118        die("internal error: krb5_524_conv_principal failed\n");
119    if (kname_unparse(user, pname, pinst,
120                      strcmp(prealm, realm_list[0]) == 0 ? NULL : prealm) != 0)
121        die("internal error: kname_unparse failed\n");
122
123    krb5_free_principal(context, principal);
124    krb5_free_host_realm(context, realm_list);
125    krb5_free_context(context);
126
127    /* Instead of canonicalizing the name as below, we just use
128       strcasecmp above. */
129#if 0
130    afs_int32 id;
131    if (pr_SNameToId((char *)user, &id) != 0)
132        die("bad principal\n");
133    if (id == ANONYMOUSID)
134        die("anonymous\n");
135    if (pr_SIdToName(id, user) != 0)
136        die("internal error: pr_SIdToName failed\n");
137#endif
138}
139
140int
141main(int argc, const char *argv[])
142{
143    /* Get arguments. */
144    const char *locker, *name;
145    afs_int32 secLevel;
146
147    if (argc == 3) {
148        locker = argv[1];
149        name = argv[2];
150        secLevel = 3;
151    } else if (argc == 4 && strcmp("-noauth", argv[1]) == 0) {
152        locker = argv[2];
153        name = argv[3];
154        secLevel = 0;
155    } else {
156        die("Usage: %s [-noauth] LOCKER PRINCIPAL\n", argv[0]);
157    }
158
159    /* Convert the locker into a directory. */
160    char dir[PATH_MAX];
161    int n;
162    struct passwd *pwd = getpwnam(locker);
163    if (pwd != NULL)
164        n = snprintf(dir, sizeof dir, "%s", pwd->pw_dir);
165    else
166        n = snprintf(dir, sizeof dir, "/mit/%s", locker);
167    if (n < 0 || n >= sizeof dir)
168        die("internal error\n");
169
170    /* For non-AFS homedirs, read the .k5login file. */
171    if (strncmp(dir, "/afs/", 5) != 0 && strncmp(dir, "/mit/", 5) != 0) {
172        if (chdir(dir) != 0)
173            die("internal error: chdir: %m\n");
174        FILE *fp = fopen(".k5login", "r");
175        if (fp == NULL)
176            die("internal error: .k5login: %m\n");
177        struct stat st;
178        if (fstat(fileno(fp), &st) != 0)
179            die("internal error: fstat: %m\n");
180        if (st.st_uid != pwd->pw_uid && st.st_uid != 0) {
181            fclose(fp);
182            die("internal error: bad .k5login permissions\n");
183        }
184        bool found = false;
185        char *line = NULL;
186        size_t len = 0;
187        ssize_t read;
188        while ((read = getline(&line, &len, fp)) != -1) {
189            if (read > 0 && line[read - 1] == '\n')
190                line[read - 1] = '\0';
191            if (strcmp(name, line) == 0) {
192                found = true;
193                break;
194            }
195        }
196        if (line)
197            free(line);
198        fclose(fp);
199        if (found) {
200            printf("yes\n");
201            exit(33);
202        } else {
203            printf("no\n");
204            exit(1);
205        }
206    }
207
208    /* Get the locker's cell. */
209    char cell[MAXCELLCHARS];
210    struct ViceIoctl vi;
211    vi.in = NULL;
212    vi.in_size = 0;
213    vi.out = cell;
214    vi.out_size = sizeof cell;
215    if (pioctl(dir, VIOC_FILE_CELL_NAME, &vi, 1) != 0)
216        die("internal error: pioctl: %m\n");
217
218    if (pr_Initialize(secLevel, (char *)AFSDIR_CLIENT_ETC_DIRPATH, cell) != 0)
219        die("internal error: pr_Initialize failed\n");
220
221    /* Get the cell configuration. */
222    struct afsconf_dir *configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
223    if (configdir == NULL)
224        die("internal error: afsconf_Open failed\n");
225    struct afsconf_cell cellconfig;
226    if (afsconf_GetCellInfo(configdir, cell, NULL, &cellconfig) != 0)
227        die("internal error: afsconf_GetCellInfo failed\n");
228    afsconf_Close(configdir);
229
230    char user[MAX(PR_MAXNAMELEN, MAX_K_NAME_SZ)];
231    resolve_principal(name, cellconfig.hostName[0], user);
232
233    /* Read the locker ACL. */
234    char acl[2048];
235    vi.in = NULL;
236    vi.in_size = 0;
237    vi.out = acl;
238    vi.out_size = sizeof acl;
239    if (pioctl(dir, VIOCGETAL, &vi, 1) != 0)
240        die("internal error: pioctl: %m\n");
241
242    /* Parse the locker ACL to compute the user's rights. */
243    const char *p = acl;
244
245    int nplus, nminus;
246    int off;
247    if (sscanf(p, "%d\n%d\n%n", &nplus, &nminus, &off) < 2)
248        die("internal error: can't parse output from pioctl\n");
249    p += off;
250
251    int rights = parse_rights(nplus, &p, user);
252    rights &= ~parse_rights(nminus, &p, user);
253    pr_End();
254
255#ifdef SYSADMINS
256    if (~rights & PRSFS_ADMINISTER) {
257        char sysadmins[] = SYSADMINS, sysadmin_cell[] = SYSADMIN_CELL;
258        if (pr_Initialize(secLevel, (char *)AFSDIR_CLIENT_ETC_DIRPATH, sysadmin_cell) == 0) {
259            resolve_principal(name, sysadmin_cell, user);
260            if (ismember(user, sysadmins)) {
261                openlog("admof", 0, LOG_AUTHPRIV);
262                syslog(LOG_NOTICE, "giving %s admin rights on %s", user, locker);
263                closelog();
264                rights |= PRSFS_ADMINISTER;
265            }
266            pr_End();
267        }
268        /* If not, that's okay -- the normal codepath ran fine, so don't error */
269    }
270#endif
271
272    /* Output whether the user is an administrator. */
273    if (rights & PRSFS_ADMINISTER) {
274        printf("yes\n");
275        exit(33);
276    } else {
277        printf("no\n");
278        exit(1);
279    }
280}
Note: See TracBrowser for help on using the repository browser.