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

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