source: trunk/athena/bin/discuss/server/acl_core.c @ 15700

Revision 15700, 7.9 KB checked in by ghudson, 23 years ago (diff)
From kolya: fix buffer overflows.
Line 
1/*
2 *
3 *      Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8/*
9 *      $Id: acl_core.c,v 1.22 2001-02-28 20:44:23 ghudson Exp $
10 *
11 *      Routines for use in a server to edit access control lists remotely.
12 *      Originally written for the discuss system by Bill Sommerfeld
13 *
14 */
15
16#include <discuss/types.h>
17#include <discuss/dsc_et.h>
18#include <discuss/acl.h>
19#include "internal.h"
20#include <sys/file.h>
21#include <errno.h>
22#include <stdio.h>
23#include <sys/param.h>
24#include <string.h>
25#include "ansi.h"
26#if HAVE_FCNTL_H
27#include <fcntl.h>
28#endif
29#ifndef lint
30static const char rcsid_acl_core_c[] =
31    "$Id: acl_core.c,v 1.22 2001-02-28 20:44:23 ghudson Exp $";
32#endif /* lint */
33
34extern dsc_acl *mtg_acl;
35extern char rpc_caller [];
36extern int errno;
37extern int has_privs;
38extern char *new_string();
39
40/*
41 * Routines provided:
42 *      get_acl(obj_name) returns(code, acl)
43 *              Return the access control list for an object (meeting).
44 *      get_access(obj_name, princ_name) returns(code, modes)
45 *              Returns the access allowed for a given principal
46 *      set_access(obj_name, princ_name, modes) returns(code)
47 *              Sets the access on an object for a specific principal.
48 *      delete_access(obj_name, princ_name) returns(code)
49 *              Deletes princ_name from the access control list;
50 */
51
52/*
53 *      Locking protocol:
54 *      It is assumed that the ACL is in a file in a known directory;
55 *      the following locking protocol is used when modifying ACL's
56 *      to ensure consistancy.
57 *      1) exclusively lock the control file
58 *      2) open acl for read, read ACL.
59 *      3) modify ACL in core.
60 *      4) open #acl for write, write ACL.
61 *      5) rename #acl to acl (this is assumed to be atomic)
62 *      6) unlock control file.
63 *
64 *      The directory is locked, rather than the acl, because the file is
65 *      recreated and deleted (for atomicity reasons) on every transaction
66 *      (thank god UNIX files are lightweight). 
67 *
68 *      Since the acl in place is always consistant (and file opens
69 *      are atomic), there is no need to read-lock when getting the ACL.
70 */
71       
72static dsc_acl *read_acl(mtg_name, code_ptr, check)
73        char *mtg_name;
74        int *code_ptr;
75        int check;
76{
77        int fd = -1;
78        dsc_acl *list = NULL;
79        int result = 0;
80        int mtg_name_len = strlen(mtg_name);
81        char acl_name[MAXPATHLEN];
82       
83        if (mtg_name[0] != '/' || mtg_name_len == 0 ||
84            mtg_name_len+5 >= MAXPATHLEN || mtg_name [mtg_name_len-1] == '/') {
85                result = BAD_PATH;
86                goto punt;
87        }
88        strcpy(acl_name, mtg_name);
89        strcat(acl_name, "/acl");
90       
91        if((fd = open(acl_name, O_RDONLY, 0700)) < 0) {
92                result = errno;
93                goto punt;
94        }
95
96        errno = 0;
97        if ((list = acl_read(fd)) == (dsc_acl *) NULL) {
98                result = errno?errno:NO_ACCESS;
99                goto punt;
100        }
101       
102        if (check && !acl_check(list, rpc_caller, "s")) {
103                result = NO_ACCESS;
104                goto punt;
105        }
106        close(fd);
107        *code_ptr = result;
108        return (list);
109punt:
110        *code_ptr = result;
111        if (list) acl_destroy(list);
112        close(fd);
113       
114        return NULL;
115}
116
117get_acl(mtg_name, code, list)
118        char *mtg_name;
119        int *code;              /* RETURN */
120        dsc_acl **list;         /* RETURN */
121{
122        *list = read_acl(mtg_name, code, !has_privs);
123        return;
124}
125
126get_access(mtg_name, princ_name, modes, code)
127        char *mtg_name;
128        char *princ_name;
129        char **modes;           /* RETURN */
130        int *code;              /* RETURN */
131{
132        dsc_acl *list;
133        *modes = NULL;
134       
135        *code = 0;
136        list = read_acl(mtg_name, code, !has_privs && (strcmp(princ_name, rpc_caller) != 0));
137        if (*code) return;
138       
139        *modes = new_string(acl_get_access(list, princ_name));
140        acl_destroy(list);
141        return;
142}
143
144/*
145 * Set access.
146 */
147
148set_access(mtg_name, princ_name, modes, code)
149        char *mtg_name;
150        char *princ_name;
151        char *modes;
152        int *code;              /* RETURN */
153{
154        int lockfd;
155        char acl_name[MAXPATHLEN];
156        dsc_acl *acl;
157        char *n_modes;
158        /*
159         * We do not use open_mtg here; it does more than what we need.
160         */
161
162        n_modes = acl_canon(modes, "acdorsw", code);
163        if (*code) goto punt;
164
165        /* steps 1 and 2 performed by locked_open_mtg */
166        if ((*code = locked_open_mtg(mtg_name, &lockfd, acl_name, &acl))
167            != 0) {
168                goto punt;
169        }
170
171        /* step 3: modify ACL in core */
172        if (!has_privs && !acl_check(acl,rpc_caller,"c")) {
173                *code = NO_ACCESS;
174                locked_abort(mtg_name, lockfd, acl_name, acl);
175                goto punt;
176        }
177
178        acl_replace_access(acl, princ_name, n_modes);
179                           
180        /* step 4, 5, 6 performed by locked_close_mtg */
181        if((*code = locked_close_mtg(mtg_name, lockfd, acl_name, acl))
182           != 0) {
183                   goto punt;
184        }
185        *code = 0;
186 punt:
187        (void) free(n_modes);
188        return;
189}
190
191/*
192 * Delete access.  This should look a lot like the above..
193 */
194
195delete_access(mtg_name, princ_name, code)
196        char *mtg_name;
197        char *princ_name;
198        int *code;              /* RETURN */
199{
200        int lockfd;
201        char acl_name[MAXPATHLEN];
202        dsc_acl *acl;
203
204        /*
205         * We do not use open_mtg here; it does more than what we need.
206         */
207
208        /* steps 1 and 2 performed by locked_open_mtg */
209        if ((*code = locked_open_mtg(mtg_name, &lockfd, acl_name, &acl))
210            != 0) {
211                return;
212        }
213
214        /* step 3: modify ACL in core */
215        if (!has_privs && !acl_check(acl,rpc_caller,"c")) {
216                *code = NO_ACCESS;
217                locked_abort(mtg_name, lockfd, acl_name, acl);
218                return;
219        }
220
221        if(!acl_delete_access(acl, princ_name)) {
222                *code = NO_PRINC;
223                locked_abort(mtg_name, lockfd, acl_name, acl);
224                return;
225        }
226       
227        /* step 4, 5, 6 performed by locked_close_mtg */
228        if((*code = locked_close_mtg(mtg_name, lockfd, acl_name, acl))
229           != 0) {
230                return;
231        }
232        *code = 0;
233        return;
234}
235
236int
237locked_open_mtg(mtg_name, lockfd, acl_name, acl)
238        char *mtg_name;
239        int *lockfd;            /* RETURN */
240        char *acl_name;         /* RETURN - address of buffer */
241        dsc_acl **acl;          /* RETURN */
242{
243        int mtg_name_len = strlen (mtg_name);
244        int result;
245        int u_acl_f;
246        struct flock lock;
247        char control_name[MAXPATHLEN];
248       
249        *lockfd = -1;
250        u_acl_f = -1;
251        /* XXX historical magic number should be MAXPATHLEN */
252        if (mtg_name[0] != '/' || mtg_name_len == 0 ||
253            mtg_name_len+9 >= MAXPATHLEN || mtg_name [mtg_name_len-1] == '/') {
254                result = BAD_PATH;
255                goto punt;
256        }
257        (void) strcpy (control_name, mtg_name);
258        (void) strcat (control_name, "/control");
259        if((*lockfd = open(control_name, O_RDWR, 0700)) < 0) {
260                result = errno;
261                goto punt;
262        }
263        lock.l_type = F_WRLCK;
264        lock.l_start = 0;
265        lock.l_whence = 0;
266        lock.l_len = 0;
267        if (fcntl(*lockfd, F_SETLK, &lock) != 0)  {
268                result = errno;
269                goto punt;
270        }
271        (void) strcpy (acl_name, mtg_name);
272        (void) strcat (acl_name, "/acl");
273
274        if ((u_acl_f = open(acl_name, O_RDONLY, 0700)) < 0) {
275                if (errno == ENOENT)
276                        result = NO_SUCH_MTG;
277                else if (errno == EACCES)
278                        result = NO_ACCESS;
279                else
280                        result = BAD_PATH;
281                goto punt;
282        }
283        if (!fis_owner (u_acl_f, (int)geteuid())) {
284                result = NO_ACCESS;
285                goto punt;
286        }
287
288        *acl = acl_read (u_acl_f);
289        (void) close(u_acl_f);
290        u_acl_f = 0;
291        return 0;
292punt:
293        if (*lockfd >= 0) (void) close(*lockfd);
294        if (u_acl_f >= 0) (void) close(u_acl_f);
295        if (*acl) acl_destroy(*acl);
296
297        return result;
298}
299
300locked_abort(mtg_name, lockfd, acl_name, acl)
301        char *mtg_name;
302        int lockfd;
303        char *acl_name;
304        dsc_acl *acl;
305{
306        (void) close(lockfd);   /* unlocks, too */
307        acl_destroy(acl);
308}
309
310/*
311 * Here's the hairy one.
312 */
313
314int
315locked_close_mtg(mtg_name, lockfd, acl_name, acl)
316        char *mtg_name;
317        int lockfd;
318        char *acl_name;
319        dsc_acl *acl;
320{
321        int u_acl_f = -1;
322        int result;
323        char acl_nname[MAXPATHLEN];
324
325        /* Check for stupidity */
326        if (!has_privs && !acl_check(acl,rpc_caller,"c")) {
327                result = YOU_TWIT;
328                goto punt;
329        }
330        /*
331         * 4: Open up acl file for writing and write new acl.
332         */
333        (void) strcpy(acl_nname, acl_name);
334        (void) strcat(acl_nname, "~");
335
336        if ((u_acl_f = open(acl_nname, O_WRONLY|O_TRUNC|O_CREAT, 0600)) < 0) {
337                result = errno; /*XXX*/
338                goto punt;
339        }
340
341        if(!acl_write(u_acl_f, acl)) {
342                result = errno; /*XXX*/
343                goto punt;
344        }
345
346        (void) close(u_acl_f); u_acl_f = -1;
347       
348        /*
349         * 5: the commit point; rename the file.
350         */
351       
352        if(rename(acl_nname, acl_name) != 0)  {
353                result = errno;
354                goto punt;
355        }
356        /*
357         * 6: Cleanup.
358         */
359
360        result = 0;
361 punt:
362        (void) close(lockfd);   /* unlock */
363        acl_destroy(acl);       /* kaboom */
364        if (u_acl_f >= 0)
365                (void) close(u_acl_f);
366        return result;
367}
Note: See TracBrowser for help on using the repository browser.