]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/unix.c
From trunk: preserve special file permissions.
[netatalk.git] / etc / afpd / unix.c
1 /*
2  * $Id: unix.c,v 1.24.2.6 2002-03-12 15:25:07 srittau Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/param.h>
18 #include <sys/stat.h>
19 #include <syslog.h>
20 #include <netatalk/endian.h>
21 #include <dirent.h>
22 #include <limits.h>
23 #include <atalk/afp.h>
24
25 /* STDC check */
26 #if STDC_HEADERS
27 #include <string.h>
28 #else /* STDC_HEADERS */
29 #ifndef HAVE_STRCHR
30 #define strchr index
31 #define strrchr index
32 #endif /* HAVE_STRCHR */
33 char *strchr (), *strrchr ();
34 #ifndef HAVE_MEMCPY
35 #define memcpy(d,s,n) bcopy ((s), (d), (n))
36 #define memmove(d,s,n) bcopy ((s), (d), (n))
37 #endif /* ! HAVE_MEMCPY */
38 #endif /* STDC_HEADERS */
39
40 #ifdef HAVE_FCNTL_H
41 #include <fcntl.h>
42 #endif /* HAVE_FCNTL_H */
43 #include "auth.h"
44 #include "directory.h"
45 #include "volume.h"
46 #include "unix.h"
47
48 /*
49  * Get the free space on a partition.
50  */
51 int ustatfs_getvolspace( vol, bfree, btotal, bsize )
52 const struct vol        *vol;
53 VolSpace    *bfree, *btotal;
54 u_int32_t   *bsize;
55 {
56     VolSpace maxVolSpace = (~(VolSpace)0);
57
58 #ifdef ultrix
59     struct fs_data      sfs;
60 #else /*ultrix*/
61     struct statfs       sfs;
62 #endif /*ultrix*/
63
64
65     if ( statfs( vol->v_path, &sfs ) < 0 ) {
66         syslog(LOG_ERR, "ustatfs_getvolspace unable to stat %s", vol->v_path);
67         return( AFPERR_PARAM );
68     }
69
70 #ifdef ultrix
71     *bfree = (VolSpace) sfs.fd_req.bfreen;
72     *bsize = 1024;
73 #else /* !ultrix */
74     *bfree = (VolSpace) sfs.f_bavail;
75     *bsize = sfs.f_frsize;
76 #endif /* ultrix */
77
78     if ( *bfree > maxVolSpace / *bsize ) {
79         *bfree = maxVolSpace;
80     } else {
81         *bfree *= *bsize;
82     }
83
84 #ifdef ultrix
85     *btotal = (VolSpace)
86               ( sfs.fd_req.btot - ( sfs.fd_req.bfree - sfs.fd_req.bfreen ));
87 #else /* !ultrix */
88     *btotal = (VolSpace)
89               ( sfs.f_blocks - ( sfs.f_bfree - sfs.f_bavail ));
90 #endif /* ultrix */
91
92     /* see similar block above comments */
93     if ( *btotal > maxVolSpace / *bsize ) {
94         *btotal = maxVolSpace;
95     } else {
96         *btotal *= *bsize;
97     }
98
99     return( AFP_OK );
100 }
101
102 static __inline__ int utombits( bits )
103 mode_t  bits;
104 {
105     int         mbits;
106
107     mbits = 0;
108
109     mbits |= ( bits & ( S_IREAD >> 6 )) ? (AR_UREAD | AR_USEARCH) : 0;
110     mbits |= ( bits & ( S_IWRITE >> 6 )) ? AR_UWRITE : 0;
111     /* Do we really need this?
112         mbits |= ( bits & ( S_IEXEC >> 6) ) ? AR_USEARCH : 0; */
113
114     return( mbits );
115 }
116
117 void utommode( stat, ma )
118 struct stat             *stat;
119 struct maccess  *ma;
120 {
121     mode_t              mode;
122
123     mode = stat->st_mode;
124
125     ma->ma_world = utombits( mode );
126     mode = mode >> 3;
127
128     ma->ma_group = utombits( mode );
129     mode = mode >> 3;
130
131     ma->ma_owner = utombits( mode );
132
133     if ( (uuid == stat->st_uid) || (uuid == 0)) {
134         ma->ma_user = ma->ma_owner | AR_UOWN;
135     } else if ( gmem( stat->st_gid )) {
136         ma->ma_user = ma->ma_group;
137     } else {
138         ma->ma_user = ma->ma_world;
139     }
140
141     /*
142      * There are certain things the mac won't try if you don't have
143      * the "owner" bit set, even tho you can do these things on unix wiht
144      * only write permission.  What were the things?
145      */
146     if ( ma->ma_user & AR_UWRITE ) {
147         ma->ma_user |= AR_UOWN;
148     }
149 }
150
151
152 /*
153  * Calculate the mode for a directory using a stat() call to
154  * estimate permission.
155  *
156  * Note: the previous method, using access(), does not work correctly
157  * over NFS.
158  */
159 void accessmode( path, ma, dir )
160 char            *path;
161 struct maccess  *ma;
162 struct dir              *dir;
163 {
164     struct stat sb;
165     ma->ma_user = ma->ma_owner = 0;
166     if ( stat( path, &sb ) == 0 )
167         utommode( &sb, ma );
168     return;
169 }
170
171 int gmem( gid )
172 const gid_t     gid;
173 {
174     int         i;
175
176     for ( i = 0; i < ngroups; i++ ) {
177         if ( groups[ i ] == gid ) {
178             return( 1 );
179         }
180     }
181     return( 0 );
182 }
183
184 static __inline__ mode_t mtoubits( bits )
185 u_char  bits;
186 {
187     mode_t      mode;
188
189     mode = 0;
190
191     mode |= ( bits & AR_UREAD ) ? ( (S_IREAD | S_IEXEC) >> 6 ) : 0;
192     mode |= ( bits & AR_UWRITE ) ? ( (S_IWRITE | S_IEXEC) >> 6 ) : 0;
193     /* I don't think there's a way to set the SEARCH bit by itself on a Mac
194         mode |= ( bits & AR_USEARCH ) ? ( S_IEXEC >> 6 ) : 0; */
195
196     return( mode );
197 }
198
199 mode_t mtoumode( ma )
200 struct maccess  *ma;
201 {
202     mode_t              mode;
203
204     mode = 0;
205     mode |= mtoubits( ma->ma_owner );
206     mode = mode << 3;
207
208     mode |= mtoubits( ma->ma_group );
209     mode = mode << 3;
210
211     mode |= mtoubits( ma->ma_world );
212
213     return( mode );
214 }
215
216 inline int stickydirmode(name, mode, dropbox)
217 char * name;
218 const mode_t mode;
219 const int dropbox;
220 {
221     int retval;
222 #ifdef DROPKLUDGE
223     int uid;
224 #endif /* DROPKLUDGE */
225
226     /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
227     retval=0;
228 #ifdef DROPKLUDGE
229     if (dropbox) {
230         if (mode & S_IWOTH) {
231             if (mode & S_IROTH);
232             else { /* if S_IWOTH and not S_IROTH */
233                 uid=geteuid();
234                 if ( seteuid(0) < 0) {
235                     syslog( LOG_ERR, "stickydirmode: unable to seteuid root: %s", strerror(errno));
236                 }
237                 if ( retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~default_options.umask) ) < 0) {
238                     syslog( LOG_ERR, "stickydirmode: chmod \"%s\": %s", name, strerror(errno) );
239                     return(AFPERR_ACCESS);
240                 } else {
241 #ifdef DEBUG
242                     syslog( LOG_INFO, "stickydirmode: (debug) chmod \"%s\"", name );
243 #endif /* DEBUG */
244                     seteuid(uid);
245                 } /* end getting retval */
246             } /* end if not & S_IROTH */
247         } else { /* end if S_IWOTH and not S_IROTH */
248 #endif /* DROPKLUDGE */
249
250             /*
251             *  Ignore EPERM errors:  We may be dealing with a directory that is
252             *  group writable, in which case chmod will fail.
253             */
254             if ( (chmod( name, (DIRBITS | mode) & ~default_options.umask ) < 0) && errno != EPERM)  {
255                 syslog( LOG_ERR, "stickydirmode: chmod \"%s\": %s",
256                         name, strerror(errno) );
257                 retval = -1;
258             }
259 #ifdef DROPKLUDGE
260         } /* end if not mode */
261     } /* end checking for "dropbox" */
262 #endif /* DROPKLUDGE */
263     return retval;
264 }
265
266 int setdeskmode( mode )
267 const mode_t    mode;
268 {
269     char                wd[ MAXPATHLEN + 1];
270     struct stat         st;
271     char                modbuf[ 12 + 1], *m;
272     struct dirent       *deskp, *subp;
273     DIR                 *desk, *sub;
274
275     if ( getcwd( wd , MAXPATHLEN) == NULL ) {
276         return( -1 );
277     }
278     if ( chdir( ".AppleDesktop" ) < 0 ) {
279         return( -1 );
280     }
281     if (( desk = opendir( "." )) == NULL ) {
282         if ( chdir( wd ) < 0 ) {
283             syslog( LOG_ERR, "setdeskmode: chdir %s: %s", wd, strerror(errno) );
284         }
285         return( -1 );
286     }
287     for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
288         if ( strcmp( deskp->d_name, "." ) == 0 ||
289                 strcmp( deskp->d_name, ".." ) == 0 || strlen( deskp->d_name ) > 2 ) {
290             continue;
291         }
292         strcpy( modbuf, deskp->d_name );
293         strcat( modbuf, "/" );
294         m = strchr( modbuf, '\0' );
295         if (( sub = opendir( deskp->d_name )) == NULL ) {
296             continue;
297         }
298         for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
299             if ( strcmp( subp->d_name, "." ) == 0 ||
300                     strcmp( subp->d_name, ".." ) == 0 ) {
301                 continue;
302             }
303             *m = '\0';
304             strcat( modbuf, subp->d_name );
305             /* XXX: need to preserve special modes */
306             if (stat(modbuf, &st) < 0) {
307                 syslog( LOG_ERR, "setdeskmode: stat %s: %s",
308                         modbuf, strerror(errno) );
309                 continue;
310             }
311
312             if (S_ISDIR(st.st_mode)) {
313                 if ( chmod( modbuf,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
314                     syslog( LOG_ERR, "setdeskmode: chmod %s: %s",
315                             modbuf, strerror(errno) );
316                 }
317             } else if ( chmod( modbuf,  mode & ~default_options.umask ) < 0 && errno != EPERM ) {
318                 syslog( LOG_ERR, "setdeskmode: chmod %s: %s",
319                         modbuf, strerror(errno) );
320             }
321
322         }
323         closedir( sub );
324         if ( chmod( deskp->d_name,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
325             syslog( LOG_ERR, "setdeskmode: chmod %s: %s",
326                     deskp->d_name, strerror(errno) );
327         }
328     }
329     closedir( desk );
330     if ( chdir( wd ) < 0 ) {
331         syslog( LOG_ERR, "setdeskmode: chdir %s: %s", wd, strerror(errno) );
332         return -1;
333     }
334     if ( chmod( ".AppleDesktop",  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
335         syslog( LOG_ERR, "setdeskmode: chmod .AppleDesktop: %s", strerror(errno) );
336     }
337     return( 0 );
338 }
339
340 int setdirmode( mode, noadouble, dropbox )
341 const mode_t mode;
342 const int noadouble;
343 const int dropbox;
344 {
345     char                buf[ MAXPATHLEN + 1];
346     struct stat         st;
347     char                *m;
348     struct dirent       *dirp;
349     DIR                 *dir;
350
351     if (( dir = opendir( "." )) == NULL ) {
352         syslog( LOG_ERR, "setdirmode: opendir .: %s", strerror(errno) );
353         return( -1 );
354     }
355
356     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
357         if ( *dirp->d_name == '.' ) {
358             continue;
359         }
360         if ( stat( dirp->d_name, &st ) < 0 ) {
361             syslog( LOG_ERR, "setdirmode: stat %s: %s",
362                     dirp->d_name, strerror(errno) );
363             continue;
364         }
365
366         if (S_ISREG(st.st_mode)) {
367             /* XXX: need to preserve special modes */
368             if (S_ISDIR(st.st_mode)) {
369                 if (stickydirmode(dirp->d_name, DIRBITS | mode, dropbox) < 0)
370                     return (-1);
371             } else if (stickydirmode(dirp->d_name, mode, dropbox) < 0)
372                 return (-1);
373         }
374     }
375     closedir( dir );
376     if (( dir = opendir( ".AppleDouble" )) == NULL ) {
377         if (noadouble)
378             goto setdirmode_noadouble;
379         syslog( LOG_ERR, "setdirmode: opendir .AppleDouble: %s", strerror(errno) );
380         return( -1 );
381     }
382     strcpy( buf, ".AppleDouble" );
383     strcat( buf, "/" );
384     m = strchr( buf, '\0' );
385     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
386         if ( strcmp( dirp->d_name, "." ) == 0 ||
387                 strcmp( dirp->d_name, ".." ) == 0 ) {
388             continue;
389         }
390         *m = '\0';
391         strcat( buf, dirp->d_name );
392
393         if ( stat( buf, &st ) < 0 ) {
394             syslog( LOG_ERR, "setdirmode: stat %s: %s", buf, strerror(errno) );
395             continue;
396         }
397
398         if (S_ISDIR(st.st_mode)) {
399             stickydirmode( buf, DIRBITS | mode, dropbox );
400         } else
401             stickydirmode( buf, mode, dropbox );
402     } /* end for */
403     closedir( dir );
404
405     /* XXX: use special bits to tag directory permissions */
406
407     /* XXX: need to preserve special modes */
408     if ( stickydirmode(".AppleDouble", DIRBITS | mode, dropbox) < 0 )
409         return( -1 );
410
411 setdirmode_noadouble:
412     /* XXX: need to preserve special modes */
413     if ( stickydirmode(".", DIRBITS | mode, dropbox) < 0 )
414         return( -1 );
415     return( 0 );
416 }
417
418 int setdeskowner( uid, gid )
419 const uid_t     uid;
420 const gid_t     gid;
421 {
422     char                wd[ MAXPATHLEN + 1];
423     char                modbuf[12 + 1], *m;
424     struct dirent       *deskp, *subp;
425     DIR                 *desk, *sub;
426
427     if ( getcwd( wd, MAXPATHLEN ) == NULL ) {
428         return( -1 );
429     }
430     if ( chdir( ".AppleDesktop" ) < 0 ) {
431         return( -1 );
432     }
433     if (( desk = opendir( "." )) == NULL ) {
434         if ( chdir( wd ) < 0 ) {
435             syslog( LOG_ERR, "setdeskowner: chdir %s: %s", wd, strerror(errno) );
436         }
437         return( -1 );
438     }
439     for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
440         if ( strcmp( deskp->d_name, "." ) == 0 ||
441                 strcmp( deskp->d_name, ".." ) == 0 ||
442                 strlen( deskp->d_name ) > 2 ) {
443             continue;
444         }
445         strcpy( modbuf, deskp->d_name );
446         strcat( modbuf, "/" );
447         m = strchr( modbuf, '\0' );
448         if (( sub = opendir( deskp->d_name )) == NULL ) {
449             continue;
450         }
451         for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
452             if ( strcmp( subp->d_name, "." ) == 0 ||
453                     strcmp( subp->d_name, ".." ) == 0 ) {
454                 continue;
455             }
456             *m = '\0';
457             strcat( modbuf, subp->d_name );
458             /* XXX: add special any uid, ignore group bits */
459             if ( chown( modbuf, uid, gid ) < 0 && errno != EPERM ) {
460                 syslog( LOG_ERR, "setdeskown: chown %s: %s",
461                         modbuf, strerror(errno) );
462             }
463         }
464         closedir( sub );
465         /* XXX: add special any uid, ignore group bits */
466         if ( chown( deskp->d_name, uid, gid ) < 0 && errno != EPERM ) {
467             syslog( LOG_ERR, "setdeskowner: chown %s: %s",
468                     deskp->d_name, strerror(errno) );
469         }
470     }
471     closedir( desk );
472     if ( chdir( wd ) < 0 ) {
473         syslog( LOG_ERR, "setdeskowner: chdir %s: %s", wd, strerror(errno) );
474         return -1;
475     }
476     if ( chown( ".AppleDesktop", uid, gid ) < 0 && errno != EPERM ) {
477         syslog( LOG_ERR, "setdeskowner: chown .AppleDesktop: %s",
478                 strerror(errno) );
479     }
480     return( 0 );
481 }
482
483
484 /* uid/gid == 0 need to be handled as special cases. they really mean
485  * that user/group should inherit from other, but that doesn't fit
486  * into the unix permission scheme. we can get around this by
487  * co-opting some bits. */
488 int setdirowner( uid, gid, noadouble )
489 const uid_t     uid;
490 const gid_t     gid;
491 const int   noadouble;
492 {
493     char                buf[ MAXPATHLEN + 1];
494     struct stat         st;
495     char                *m;
496     struct dirent       *dirp;
497     DIR                 *dir;
498
499     if (( dir = opendir( "." )) == NULL ) {
500         return( -1 );
501     }
502     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
503         if ( *dirp->d_name == '.' ) {
504             continue;
505         };
506         if ( stat( dirp->d_name, &st ) < 0 ) {
507             syslog( LOG_ERR, "setdirowner: stat %s: %s",
508                     dirp->d_name, strerror(errno) );
509             continue;
510         }
511         if (( st.st_mode & S_IFMT ) == S_IFREG ) {
512             if ( chown( dirp->d_name, uid, gid ) < 0 && errno != EPERM ) {
513                 syslog( LOG_DEBUG, "setdirowner: chown %s: %s",
514                         dirp->d_name, strerror(errno) );
515                 /* return ( -1 ); Sometimes this is okay */
516             }
517         }
518     }
519     closedir( dir );
520     if (( dir = opendir( ".AppleDouble" )) == NULL ) {
521         if (noadouble)
522             goto setdirowner_noadouble;
523         return( -1 );
524     }
525     strcpy( buf, ".AppleDouble" );
526     strcat( buf, "/" );
527     m = strchr( buf, '\0' );
528     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
529         if ( strcmp( dirp->d_name, "." ) == 0 ||
530                 strcmp( dirp->d_name, ".." ) == 0 ) {
531             continue;
532         }
533         *m = '\0';
534         strcat( buf, dirp->d_name );
535         if ( chown( buf, uid, gid ) < 0 && errno != EPERM ) {
536             syslog( LOG_DEBUG, "setdirowner: chown %d/%d %s: %s",
537                     uid, gid, buf, strerror(errno) );
538             /* return ( -1 ); Sometimes this is okay */
539         }
540     }
541     closedir( dir );
542
543     /*
544      * We cheat: we know that chown doesn't do anything.
545      */
546     if ( stat( ".AppleDouble", &st ) < 0 ) {
547         syslog( LOG_ERR, "setdirowner: stat .AppleDouble: %s", strerror(errno) );
548         return( -1 );
549     }
550     if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 &&
551             errno != EPERM ) {
552         syslog( LOG_DEBUG, "setdirowner: chown %d/%d .AppleDouble: %s",
553                 uid, gid, strerror(errno) );
554         /* return ( -1 ); Sometimes this is okay */
555     }
556
557 setdirowner_noadouble:
558     if ( stat( ".", &st ) < 0 ) {
559         return( -1 );
560     }
561     if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 &&
562             errno != EPERM ) {
563         syslog( LOG_DEBUG, "setdirowner: chown %d/%d .: %s",
564                 uid, gid, strerror(errno) );
565     }
566
567     return( 0 );
568 }