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

Revision 24069, 13.2 KB checked in by broder, 15 years ago (diff)
In locker: * Add a configure-time option to tear the NFS (and therefore the krb4) out of liblocker. * Shell out to aklog, instead of duplicating all of its code. * On Debathena, build without NFS support.
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.16 2006-08-08 21:50:09 ghudson Exp $";
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <sys/wait.h>
24#include <errno.h>
25#include <netdb.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <fcntl.h>
30
31#include <afs/stds.h>
32#include <afs/param.h>
33#include <afs/auth.h>
34#include <afs/cellconfig.h>
35#include <afs/ptserver.h>
36#include <afs/ptuser.h>
37#include <afs/venus.h>
38#include <rx/rxkad.h>
39
40/* This is defined in <afs/volume.h>, but it doesn't seem possible to
41 * include that without dragging in most of the rest of the afs
42 * includes as dependencies.
43 */
44#define VNAMESIZE 32
45
46#include <com_err.h>
47#include <krb5.h>
48
49#include "locker.h"
50#include "locker_private.h"
51
52/* Cheesy test for determining AFS 3.5. */
53#ifndef AFSCONF_CLIENTNAME
54#define AFS35
55#endif
56
57#ifdef AFS35
58#include <afs/dirpath.h>
59#else
60#define AFSDIR_CLIENT_ETC_DIRPATH AFSCONF_CLIENTNAME
61#endif
62
63/*
64 * Why doesn't AFS provide this prototype?
65 */
66extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
67
68static int afs_parse(locker_context context, char *name, char *desc,
69                     char *mountpoint, locker_attachent **at);
70static int afs_attach(locker_context context, locker_attachent *at,
71                      char *mountoptions);
72static int afs_detach(locker_context context, locker_attachent *at);
73static int afs_auth(locker_context context, locker_attachent *at,
74                    int mode, int op);
75static int afs_zsubs(locker_context context, locker_attachent *at);
76
77struct locker_ops locker__afs_ops = {
78  "AFS",
79  0,
80  afs_parse,
81  afs_attach,
82  afs_detach,
83  afs_auth,
84  afs_zsubs
85};
86
87static int afs_get_cred(krb5_context context, char *name, char *inst, char *realm,
88                        krb5_creds **creds);
89static int afs_maybe_auth_to_cell(locker_context context, char *name,
90                                  char *cell, int op, int force);
91
92static int afs_parse(locker_context context, char *name, char *desc,
93                     char *mountpoint, locker_attachent **atp)
94{
95  locker_attachent *at;
96  char *p, *dup = NULL, *lasts = NULL;
97  int status;
98
99  at = locker__new_attachent(context, &locker__afs_ops);
100  if (!at)
101    return LOCKER_ENOMEM;
102
103  if (!name)
104    {
105      /* This is an explicit description. */
106
107      if (strncmp(desc, "/afs/", 5))
108        {
109          locker__error(context, "%s: Path is not in AFS.\n", desc);
110          status = LOCKER_EPARSE;
111          goto cleanup;
112        }
113
114      at->name = strdup(desc);
115      at->hostdir = strdup(desc);
116      if (mountpoint)
117        at->mountpoint = strdup(mountpoint);
118      else
119        {
120          p = strrchr(desc, '/') + 1;
121          at->mountpoint = malloc(strlen(context->afs_mount_dir) +
122                                  strlen(p) + 2);
123          if (at->mountpoint)
124            sprintf(at->mountpoint, "%s/%s", context->afs_mount_dir, p);
125        }
126      if (!at->name || !at->hostdir || !at->mountpoint)
127        goto mem_error;
128
129      at->mode = LOCKER_AUTH_READWRITE;
130    }
131  else
132    {
133      /* A Hesiod AFS description looks like:
134       * AFS /afs/dev.mit.edu/source/src-current w /mit/source
135       */
136
137      at->name = strdup(name);
138      if (!at->name)
139        goto mem_error;
140
141      dup = strdup(desc);
142      if (!dup)
143        goto mem_error;
144
145      /* Skip "AFS". */
146      if (!strtok_r(dup, " ", &lasts))
147        goto parse_error;
148
149      /* Hostdir */
150      at->hostdir = strtok_r(NULL, " ", &lasts);
151      if (!at->hostdir)
152        goto parse_error;
153      if (strncmp(at->hostdir, "/afs/", 5))
154        {
155          locker__error(context, "%s: Path \"%s\" is not in AFS.\n", name,
156                        at->hostdir);
157          status = LOCKER_EPARSE;
158          goto cleanup;
159        }
160      at->hostdir = strdup(at->hostdir);
161      if (!at->hostdir)
162        goto mem_error;
163
164      /* Auth mode */
165      p = strtok_r(NULL, " ", &lasts);
166      if (!p || *(p + 1))
167        goto parse_error;
168
169      switch (*p)
170        {
171        case 'r':
172          at->mode = LOCKER_AUTH_READONLY;
173          break;
174        case 'w':
175          at->mode = LOCKER_AUTH_READWRITE;
176          break;
177        case 'n':
178          at->mode = LOCKER_AUTH_NONE;
179          break;
180        default:
181          locker__error(context, "%s: Unrecognized auth mode '%c' in "
182                        "description:\n%s\n", name, *p, desc);
183          status = LOCKER_EPARSE;
184          goto cleanup;
185        }
186
187      /* Mountpoint */
188      p = strtok_r(NULL, " ", &lasts);
189      if (!p)
190        goto parse_error;
191      if (mountpoint)
192        at->mountpoint = strdup(mountpoint);
193      else
194        at->mountpoint = strdup(p);
195      if (!at->mountpoint)
196        goto mem_error;
197
198      free(dup);
199      dup = NULL;
200    }
201
202  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_MOST,
203                                     &(at->mountpoint), &(at->buildfrom));
204  if (status != LOCKER_SUCCESS)
205    goto cleanup;
206
207  *atp = at;
208  return LOCKER_SUCCESS;
209
210mem_error:
211  locker__error(context, "Out of memory parsing locker description.\n");
212  status = LOCKER_ENOMEM;
213  goto cleanup;
214
215parse_error:
216  locker__error(context, "Could not parse locker description "
217                "\"%s\".\n", desc);
218  status = LOCKER_EPARSE;
219
220cleanup:
221  free(dup);
222  locker_free_attachent(context, at);
223  return status;
224}
225
226static int afs_attach(locker_context context, locker_attachent *at,
227                      char *mountoptions)
228{
229  struct stat st1, st2;
230  struct ViceIoctl vio;
231  afs_int32 hosts[8]; /* AFS docs say VIOCWHEREIS won't return more than 8. */
232  uid_t uid = geteuid();
233  int status;
234
235  /* Make sure user can read the destination, and it's a directory. */
236  if (uid != context->user)
237    seteuid(context->user);
238  status = lstat(at->hostdir, &st1);
239  if (uid != context->user)
240    seteuid(uid);
241
242  if (status == -1)
243    {
244     if (errno == ETIMEDOUT)
245        {
246          locker__error(context, "%s: Connection timed out while trying to "
247                        "attach locker.\nThis probably indicates a temporary "
248                        "problem with the file server containing\n"
249                        "this locker. Try again later.\n", at->name);
250        }
251      else
252        {
253          locker__error(context, "%s: Could not attach locker:\n%s for %s\n",
254                        at->name, strerror(errno), at->hostdir);
255        }
256      return LOCKER_EATTACH;
257    }
258  if (!S_ISDIR(st1.st_mode) && !S_ISLNK(st1.st_mode))
259    {
260      locker__error(context, "%s: Could not attach locker:\n"
261                    "%s is not a directory.\n", at->name, at->hostdir);
262      return LOCKER_EATTACH;
263    }
264
265  /* Make sure nothing is already mounted on our mountpoint. */
266  status = stat(at->mountpoint, &st2);
267  if (!status)
268    {
269      /* Assume the automounter took care of it */
270    }
271  else
272    {
273    status = symlink(at->hostdir, at->mountpoint);
274    if (status < 0)
275      {
276        locker__error(context, "%s: Could not attach locker:\n%s while "
277                    "symlinking %s to %s\n", at->name, strerror(errno),
278                    at->hostdir, at->mountpoint);
279        return LOCKER_EATTACH;
280      }
281    }
282
283  /* Find host that the locker is on, and update the attachent. */
284  memset(hosts, 0, sizeof(hosts));
285  vio.in_size = 0;
286  vio.out = (caddr_t)hosts;
287  vio.out_size = sizeof(hosts);
288  if (pioctl(at->hostdir, VIOCWHEREIS, &vio, 1) == 0)
289    {
290      /* Only record the hostaddr if the locker is on a single host.
291       * (We assume that if it's on multiple hosts, it can't fail,
292       * so we don't need to know what those hosts are.)
293       */
294      if (!hosts[1])
295        at->hostaddr.s_addr = hosts[0];
296    }
297
298  return LOCKER_SUCCESS;
299}
300
301static int afs_detach(locker_context context, locker_attachent *at)
302{
303  int status;
304
305  /* Let the automounter manage the symlink */
306  /* status = unlink(at->mountpoint);
307  if (status < 0)
308    {
309      if (errno == ENOENT)
310        {
311          locker__error(context, "%s: Locker is not attached.\n", at->name);
312          return LOCKER_ENOTATTACHED;
313        }
314      else
315        {
316          locker__error(context, "%s: Could not detach locker:\n%s while "
317                        "trying to unlink %s.\n", at->name, strerror(errno),
318                        at->mountpoint);
319          return LOCKER_EDETACH;
320        }
321    }
322  */
323  return LOCKER_SUCCESS;
324}
325
326static int afs_auth(locker_context context, locker_attachent *at,
327                    int mode, int op)
328{
329  char *cell, *p;
330  int status;
331
332  if (op != LOCKER_AUTH_AUTHENTICATE)
333    return LOCKER_SUCCESS;
334
335  /* We know (from afs_parse) that at->hostdir starts with "/afs/". */
336  cell = at->hostdir + 5;
337  /* Skip initial "." in the cell name (if this is a path to a rw volume). */
338  if (*cell == '.')
339    cell++;
340
341  p = strchr(cell, '/');
342  if (p)
343    *p = '\0';
344  status = afs_maybe_auth_to_cell(context, at->name, cell, op, 0);
345  if (p)
346    *p = '/';
347  return status;
348}
349
350int locker_auth_to_cell(locker_context context, char *name, char *cell, int op)
351{
352  return afs_maybe_auth_to_cell(context, name, cell, op, 1);
353}
354
355static int afs_maybe_auth_to_cell(locker_context context, char *name,
356                                  char *cell, int op, int force)
357{
358  uid_t uid = geteuid(), ruid = getuid();
359  pid_t pid;
360  int stat_loc;
361
362  if (op != LOCKER_AUTH_AUTHENTICATE)
363    return LOCKER_SUCCESS;
364
365  pid = fork();
366  if (pid == -1)
367    {
368      locker__error(context, "%s: Unable to fork: %s\n",
369                    name, error_message(errno));
370      return LOCKER_EAUTH;
371    }
372  else if (pid == 0)
373    {
374      if (uid != ruid)
375        seteuid(ruid);
376
377      close(0);
378      close(1);
379      close(2);
380      open("/dev/null", O_RDWR);
381      dup2(0, 1);
382      dup2(0, 2);
383
384      if (-1 == execlp("aklog", "aklog", "-c", cell, NULL))
385        exit(errno);
386    }
387  else
388    {
389      waitpid(pid, &stat_loc, 0);
390
391      if (0 != WEXITSTATUS(stat_loc))
392        {
393          locker__error(context, "%s: Error acquiring tokens: %s\n",
394                        name, error_message(WEXITSTATUS(stat_loc)));
395          return LOCKER_EAUTH;
396        }
397    }
398
399  return LOCKER_SUCCESS;
400}
401
402static int afs_get_cred(krb5_context context, char *name, char *inst, char *realm,
403                        krb5_creds **creds)
404{
405  krb5_creds increds;
406  krb5_principal client_principal = NULL;
407  krb5_ccache krb425_ccache = NULL;
408  krb5_error_code retval = 0;
409
410  memset(&increds, 0, sizeof(increds));
411  /* ANL - instance may be ptr to a null string. Pass null then */
412  retval = krb5_build_principal(context, &increds.server, strlen(realm),
413                                realm, name,
414                                (inst && strlen(inst)) ? inst : NULL,
415                                NULL);
416  if (retval)
417    goto fail;
418
419  retval = krb5_cc_default(context, &krb425_ccache);
420  if (retval)
421    goto fail;
422
423  retval = krb5_cc_get_principal(context, krb425_ccache, &client_principal);
424  if (retval)
425    goto fail;
426
427  increds.client = client_principal;
428  increds.times.endtime = 0;
429  /* Ask for DES since that is what V4 understands */
430  increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
431
432  retval = krb5_get_credentials(context, 0, krb425_ccache, &increds, creds);
433
434fail:
435  krb5_free_cred_contents(context, &increds);
436  if (krb425_ccache)
437    krb5_cc_close(context, krb425_ccache);
438  return(retval);
439}
440
441static int afs_zsubs(locker_context context, locker_attachent *at)
442{
443  struct ViceIoctl vio;
444  char *path, *last_component, *p, *subs[3];
445  char cell[MAXCELLCHARS + 1], vol[VNAMESIZE + 1];
446  char cellvol[MAXCELLCHARS + VNAMESIZE + 2];
447  afs_int32 hosts[8];
448  int status = 0, pstatus;
449  struct hostent *h;
450
451  subs[0] = cell;
452  subs[1] = cellvol;
453
454  path = strdup(at->hostdir);
455  if (!path)
456    {
457      locker__error(context, "Out of memory getting zephyr subscriptions.\n");
458      return LOCKER_ENOMEM;
459    }
460
461  /* Walk down the path. At each level, add subscriptions for the cell,
462   * host, and volume where that path component lives.
463   */
464  p = path;
465  do
466    {
467      /* Move trailing NUL over one pathname component. */
468      *p = '/';
469      p = strchr(p + 1, '/');
470      if (p)
471        *p = '\0';
472
473      /* Get cell */
474      vio.in_size = 0;
475      vio.out = cell;
476      vio.out_size = sizeof(cell);
477      if (pioctl(path, VIOC_FILE_CELL_NAME, &vio, 1) != 0)
478        continue;
479
480      /* Get mountpoint name and generate cell:mountpoint. */
481      last_component = strrchr(path, '/');
482      if (last_component)
483        {
484          *last_component++ = '\0';
485          vio.in = last_component;
486        }
487      else
488        vio.in = "/";
489      vio.in_size = strlen(vio.in) + 1;
490      vio.out = vol;
491      vio.out_size = sizeof(vol);
492      pstatus = pioctl(path, VIOC_AFS_STAT_MT_PT, &vio, 1);
493      if (last_component)
494        *(last_component - 1) = '/';
495
496      if (pstatus != 0)
497        continue;
498
499      /* Get cell:volumname into cellvol, ignoring initial '#' or '%'. */
500      if (strchr(vol, ':'))
501        strcpy(cellvol, vol + 1);
502      else
503        sprintf(cellvol, "%s:%s", cell, vol + 1);
504
505      /* If there's only one site for this volume, add the hostname
506       * of the server to the subs list.
507       */
508      memset(hosts, 0, 2 * sizeof(*hosts));
509      vio.out = (caddr_t)hosts;
510      vio.out_size = sizeof(hosts);
511      if (pioctl(path, VIOCWHEREIS, &vio, 1) != 0)
512        continue;
513      if (!hosts[1])
514        {
515          h = gethostbyaddr((char *)&hosts[0], 4, AF_INET);
516          if (!h)
517            continue;
518          subs[2] = h->h_name;
519          status = locker__add_zsubs(context, subs, 3);
520        }
521      else
522        status = locker__add_zsubs(context, subs, 2);
523    }
524  while (status == LOCKER_SUCCESS && strlen(path) != strlen(at->hostdir));
525
526  free(path);
527  return status;
528}
529
530/* librxkad depends on this symbol in Transarc's des library, which we
531 * can't link with because of conflicts with our krb4 library. It never
532 * gets called though.
533 */
534void des_pcbc_init(void)
535{
536  abort();
537}
Note: See TracBrowser for help on using the repository browser.