source: trunk/athena/lib/locker/afs.c @ 13384

Revision 13384, 16.1 KB checked in by danw, 25 years ago (diff)
fix the "only renew tokens if the new ones match the old ones and last longer" code.
Line 
1/* Copyright 1998 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This file is part of liblocker. It implements AFS lockers. */
17
18static const char rcsid[] = "$Id: afs.c,v 1.7 1999-07-14 17:37:19 danw Exp $";
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <errno.h>
24#include <netdb.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29#include <afs/stds.h>
30#include <afs/param.h>
31#include <afs/auth.h>
32#include <afs/cellconfig.h>
33#include <afs/ptserver.h>
34#include <afs/venus.h>
35#include <rx/rxkad.h>
36
37/* This is defined in <afs/volume.h>, but it doesn't seem possible to
38 * include that without dragging in most of the rest of the afs
39 * includes as dependencies.
40 */
41#define VNAMESIZE 32
42
43#include <com_err.h>
44#include <krb.h>
45
46#include "locker.h"
47#include "locker_private.h"
48
49/* Cheesy test for determining AFS 3.5. */
50#ifndef AFSCONF_CLIENTNAME
51#define AFS35
52#endif
53
54#ifdef AFS35
55#include <afs/dirpath.h>
56#else
57#define AFSDIR_CLIENT_ETC_DIRPATH AFSCONF_CLIENTNAME
58#endif
59
60static int afs_parse(locker_context context, char *name, char *desc,
61                     char *mountpoint, locker_attachent **at);
62static int afs_attach(locker_context context, locker_attachent *at,
63                      char *mountoptions);
64static int afs_detach(locker_context context, locker_attachent *at);
65static int afs_auth(locker_context context, locker_attachent *at,
66                    int mode, int op);
67static int afs_zsubs(locker_context context, locker_attachent *at);
68
69struct locker_ops locker__afs_ops = {
70  "AFS",
71  0,
72  afs_parse,
73  afs_attach,
74  afs_detach,
75  afs_auth,
76  afs_zsubs
77};
78
79static int afs_get_cred(char *name, char *inst, char *realm,
80                        CREDENTIALS *cred);
81static int afs_maybe_auth_to_cell(locker_context context, char *name,
82                                  char *cell, int op, int force);
83
84static int afs_parse(locker_context context, char *name, char *desc,
85                     char *mountpoint, locker_attachent **atp)
86{
87  locker_attachent *at;
88  char *p, *dup = NULL, *lasts = NULL;
89  int status;
90
91  at = locker__new_attachent(context, &locker__afs_ops);
92  if (!at)
93    return LOCKER_ENOMEM;
94
95  if (!name)
96    {
97      /* This is an explicit description. */
98
99      if (strncmp(desc, "/afs/", 5))
100        {
101          locker__error(context, "%s: Path is not in AFS.\n", desc);
102          status = LOCKER_EPARSE;
103          goto cleanup;
104        }
105
106      at->name = strdup(desc);
107      at->hostdir = strdup(desc);
108      if (mountpoint)
109        at->mountpoint = strdup(mountpoint);
110      else
111        {
112          p = strrchr(desc, '/') + 1;
113          at->mountpoint = malloc(strlen(context->afs_mount_dir) +
114                                  strlen(p) + 2);
115          if (at->mountpoint)
116            sprintf(at->mountpoint, "%s/%s", context->afs_mount_dir, p);
117        }
118      if (!at->name || !at->hostdir || !at->mountpoint)
119        goto mem_error;
120
121      at->mode = LOCKER_AUTH_READWRITE;
122    }
123  else
124    {
125      /* A Hesiod AFS description looks like:
126       * AFS /afs/dev.mit.edu/source/src-current w /mit/source
127       */
128
129      at->name = strdup(name);
130      if (!at->name)
131        goto mem_error;
132
133      dup = strdup(desc);
134      if (!dup)
135        goto mem_error;
136
137      /* Skip "AFS". */
138      if (!strtok_r(dup, " ", &lasts))
139        goto parse_error;
140
141      /* Hostdir */
142      at->hostdir = strtok_r(NULL, " ", &lasts);
143      if (!at->hostdir)
144        goto parse_error;
145      if (strncmp(at->hostdir, "/afs/", 5))
146        {
147          locker__error(context, "%s: Path \"%s\" is not in AFS.\n", name,
148                        at->hostdir);
149          status = LOCKER_EPARSE;
150          goto cleanup;
151        }
152      at->hostdir = strdup(at->hostdir);
153      if (!at->hostdir)
154        goto mem_error;
155
156      /* Auth mode */
157      p = strtok_r(NULL, " ", &lasts);
158      if (!p || *(p + 1))
159        goto parse_error;
160
161      switch (*p)
162        {
163        case 'r':
164          at->mode = LOCKER_AUTH_READONLY;
165          break;
166        case 'w':
167          at->mode = LOCKER_AUTH_READWRITE;
168          break;
169        case 'n':
170          at->mode = LOCKER_AUTH_NONE;
171          break;
172        default:
173          locker__error(context, "%s: Unrecognized auth mode '%c' in "
174                        "description:\n%s\n", name, *p, desc);
175          status = LOCKER_EPARSE;
176          goto cleanup;
177        }
178
179      /* Mountpoint */
180      p = strtok_r(NULL, " ", &lasts);
181      if (!p)
182        goto parse_error;
183      if (mountpoint)
184        at->mountpoint = strdup(mountpoint);
185      else
186        at->mountpoint = strdup(p);
187      if (!at->mountpoint)
188        goto mem_error;
189
190      free(dup);
191      dup = NULL;
192    }
193
194  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_MOST,
195                                     &(at->mountpoint), &(at->buildfrom));
196  if (status != LOCKER_SUCCESS)
197    goto cleanup;
198
199  *atp = at;
200  return LOCKER_SUCCESS;
201
202mem_error:
203  locker__error(context, "Out of memory parsing locker description.\n");
204  status = LOCKER_ENOMEM;
205  goto cleanup;
206
207parse_error:
208  locker__error(context, "Could not parse locker description "
209                "\"%s\".\n", desc);
210  status = LOCKER_EPARSE;
211
212cleanup:
213  free(dup);
214  locker_free_attachent(context, at);
215  return status;
216}
217
218static int afs_attach(locker_context context, locker_attachent *at,
219                      char *mountoptions)
220{
221  struct stat st1, st2;
222  struct ViceIoctl vio;
223  int32 hosts[8]; /* AFS docs say VIOCWHEREIS won't return more than 8. */
224  uid_t uid = geteuid();
225  int status;
226
227  /* Make sure user can read the destination, and it's a directory. */
228  if (uid != context->user)
229    seteuid(context->user);
230  status = lstat(at->hostdir, &st1);
231  if (uid != context->user)
232    seteuid(uid);
233
234  if (status == -1)
235    {
236     if (errno == ETIMEDOUT)
237        {
238          locker__error(context, "%s: Connection timed out while trying to "
239                        "attach locker.\nThis probably indicates a temporary "
240                        "problem with the file server containing\n"
241                        "this locker. Try again later.\n", at->name);
242        }
243      else
244        {
245          locker__error(context, "%s: Could not attach locker:\n%s for %s\n",
246                        at->name, strerror(errno), at->hostdir);
247        }
248      return LOCKER_EATTACH;
249    }
250  if (!S_ISDIR(st1.st_mode) && !S_ISLNK(st1.st_mode))
251    {
252      locker__error(context, "%s: Could not attach locker:\n"
253                    "%s is not a directory.\n", at->name, at->hostdir);
254      return LOCKER_EATTACH;
255    }
256
257  /* Make sure nothing is already mounted on our mountpoint. */
258  status = stat(at->mountpoint, &st2);
259  if (!status && st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
260    {
261      locker__error(context, "%s: Locker is already attached.\n",
262                    at->name);
263      return LOCKER_EALREADY;
264    }
265  else if (!status || errno != ENOENT)
266    {
267      locker__error(context, "%s: Could not attach locker:\n"
268                    "Mountpoint %s is busy.\n", at->name, at->mountpoint);
269      return LOCKER_EMOUNTPOINTBUSY;
270    }
271
272  status = symlink(at->hostdir, at->mountpoint);
273  if (status < 0)
274    {
275      locker__error(context, "%s: Could not attach locker:\n%s while "
276                    "symlinking %s to %s\n", at->name, strerror(errno),
277                    at->hostdir, at->mountpoint);
278      return LOCKER_EATTACH;
279    }
280
281  /* Find host that the locker is on, and update the attachent. */
282  memset(hosts, 0, sizeof(hosts));
283  vio.in_size = 0;
284  vio.out = (caddr_t)hosts;
285  vio.out_size = sizeof(hosts);
286  if (pioctl(at->hostdir, VIOCWHEREIS, &vio, 1) == 0)
287    {
288      /* Only record the hostaddr if the locker is on a single host.
289       * (We assume that if it's on multiple hosts, it can't fail,
290       * so we don't need to know what those hosts are.)
291       */
292      if (!hosts[1])
293        at->hostaddr.s_addr = hosts[0];
294    }
295
296  return LOCKER_SUCCESS;
297}
298
299static int afs_detach(locker_context context, locker_attachent *at)
300{
301  int status;
302
303  status = unlink(at->mountpoint);
304  if (status < 0)
305    {
306      if (errno == ENOENT)
307        {
308          locker__error(context, "%s: Locker is not attached.\n", at->name);
309          return LOCKER_ENOTATTACHED;
310        }
311      else
312        {
313          locker__error(context, "%s: Could not detach locker:\n%s while "
314                        "trying to unlink %s.\n", at->name, strerror(errno),
315                        at->mountpoint);
316          return LOCKER_EDETACH;
317        }
318    }
319  return LOCKER_SUCCESS;
320}
321
322static int afs_auth(locker_context context, locker_attachent *at,
323                    int mode, int op)
324{
325  char *cell, *p;
326  int status;
327
328  if (op != LOCKER_AUTH_AUTHENTICATE)
329    return LOCKER_SUCCESS;
330
331  /* We know (from afs_parse) that at->hostdir starts with "/afs/". */
332  cell = at->hostdir + 5;
333  /* Skip initial "." in the cell name (if this is a path to a rw volume). */
334  if (*cell == '.')
335    cell++;
336
337  p = strchr(cell, '/');
338  if (p)
339    *p = '\0';
340  status = afs_maybe_auth_to_cell(context, at->name, cell, op, 0);
341  if (p)
342    *p = '/';
343  return status;
344}
345
346int locker_auth_to_cell(locker_context context, char *name, char *cell, int op)
347{
348  return afs_maybe_auth_to_cell(context, name, cell, op, 1);
349}
350
351static int afs_maybe_auth_to_cell(locker_context context, char *name,
352                                  char *cell, int op, int force)
353{
354  char *crealm, urealm[REALM_SZ], *user;
355  CREDENTIALS cred;
356  int status;
357  struct afsconf_dir *configdir;
358  struct afsconf_cell cellconfig;
359  struct ktc_principal server, client, xclient;
360  struct ktc_token token, xtoken;
361  long vice_id;
362  uid_t uid = geteuid(), ruid = getuid();
363
364  if (op != LOCKER_AUTH_AUTHENTICATE)
365    return LOCKER_SUCCESS;
366
367  /* Find the cell's db servers. */
368  configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
369  if (!configdir)
370    {
371      locker__error(context, "%s: Could not authenticate to AFS: "
372                    "error opening CellServDB file.\n", name);
373      return LOCKER_EAUTH;
374    }
375  status = afsconf_GetCellInfo(configdir, cell, NULL, &cellconfig);
376  afsconf_Close(configdir);
377  if (status)
378    {
379      initialize_acfg_error_table();
380      locker__error(context, "%s: Could not authenticate to AFS:\n%s while "
381                    "reading CellServDB file.\n", name, error_message(status));
382      return LOCKER_EAUTH;
383    }
384
385  /* Canonicalize the cell name. */
386  cell = cellconfig.name;
387
388  /* Get tickets for the realm containing the cell's servers. (Set uid
389   * to the user before touching the ticket file.) Try
390   * afs.cellname@realm first, and afs@realm if that doesn't exist.
391   */
392  crealm = (char *)krb_realmofhost(cellconfig.hostName[0]);
393  if (uid != ruid)
394    seteuid(ruid);
395  krb_get_tf_realm(TKT_FILE, urealm);
396  status = afs_get_cred("afs", cell, crealm, &cred);
397  if (status)
398    status = afs_get_cred("afs", "", crealm, &cred);
399  if (uid != ruid)
400    seteuid(uid);
401  if (status)
402    {
403      if (status == NO_TKT_FIL)
404        {
405          locker__error(context, "%s: Could not authenticate to AFS: %s.\n",
406                        name, krb_get_err_text(status));
407        }
408      else
409        {
410          locker__error(context, "%s: Could not authenticate to AFS cell "
411                        "%s:\n%s while getting tickets for %s.\n",
412                        name, cell, krb_get_err_text(status), crealm);
413        }
414      return LOCKER_EAUTH;
415    }
416
417  /* Create a token from the ticket. (Code stolen from aklog.) */
418  token.kvno = cred.kvno;
419  token.startTime = cred.issue_date;
420  /* ticket lifetime is in five-minutes blocks. */
421  token.endTime = cred.issue_date + ((unsigned char)cred.lifetime * 5 * 60);
422  memcpy(&token.sessionKey, cred.session, sizeof(token.sessionKey));
423  token.ticketLen = cred.ticket_st.length;
424  memcpy(token.ticket, cred.ticket_st.dat, token.ticketLen);
425
426  user = malloc(strlen(cred.pname) + strlen(cred.pinst) + strlen(urealm) + 3);
427  if (!user)
428    {
429      locker__error(context, "Out of memory authenticating to cell.\n");
430      return LOCKER_ENOMEM;
431    }
432  strcpy(user, cred.pname);
433  if (*cred.pinst)
434    sprintf(user + strlen(user), ".%s", cred.pinst);
435  if (strcasecmp(crealm, urealm))
436    sprintf(user + strlen(user), "@%s", urealm);
437
438  /* Look up principal's PTS id. */
439  initialize_pt_error_table();
440  status = pr_Initialize(0, AFSDIR_CLIENT_ETC_DIRPATH, cell);
441  if (status)
442    {
443      locker__error(context, "%s: Could not initialize AFS protection "
444                    "library while authenticating to cell \"%s\":\n%s.\n",
445                    name, cell, error_message(status));
446      free(user);
447      return LOCKER_EAUTH;
448    }
449  status = pr_SNameToId(user, &vice_id);
450  if (status)
451    {
452      locker__error(context, "%s: Could not find AFS PTS id for user \"%s\""
453                    "in cell \"%s\":\n%s.\n", name, user, cell,
454                    error_message(status));
455      free(user);
456      return LOCKER_EAUTH;
457    }
458
459  /* Select appropriate dead chicken to wave. */
460  if (vice_id == ANONYMOUSID)
461    strncpy(client.name, user, MAXKTCNAMELEN - 1);
462  else
463    sprintf(client.name, "AFS ID %d", vice_id);
464  free(user);
465  strcpy(client.instance, "");
466  strncpy(client.cell, crealm, MAXKTCREALMLEN - 1);
467  client.cell[MAXKTCREALMLEN - 1] = '\0';
468
469  strcpy(server.name, "afs");
470  strcpy(server.instance, "");
471  strncpy(server.cell, cell, MAXKTCREALMLEN - 1);
472  server.cell[MAXKTCREALMLEN - 1] = '\0';
473
474  if (!force)
475    {
476      /* Check for an existing token. */
477      status = ktc_GetToken(&server, &xtoken, sizeof(token), &xclient);
478      if (!status)
479        {
480          /* Don't get tokens as another user. */
481          if (strcmp(xclient.name, client.name))
482            return LOCKER_SUCCESS;
483
484          /* Don't get tokens that won't last longer than existing tokens. */
485          if (token.endTime <= xtoken.endTime)
486            return LOCKER_SUCCESS;
487        }
488    }
489
490  /* Store the token. */
491  status = ktc_SetToken(&server, &token, &client, 0);
492  if (status)
493    {
494      locker__error(context, "%s: Could not obtain %s tokens for cell "
495                    "%s:\n%s.\n", name, user, cell, error_message(status));
496      return LOCKER_EAUTH;
497    }
498
499  return LOCKER_SUCCESS;
500}
501
502static int afs_get_cred(char *name, char *inst, char *realm, CREDENTIALS *cred)
503{
504  int status;
505
506  status = krb_get_cred(name, inst, realm, cred);
507  if (status != KSUCCESS)
508    {
509      status = get_ad_tkt(name, inst, realm, 255);
510      if (status == KSUCCESS)
511        status = krb_get_cred(name, inst, realm, cred);
512    }
513
514  return status;
515}
516
517static int afs_zsubs(locker_context context, locker_attachent *at)
518{
519  struct ViceIoctl vio;
520  char *path, *last_component, *p, *subs[3];
521  char cell[MAXCELLCHARS + 1], vol[VNAMESIZE + 1];
522  char cellvol[MAXCELLCHARS + VNAMESIZE + 2];
523  int32 hosts[8];
524  int status = 0, pstatus;
525  struct hostent *h;
526
527  subs[0] = cell;
528  subs[1] = cellvol;
529
530  path = strdup(at->hostdir);
531  if (!path)
532    {
533      locker__error(context, "Out of memory getting zephyr subscriptions.\n");
534      return LOCKER_ENOMEM;
535    }
536
537  /* Walk down the path. At each level, add subscriptions for the cell,
538   * host, and volume where that path component lives.
539   */
540  p = path;
541  do
542    {
543      /* Move trailing NUL over one pathname component. */
544      *p = '/';
545      p = strchr(p + 1, '/');
546      if (p)
547        *p = '\0';
548
549      /* Get cell */
550      vio.in_size = 0;
551      vio.out = cell;
552      vio.out_size = sizeof(cell);
553      if (pioctl(path, VIOC_FILE_CELL_NAME, &vio, 1) != 0)
554        continue;
555
556      /* Get mountpoint name and generate cell:mountpoint. */
557      last_component = strrchr(path, '/');
558      if (last_component)
559        {
560          *last_component++ = '\0';
561          vio.in = last_component;
562        }
563      else
564        vio.in = "/";
565      vio.in_size = strlen(vio.in) + 1;
566      vio.out = vol;
567      vio.out_size = sizeof(vol);
568      pstatus = pioctl(path, VIOC_AFS_STAT_MT_PT, &vio, 1);
569      if (last_component)
570        *(last_component - 1) = '/';
571
572      if (pstatus != 0)
573        continue;
574
575      /* Get cell:volumname into cellvol, ignoring initial '#' or '%'. */
576      if (strchr(vol, ':'))
577        strcpy(cellvol, vol + 1);
578      else
579        sprintf(cellvol, "%s:%s", cell, vol + 1);
580
581      /* If there's only one site for this volume, add the hostname
582       * of the server to the subs list.
583       */
584      memset(hosts, 0, 2 * sizeof(*hosts));
585      vio.out = (caddr_t)hosts;
586      vio.out_size = sizeof(hosts);
587      if (pioctl(path, VIOCWHEREIS, &vio, 1) != 0)
588        continue;
589      if (!hosts[1])
590        {
591          h = gethostbyaddr((char *)&hosts[0], 4, AF_INET);
592          if (!h)
593            continue;
594          subs[2] = h->h_name;
595          status = locker__add_zsubs(context, subs, 3);
596        }
597      else
598        status = locker__add_zsubs(context, subs, 2);
599    }
600  while (status == LOCKER_SUCCESS && strlen(path) != strlen(at->hostdir));
601
602  free(path);
603  return status;
604}
605
606/* librxkad depends on this symbol in Transarc's des library, which we
607 * can't link with because of conflicts with our krb4 library. It never
608 * gets called though.
609 */
610void des_pcbc_init(void)
611{
612  abort();
613}
Note: See TracBrowser for help on using the repository browser.