source: trunk/athena/lib/locker/attach.c @ 13285

Revision 13285, 11.1 KB checked in by danw, 25 years ago (diff)
make "attach -o ..." work
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 attaching lockers. */
17
18static const char rcsid[] = "$Id: attach.c,v 1.4 1999-06-25 21:22:58 danw Exp $";
19
20#include <errno.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <hesiod.h>
25
26#include "locker.h"
27#include "locker_private.h"
28
29static int attach_attachent(locker_context context, locker_attachent *at,
30                            int authmode, int options, char *mountoptions);
31static int check_mountoptions(locker_context context, locker_attachent *at,
32                              int authmode, int options, char **mountoptions);
33static int add_owner(locker_context context, locker_attachent *at,
34                     uid_t user);
35
36int locker_attach(locker_context context, char *filesystem, char *mountpoint,
37                  int authmode, int options, char *mountoptions,
38                  locker_attachent **atp)
39{
40  int status, astatus = LOCKER_EATTACH, order;
41  locker_attachent *at;
42
43  if (!context->trusted)
44    {
45      if (mountpoint && !context->exp_mountpoint)
46        {
47          locker__error(context, "%s: You are not allowed to specify "
48                        "an explicit mountpoint.\n", filesystem);
49          return LOCKER_EPERM;
50        }
51    }
52
53  /* Try each Hesiod entry for the named filesystem until we run out
54   * or succeed in attaching one.
55   */
56  for (order = 1; ; order++)
57    {
58      status = locker__lookup_attachent(context, filesystem,
59                                        mountpoint, order, &at);
60      if (LOCKER_LOOKUP_FAILURE(status))
61        break;
62      if (status)
63        continue;
64
65      astatus = status = locker_attach_attachent(context, at, authmode,
66                                                 options, mountoptions);
67      if (LOCKER_ATTACH_SUCCESS(status))
68        {
69          if (atp)
70            *atp = at;
71          else
72            locker_free_attachent(context, at);
73          return status;
74        }
75
76      locker_free_attachent(context, at);
77    }
78
79  return status == LOCKER_ENOENT ? astatus : status;
80}
81
82int locker_attach_explicit(locker_context context, char *type,
83                           char *desc, char *mountpoint, int authmode,
84                           int options, char *mountoptions,
85                           locker_attachent **atp)
86{
87  int status;
88  locker_attachent *at;
89
90  if (!context->trusted)
91    {
92      if (!context->exp_desc)
93        {
94          locker__error(context, "%s: You are not allowed to specify "
95                        "an explicit locker description.\n", desc);
96          return LOCKER_EPERM;
97        }
98
99      if (mountpoint && !context->exp_mountpoint)
100        {
101          locker__error(context, "%s: You are not allowed to specify "
102                        "an explicit mountpoint.\n", desc);
103          return LOCKER_EPERM;
104        }
105    }
106
107  status = locker__lookup_attachent_explicit(context, type, desc,
108                                             mountpoint, 1, &at);
109  if (status)
110    return status;
111
112  status = locker_attach_attachent(context, at, authmode,
113                                   options, mountoptions);
114
115  if (LOCKER_ATTACH_SUCCESS(status) && atp)
116    *atp = at;
117  else
118    locker_free_attachent(context, at);
119
120  return status;
121}
122
123int locker_attach_attachent(locker_context context, locker_attachent *at,
124                            int authmode, int options, char *mountoptions)
125{
126  int status = LOCKER_SUCCESS;
127  locker_attachent *ai;
128
129  if (!context->trusted)
130    {
131      if (options & LOCKER_ATTACH_OPT_OVERRIDE)
132        {
133          locker__error(context, "%s: You are not authorized to use the "
134                        "override option.\n", at->name);
135          return LOCKER_EPERM;
136        }
137      if (options & LOCKER_ATTACH_OPT_LOCK)
138        {
139          locker__error(context, "%s: You are not authorized to use the "
140                        "lock option.\n", at->name);
141          return LOCKER_EPERM;
142        }
143      if (options & LOCKER_ATTACH_OPT_ALLOW_SETUID)
144        {
145          locker__error(context, "%s: You are not authorized to use the "
146                        "setuid option.\n", at->name);
147          return LOCKER_EPERM;
148        }
149    }
150
151  /* If this is a MUL locker, attach sublockers first. */
152  for (ai = at->next; ai && status == LOCKER_SUCCESS; ai = ai->next)
153    status = attach_attachent(context, ai, authmode, options, mountoptions);
154
155  if (!LOCKER_ATTACH_SUCCESS(status))
156    {
157      locker__error(context, "%s: MUL attach failed.\n", at->name);
158      return status;
159    }
160
161  return attach_attachent(context, at, authmode, options, mountoptions);
162}
163
164/* This is the routine that does all of the work of attaching a single
165 * filesystem.
166 */
167static int attach_attachent(locker_context context, locker_attachent *at,
168                            int authmode, int options, char *mountoptions)
169{
170  int status;
171
172  if (!(options & LOCKER_ATTACH_OPT_OVERRIDE))
173    {
174      /* Make sure this locker is allowed. */
175      if (!locker__fs_ok(context, context->allow, at->fs, at->name))
176        {
177          locker__error(context, "%s: You are not allowed to attach this "
178                        "locker.\n", at->name);
179          return LOCKER_EPERM;
180        }
181
182      /* Make sure this mountpoint is allowed. */
183      if (strcmp(at->fs->name, "MUL") &&
184          !locker__fs_ok(context, context->mountpoint, at->fs, at->mountpoint))
185        {
186          locker__error(context, "%s: You are not allowed to attach a "
187                        "locker on %s\n", at->name, at->mountpoint);
188          return LOCKER_EPERM;
189        }
190    }
191
192  /* Authenticate. */
193  if (authmode == LOCKER_AUTH_DEFAULT)
194    authmode = at->mode;
195  if ((authmode != LOCKER_AUTH_NONE) &&
196      (!at->attached || (options & LOCKER_ATTACH_OPT_REAUTH)))
197    {
198      status = at->fs->auth(context, at, authmode, LOCKER_AUTH_AUTHENTICATE);
199      if (status != LOCKER_SUCCESS && authmode != LOCKER_AUTH_MAYBE_READWRITE)
200        return status;
201    }
202
203  if (!at->attached)
204    {
205      /* Check and update the mountoptions. */
206      if (locker__fs_ok(context, context->setuid, at->fs, at->name))
207        {
208          options |= LOCKER_ATTACH_OPT_ALLOW_SETUID;
209          at->flags &= ~LOCKER_FLAG_NOSUID;
210        }
211      status = check_mountoptions(context, at, authmode, options,
212                                  &mountoptions);
213      if (status != LOCKER_SUCCESS)
214        return status;
215
216      /* Build the mountpoint if all of the directories don't exist. */
217      status = locker__build_mountpoint(context, at);
218      if (status != LOCKER_SUCCESS)
219        {
220          free(mountoptions);
221          return status;
222        }
223
224      /* Attach the locker. */
225      status = at->fs->attach(context, at, mountoptions);
226      free(mountoptions);
227      if (status == LOCKER_EALREADY)
228        {
229          if (context->keep_mount)
230            at->flags |= LOCKER_FLAG_KEEP;
231        }
232      else if (status != LOCKER_SUCCESS)
233        return status;
234    }
235  else
236    status = LOCKER_SUCCESS;
237
238  /* Update attachent on disk. */
239  at->attached = 1;
240  add_owner(context, at, context->user);
241  if (options & LOCKER_ATTACH_OPT_LOCK)
242    at->flags |= LOCKER_FLAG_LOCKED;
243  if (!(options & LOCKER_ATTACH_OPT_ALLOW_SETUID))
244    at->flags |= LOCKER_FLAG_NOSUID;
245  locker__update_attachent(context, at);
246
247  /* Record zephyr subscriptions. */
248  if (options & LOCKER_ATTACH_OPT_ZEPHYR)
249    at->fs->zsubs(context, at);
250
251  return status;
252}
253
254/* This function constructs a complete string of mount options for the
255 * filesystem based on the authmode, options, mountoptions, and
256 * attach.conf.
257 */
258static int check_mountoptions(locker_context context, locker_attachent *at,
259                              int authmode, int options, char **mountoptions)
260{
261  char *mo, *req, *def, *allow, *p, *q;
262  int len;
263
264  req = locker__fs_data(context, context->reqopts, at->fs, at->name);
265  def = locker__fs_data(context, context->defopts, at->fs, at->name);
266  allow = locker__fs_data(context, context->allowopts, at->fs, at->name);
267
268  /* Malloc a string large enough to hold all necessary mount options. */
269  len = 1;
270  if (*mountoptions)
271    len += strlen(*mountoptions) + 1;
272  if (req)
273    len += strlen(req) + 1;
274  if (def)
275    len += strlen(def) + 1;
276  if (!(options & LOCKER_ATTACH_OPT_ALLOW_SETUID))
277    len += 7;
278  if (authmode != LOCKER_AUTH_NONE)
279    len += 3;
280  mo = malloc(len);
281  if (!mo)
282    {
283      locker__error(context, "Out of memory checking mountoptions.\n");
284      return LOCKER_ENOMEM;
285    }
286  *mo = '\0';
287
288  /* Later mountoptions will override earlier ones. So we copy them in
289   * in the order: defaults, authmode, mountoptions, setuid (from
290   * options), and finally the mountoptions specified by attach.conf.
291   * This means that an explicitly specified "ro" or "rw" will
292   * override the default locker mode, an explicitly specified "suid"
293   * will NOT override an enforced "nosuid", and nothing will override
294   * the attach.conf-mandated options. In addition to this, we have to
295   * verify that each option specified in mountoptions is allowed at
296   * all.
297   */
298
299  if (def)
300    sprintf(mo, "%s,", def);
301  if (authmode == LOCKER_AUTH_READONLY)
302    strcat(mo, "ro,");
303  else if (authmode != LOCKER_AUTH_NONE)
304    strcat(mo, "rw,");
305  p = mo + strlen(mo);
306  if (*mountoptions)
307    sprintf(p, "%s,", *mountoptions);
308
309  /* Interlude: check user-specified mountoptions */
310  while (p && *p)
311    {
312      /* Get the length of the option pointed to by p. */
313      len = strcspn(p, "=,");
314
315      /* See if this option appears in the allowed options list. */
316      q = allow;
317      while (q)
318        {
319          if (!strncmp(p, q, len))
320            break;
321
322          q = strchr(q, ',');
323          if (q)
324            q++;
325        }
326
327      if (!q)
328        {
329          /* Option not allowed. Silently discard it. */
330          q = strchr(p, ',');
331          if (q)
332            memmove(p, q + 1, strlen(q + 1) + 1);
333          else
334            *p = '\0';
335        }
336      else
337        {
338          p = strchr(p, ',');
339          if (p)
340            p++;
341        }
342    }
343
344  /* Now continue appending options. */
345  if (!(options & LOCKER_ATTACH_OPT_ALLOW_SETUID))
346    strcat(mo, "nosuid,");
347  if (req)
348    strcat(mo, req);
349
350  /* If mo ends with a comma, remove it. */
351  len = strlen(mo);
352  if (mo[len - 1] == ',')
353    mo[len - 1] = '\0';
354
355  /* Finally, go back through and remove duplicates and contradictions. */
356  p = mo;
357  while (p)
358    {
359      len = strcspn(p, "=,");
360      q = strchr(p, ',');
361
362      if (q)
363        {
364          /* Read through options, looking for a match for p. If p
365           * starts with "no", also look for a match without the "no".
366           * If p doesn't start with "no", look for a match with "no".
367           */
368          while (q)
369            {
370              q++;
371              if (!strncmp(p, q, len))
372                break;
373              if (!strncmp(p, "no", 2) && !strncmp(p + 2, q, len - 2))
374                break;
375              else if (!strncmp(q, "no", 2) && !strncmp(p, q + 2, len))
376                break;
377              q = strchr(q, ',');
378            }
379
380          /* If we found a match, delete the first occurrence. */
381          if (q)
382            {
383              q = strchr(p, ','); /* We know this won't return NULL here. */
384              memmove(p, q + 1, strlen(q + 1) + 1);
385            }
386          else
387            {
388              p = strchr(p, ',');
389              if (p)
390                p++;
391            }
392        }
393      else
394        break;
395    }
396
397  *mountoptions = mo;
398  return LOCKER_SUCCESS;
399}
400
401static int add_owner(locker_context context, locker_attachent *at, uid_t user)
402{
403  int i;
404  uid_t *new;
405
406  for (i = 0; i < at->nowners; i++)
407    {
408      if (at->owners[i] == user)
409        return LOCKER_SUCCESS;
410    }
411
412  new = realloc(at->owners, (at->nowners + 1) * sizeof(uid_t));
413  if (!new)
414    {
415      locker__error(context, "Out of memory adding new owner to "
416                    "attachtab entry.\n");
417      return LOCKER_ENOMEM;
418    }
419  at->owners = new;
420  at->owners[at->nowners++] = user;
421  return LOCKER_SUCCESS;
422}
Note: See TracBrowser for help on using the repository browser.