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

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