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

Revision 24069, 10.5 KB checked in by broder, 14 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 NFS lockers. */
17
18static const char rcsid[] = "$Id: nfs.c,v 1.5 2006-07-25 23:29:09 ghudson Exp $";
19
20#ifdef ENABLE_NFS
21
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <sys/stat.h>
25#include <sys/utsname.h>
26
27#include <ctype.h>
28#include <errno.h>
29#include <netdb.h>
30#include <pwd.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include <rpc/rpc.h>
36#ifdef NEED_SYS_FS_NFS_H
37#include <sys/fs/nfs.h>
38#endif
39#include <rpcsvc/mount.h>
40
41#include <krb.h>
42
43#endif /* ENABLE_NFS */
44
45#include "locker.h"
46#include "locker_private.h"
47
48#ifdef ENABLE_NFS
49static bool_t xdr_krbtkt(XDR *xdrs, KTEXT authp);
50#endif /* ENABLE_NFS */
51
52static int nfs_parse(locker_context context, char *name, char *desc,
53                     char *mountpoint, locker_attachent **at);
54static int nfs_auth(locker_context context, locker_attachent *at,
55                    int mode, int op);
56static int nfs_zsubs(locker_context context, locker_attachent *at);
57
58struct locker_ops locker__nfs_ops = {
59  "NFS",
60  LOCKER_FS_NEEDS_MOUNTDIR,
61  nfs_parse,
62  locker__mount,
63  locker__unmount,
64  nfs_auth,
65  nfs_zsubs
66};
67
68static int nfs_parse(locker_context context, char *name, char *desc,
69                     char *mountpoint, locker_attachent **atp)
70{
71#ifdef ENABLE_NFS
72  locker_attachent *at;
73  struct hostent *h;
74  char *dup;
75  char *p, *host, *dir, *lasts = NULL;
76  int status;
77
78  at = locker__new_attachent(context, &locker__nfs_ops);
79  if (!at)
80    return LOCKER_ENOMEM;
81
82  dup = strdup(desc);
83  if (!dup)
84    goto mem_error;
85
86  if (!name)
87    {
88      /* It's an explicit description: host:path. */
89
90      at->name = strdup(desc);
91      if (!at->name)
92        goto mem_error;
93
94      if (!(p = strchr(dup, ':')))
95        {
96          locker__error(context, "%s: No \":\" in description.\n", desc);
97          status = LOCKER_EPARSE;
98          goto cleanup;
99        }
100
101      *p = '\0';
102      host = dup;
103      dir = p + 1;
104      at->mode = LOCKER_AUTH_READWRITE;
105    }
106  else
107    {
108      /* A hesiod NFS description looks like:
109       * NFS /u1/bitbucket JASON.MIT.EDU w /mit/bitbucket
110       */
111
112      at->name = strdup(name);
113      if (!at->name)
114        goto mem_error;
115
116      /* Skip "NFS". */
117      if (!strtok_r(dup, " ", &lasts))
118        goto parse_error;
119
120      /* Directory on remote host */
121      dir = strtok_r(NULL, " ", &lasts);
122      if (!dir)
123        goto parse_error;
124
125      /* Remote host */
126      host = strtok_r(NULL, " ", &lasts);
127      if (!host)
128        goto parse_error;
129
130      /* Mount mode */
131      p = strtok_r(NULL, " ", &lasts);
132      if (!p || *(p + 1))
133        goto parse_error;
134      switch (*p)
135        {
136        case 'r':
137          at->mode = LOCKER_AUTH_READONLY;
138          break;
139        case 'm':
140          at->mode = LOCKER_AUTH_MAYBE_READWRITE;
141          break;
142        case 'w':
143          at->mode = LOCKER_AUTH_READWRITE;
144          break;
145        case 'n':
146          at->mode = LOCKER_AUTH_NONE;
147          break;
148        default:
149          locker__error(context, "%s: Unrecognized mount option '%c' in\n"
150                        "\"%s\".\n", name, *p, desc);
151          return LOCKER_EPARSE;
152        }
153
154      /* Mountpoint */
155      p = strtok_r(NULL, " ", &lasts);
156      if (!p)
157        goto parse_error;
158      if (mountpoint)
159        at->mountpoint = strdup(mountpoint);
160      else
161        at->mountpoint = strdup(p);
162      if (!at->mountpoint)
163        goto mem_error;
164    }
165
166  /* Canonicalize hostname and build hostdir. */
167  h = gethostbyname(host);
168  if (!h)
169    {
170      locker__error(context, "%s: Could not resolve hostname %s.\n",
171                    name ? name : desc, host);
172      status = LOCKER_EPARSE;
173      goto cleanup;
174    }
175  memcpy(&at->hostaddr, h->h_addr, sizeof(at->hostaddr));
176
177  at->hostdir = malloc(strlen(h->h_name) + strlen(dir) + 2);
178  if (!at->hostdir)
179    goto mem_error;
180  sprintf(at->hostdir, "%s:%s", h->h_name, dir);
181
182  if (!at->mountpoint)
183    {
184      if (mountpoint)
185        at->mountpoint = strdup(mountpoint);
186      else
187        {
188          /* Generate a default mountpoint. This defaults to
189           * /hostname/hostdir where "hostname" is the canonical
190           * hostname, in lowercase, up to the first dot, and hostdir
191           * is the path to the NFS locker on that host. BUT, if
192           * nfs_mount_dir is set, its value is prepended, and if
193           * nfs_root_hack is set, "root" is appended if hostdir is
194           * "/".
195           */
196          char *mount_dir = context->nfs_mount_dir ?
197            context->nfs_mount_dir : "";
198          int root_hack = context->nfs_root_hack && !strcmp(dir, "/");
199          int hostlen = strcspn(host, ".");
200
201          at->mountpoint = malloc(strlen(mount_dir) + 1 + hostlen +
202                              strlen(dir) + (root_hack ? 5 : 1));
203          if (at->mountpoint)
204            {
205              sprintf(at->mountpoint, "%s/%.*s%s%s", mount_dir, hostlen,
206                      host, dir, root_hack ? "root" : "");
207            }
208
209          for (p = at->mountpoint + strlen(mount_dir) + 1; *p != '/'; p++)
210            *p = tolower(*p);
211        }
212      if (!at->mountpoint)
213        goto mem_error;
214    }
215
216  status = locker__canonicalize_path(context, LOCKER_CANON_CHECK_ALL,
217                                     &(at->mountpoint), &(at->buildfrom));
218  if (status != LOCKER_SUCCESS)
219    goto cleanup;
220
221  free(dup);
222  *atp = at;
223  return LOCKER_SUCCESS;
224
225mem_error:
226  locker__error(context, "Out of memory parsing locker description.\n");
227  status = LOCKER_ENOMEM;
228  goto cleanup;
229
230parse_error:
231  locker__error(context, "Could not parse locker description "
232                "\"%s\".\n", desc);
233  status = LOCKER_EPARSE;
234
235cleanup:
236  free(dup);
237  locker_free_attachent(context, at);
238  return status;
239#else /* ENABLE_NFS */
240  return LOCKER_EPARSE;
241#endif /* ENABLE_NFS */
242}
243
244static int nfs_auth(locker_context context, locker_attachent *at,
245                    int mode, int op)
246{
247#ifdef ENABLE_NFS
248  int status, len;
249  char *host;
250
251  len = strcspn(at->hostdir, ":");
252  host = malloc(len + 1);
253  if (!host)
254    {
255      locker__error(context, "Out of memory authenticating to host.\n");
256      return LOCKER_ENOMEM;
257    }
258  memcpy(host, at->hostdir, len);
259  host[len] = '\0';
260
261  status = locker_auth_to_host(context, at->name, host, op);
262  free(host);
263  return status;
264#else /* ENABLE_NFS */
265  return LOCKER_EAUTH;
266#endif /* ENABLE_NFS */
267}
268
269int locker_auth_to_host(locker_context context, char *name, char *host,
270                        int op)
271{
272#ifdef ENABLE_NFS
273  int status, len;
274  struct timeval timeout;
275  CLIENT *cl;
276  struct utsname uts;
277  struct passwd *pw;
278  gid_t gids[NGRPS];
279  KTEXT_ST authent;
280  enum clnt_stat rpc_stat;
281
282  if (op == LOCKER_AUTH_PURGE && !context->trusted)
283    {
284      locker__error(context, "%s: You are not allowed to use the "
285                    "'purge all host mappings' option.\n", name);
286      return LOCKER_EPERM;
287    }
288
289  timeout.tv_usec = 0;
290  timeout.tv_sec = 20;
291
292  /* Get an RPC handle. */
293  cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "udp");
294  if (!cl)
295    {
296      locker__error(context, "%s: server %s not responding.\n", name, host);
297      return LOCKER_EATTACH;
298    }
299  auth_destroy(cl->cl_auth);
300
301  /* Create authunix authentication. */
302  uname(&uts);
303  pw = getpwuid(context->user);
304  len = getgroups(NGRPS, gids);
305  cl->cl_auth = authunix_create(uts.nodename, context->user,
306                                pw ? pw->pw_gid : LOCKER_DEFAULT_GID,
307                                len, gids);
308
309  /* Mapping and user purging are the only Kerberos-authenticated
310   * functions.
311   */
312  if (op == LOCKER_AUTH_AUTHENTICATE || op == LOCKER_AUTH_PURGEUSER)
313    {
314      char *realm, *instance, *src, *dst;
315
316      realm = (char *) krb_realmofhost(host);
317      instance = malloc(strcspn(host, ".") + 1);
318
319      for (src = host, dst = instance; *src && *src != '.'; )
320        *dst++ = tolower(*src++);
321      *dst = '\0';
322
323      status = krb_mk_req(&authent, LOCKER_NFS_KSERVICE, instance, realm, 0);
324      free(instance);
325      if (status != KSUCCESS)
326        {
327          auth_destroy(cl->cl_auth);
328          clnt_destroy(cl);
329          if (status == KDC_PR_UNKNOWN)
330            {
331              locker__error(context, "%s: (warning) Host %s isn't registered "
332                            "with Kerberos.%s\n", name, host,
333                            op == LOCKER_AUTH_AUTHENTICATE ?
334                            " Mapping failed." : "");
335              return LOCKER_SUCCESS;
336            }
337          else
338            {
339              locker__error(context, "%s: Could not get Kerberos ticket for "
340                            "NFS authentication:\n%s.\n", name,
341                            krb_err_txt[status]);
342              return LOCKER_EAUTH;
343            }
344        }
345
346      rpc_stat = clnt_call(cl, op, (xdrproc_t)xdr_krbtkt, (caddr_t)&authent,
347                           (xdrproc_t)xdr_void, 0, timeout);
348    }
349  else
350    rpc_stat = clnt_call(cl, op, (xdrproc_t)xdr_void, 0, (xdrproc_t)xdr_void,
351                         0, timeout);
352
353  auth_destroy(cl->cl_auth);
354  clnt_destroy(cl);
355
356  if (rpc_stat != RPC_SUCCESS)
357    {
358      switch (rpc_stat)
359        {
360        case RPC_TIMEDOUT:
361          locker__error(context, "%s: Timeout while contacting mount "
362                        "daemon on %s.\n", name, host);
363          return LOCKER_EATTACH;
364
365        case RPC_PMAPFAILURE:
366        case RPC_PROGUNAVAIL:
367        case RPC_PROGNOTREGISTERED:
368          locker__error(context, "%s: No mount daemon on %s.\n", name, host);
369          return LOCKER_EATTACH;
370
371        case RPC_AUTHERROR:
372          locker__error(context, "%s: Authentication failed to host %s.\n",
373                  name, host);
374          return LOCKER_EAUTH;
375
376        case RPC_PROCUNAVAIL:
377          locker__error(context, "%s: (warning) Mount daemon on %s doesn't "
378                        "understand UID maps.\n", name, host);
379          return LOCKER_SUCCESS;
380
381        default:
382          locker__error(context, "%s: System error contacting server %s.\n",
383                        name, host);
384          return LOCKER_EAUTH;
385        }
386    }
387
388  return LOCKER_SUCCESS;
389#else /* ENABLE_NFS */
390  return LOCKER_EAUTH;
391#endif /* ENABLE_NFS */
392}
393
394/* XDR for sending a Kerberos ticket - sends the whole KTEXT block,
395 * but this is very old lossage, and nothing that can really be fixed
396 * now.
397 */
398
399#ifdef ENABLE_NFS
400
401static bool_t xdr_krbtkt(XDR *xdrs, KTEXT authp)
402{
403  KTEXT_ST auth;
404
405  auth = *authp;
406  auth.length = htonl(authp->length);
407  return xdr_opaque(xdrs, (caddr_t)&auth, sizeof(KTEXT_ST));
408}
409
410#endif /* ENABLE_NFS */
411
412static int nfs_zsubs(locker_context context, locker_attachent *at)
413{
414#ifdef ENABLE_NFS
415  int len, status;
416  char *subs[2];
417
418  subs[0] = at->hostdir;
419
420  len = strchr(at->hostdir, ':') - at->hostdir;
421  subs[1] = malloc(len + 1);
422  if (!subs[1])
423    {
424      free(subs[0]);
425      free(subs);
426      locker__error(context, "Out of memory getting Zephyr subscriptions.\n");
427      return LOCKER_ENOMEM;
428    }
429  memcpy(subs[1], at->hostdir, len);
430  subs[1][len] = '\0';
431
432  status = locker__add_zsubs(context, subs, 2);
433  free(subs[1]);
434  return status;
435#else /* ENABLE_NFS */
436  return LOCKER_EPARSE;
437#endif
438}
Note: See TracBrowser for help on using the repository browser.