source: branches/fc17-dev/server/common/oursrc/accountadm/admof.c @ 2081

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