]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/unix.c
Use permissions the way MacOS expects, and avoid losing original permissions.
[netatalk.git] / etc / afpd / unix.c
1 /*
2  * $Id: unix.c,v 1.34 2002-05-10 21:35:41 jmarcus 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 <atalk/logger.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         LOG(log_error, logtype_afpd, "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 /* --------------------------------
118     cf AFP 3.0 page 63
119 */
120 void utommode( stat, ma )
121 struct stat             *stat;
122 struct maccess  *ma;
123 {
124     mode_t              mode;
125
126     mode = stat->st_mode;
127
128     ma->ma_world = utombits( mode );
129     mode = mode >> 3;
130
131     ma->ma_group = utombits( mode );
132     mode = mode >> 3;
133
134     ma->ma_owner = utombits( mode );
135
136     if ( (uuid == stat->st_uid) || (uuid == 0)) {
137         ma->ma_user = ma->ma_owner | AR_UOWN;
138     } else if ( gmem( stat->st_gid )) {
139         ma->ma_user = ma->ma_group;
140     } else {
141         ma->ma_user = ma->ma_world;
142     }
143
144     /*
145      * There are certain things the mac won't try if you don't have
146      * the "owner" bit set, even tho you can do these things on unix wiht
147      * only write permission.  What were the things?
148      * 
149      * FIXME and so what ?
150      */
151 #if 0
152     if ( ma->ma_user & AR_UWRITE ) {
153         ma->ma_user |= AR_UOWN;
154     }
155 #endif    
156 }
157
158
159 /*
160  * Calculate the mode for a directory using a stat() call to
161  * estimate permission.
162  *
163  * Note: the previous method, using access(), does not work correctly
164  * over NFS.
165  */
166 void accessmode( path, ma, dir )
167 char            *path;
168 struct maccess  *ma;
169 struct dir              *dir;
170 {
171     struct stat sb;
172     ma->ma_user = ma->ma_owner = 0;
173     if ( stat( path, &sb ) == 0 )
174         utommode( &sb, ma );
175     return;
176 }
177
178 int gmem( gid )
179 const gid_t     gid;
180 {
181     int         i;
182
183     for ( i = 0; i < ngroups; i++ ) {
184         if ( groups[ i ] == gid ) {
185             return( 1 );
186         }
187     }
188     return( 0 );
189 }
190
191 static __inline__ mode_t mtoubits( bits )
192 u_char  bits;
193 {
194     mode_t      mode;
195
196     mode = 0;
197
198     mode |= ( bits & AR_UREAD ) ? ( (S_IREAD | S_IEXEC) >> 6 ) : 0;
199     mode |= ( bits & AR_UWRITE ) ? ( (S_IWRITE | S_IEXEC) >> 6 ) : 0;
200     /* I don't think there's a way to set the SEARCH bit by itself on a Mac
201         mode |= ( bits & AR_USEARCH ) ? ( S_IEXEC >> 6 ) : 0; */
202
203     return( mode );
204 }
205
206 /* ----------------------------------
207    from the finder's share windows (menu--> File--> sharing...)
208    and from AFP 3.0 spec page 63 
209    the mac mode should be save somewhere 
210 */
211 mode_t mtoumode( ma )
212 struct maccess  *ma;
213 {
214     mode_t              mode;
215
216     mode = 0;
217     mode |= mtoubits( ma->ma_owner |ma->ma_world);
218     mode = mode << 3;
219
220     mode |= mtoubits( ma->ma_group |ma->ma_world);
221     mode = mode << 3;
222
223     mode |= mtoubits( ma->ma_world );
224
225     return( mode );
226 }
227
228 int stickydirmode(name, mode, dropbox)
229 char * name;
230 const mode_t mode;
231 const int dropbox;
232 {
233     int retval;
234 #ifdef DROPKLUDGE
235     int uid;
236 #endif /* DROPKLUDGE */
237
238     /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
239     retval=0;
240 #ifdef DROPKLUDGE
241     if (dropbox) {
242         if (mode & S_IWOTH) {
243             if (mode & S_IROTH);
244             else { /* if S_IWOTH and not S_IROTH */
245                 uid=geteuid();
246                 if ( seteuid(0) < 0) {
247                     LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
248                 }
249                 if ( retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~default_options.umask) ) < 0) {
250                     LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", name, strerror(errno) );
251                     return(AFPERR_ACCESS);
252                 } else {
253 #ifdef DEBUG
254                     LOG(log_info, logtype_afpd, "stickydirmode: (debug) chmod \"%s\": %s", name, strerror(retval) );
255 #endif /* DEBUG */
256                     seteuid(uid);
257                 } /* end getting retval */
258             } /* end if not & S_IROTH */
259         } else { /* end if S_IWOTH and not S_IROTH */
260 #endif /* DROPKLUDGE */
261
262             /*
263             *  Ignore EPERM errors:  We may be dealing with a directory that is
264             *  group writable, in which case chmod will fail.
265             */
266             if ( (chmod( name, (DIRBITS | mode) & ~default_options.umask ) < 0) && errno != EPERM)  {
267                 LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s",
268                     name, strerror(errno) );
269                 retval = -1;
270             }
271 #ifdef DROPKLUDGE
272         } /* end if not mode */
273     } /* end checking for "dropbox" */
274 #endif /* DROPKLUDGE */
275     return retval;
276 }
277
278 int setdeskmode( mode )
279 const mode_t    mode;
280 {
281     char                wd[ MAXPATHLEN + 1];
282     struct stat         st;
283     char                modbuf[ 12 + 1], *m;
284     struct dirent       *deskp, *subp;
285     DIR                 *desk, *sub;
286
287     if ( getcwd( wd , MAXPATHLEN) == NULL ) {
288         return( -1 );
289     }
290     if ( chdir( ".AppleDesktop" ) < 0 ) {
291         return( -1 );
292     }
293     if (( desk = opendir( "." )) == NULL ) {
294         if ( chdir( wd ) < 0 ) {
295             LOG(log_error, logtype_afpd, "setdeskmode: chdir %s: %s", wd, strerror(errno) );
296         }
297         return( -1 );
298     }
299     for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
300         if ( strcmp( deskp->d_name, "." ) == 0 ||
301                 strcmp( deskp->d_name, ".." ) == 0 || strlen( deskp->d_name ) > 2 ) {
302             continue;
303         }
304         strcpy( modbuf, deskp->d_name );
305         strcat( modbuf, "/" );
306         m = strchr( modbuf, '\0' );
307         if (( sub = opendir( deskp->d_name )) == NULL ) {
308             continue;
309         }
310         for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
311             if ( strcmp( subp->d_name, "." ) == 0 ||
312                     strcmp( subp->d_name, ".." ) == 0 ) {
313                 continue;
314             }
315             *m = '\0';
316             strcat( modbuf, subp->d_name );
317             /* XXX: need to preserve special modes */
318             if (stat(modbuf, &st) < 0) {
319                 LOG(log_error, logtype_afpd, "setdeskmode: stat %s: %s",
320                     modbuf, strerror(errno) );
321                 continue;
322             }
323
324             if (S_ISDIR(st.st_mode)) {
325                 if ( chmod( modbuf,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
326                     LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",
327                         modbuf, strerror(errno) );
328                 }
329             } else if ( chmod( modbuf,  mode & ~default_options.umask ) < 0 && errno != EPERM ) {
330                 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",
331                     modbuf, strerror(errno) );
332             }
333
334         }
335         closedir( sub );
336         /* XXX: need to preserve special modes */
337         if ( chmod( deskp->d_name,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
338             LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",
339                 deskp->d_name, strerror(errno) );
340         }
341     }
342     closedir( desk );
343     if ( chdir( wd ) < 0 ) {
344         LOG(log_error, logtype_afpd, "setdeskmode: chdir %s: %s", wd, strerror(errno) );
345         return -1;
346     }
347     /* XXX: need to preserve special modes */
348     if ( chmod( ".AppleDesktop",  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
349         LOG(log_error, logtype_afpd, "setdeskmode: chmod .AppleDesktop: %s", strerror(errno) );
350     }
351     return( 0 );
352 }
353
354 int setdirmode( mode, noadouble, dropbox )
355 const mode_t mode;
356 const int noadouble;
357 const int dropbox;
358 {
359     char                buf[ MAXPATHLEN + 1];
360     struct stat         st;
361     char                *m;
362     struct dirent       *dirp;
363     DIR                 *dir;
364
365     if (( dir = opendir( "." )) == NULL ) {
366         LOG(log_error, logtype_afpd, "setdirmode: opendir .: %s", strerror(errno) );
367         return( -1 );
368     }
369
370     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
371         if ( *dirp->d_name == '.' ) {
372             continue;
373         }
374         if ( stat( dirp->d_name, &st ) < 0 ) {
375             LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s",
376                 dirp->d_name, strerror(errno) );
377             continue;
378         }
379
380         if (S_ISREG(st.st_mode)) {
381             /* XXX: need to preserve special modes */
382             if (S_ISDIR(st.st_mode)) {
383                 if (stickydirmode(dirp->d_name, DIRBITS | mode, dropbox) < 0)
384                     return (-1);
385             } else if (stickydirmode(dirp->d_name, mode, dropbox) < 0)
386                 return (-1);
387         }
388     }
389     closedir( dir );
390     if (( dir = opendir( ".AppleDouble" )) == NULL ) {
391         if (noadouble)
392             goto setdirmode_noadouble;
393         LOG(log_error, logtype_afpd, "setdirmode: opendir .AppleDouble: %s", strerror(errno) );
394         return( -1 );
395     }
396     strcpy( buf, ".AppleDouble" );
397     strcat( buf, "/" );
398     m = strchr( buf, '\0' );
399     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
400         if ( strcmp( dirp->d_name, "." ) == 0 ||
401                 strcmp( dirp->d_name, ".." ) == 0 ) {
402             continue;
403         }
404         *m = '\0';
405         strcat( buf, dirp->d_name );
406
407         if ( stat( buf, &st ) < 0 ) {
408             LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", buf, strerror(errno) );
409             continue;
410         }
411
412         if (S_ISDIR(st.st_mode)) {
413             stickydirmode( buf, DIRBITS | mode, dropbox );
414         } else
415             stickydirmode( buf, mode, dropbox );
416     } /* end for */
417     closedir( dir );
418
419     /* XXX: use special bits to tag directory permissions */
420
421     /* XXX: need to preserve special modes */
422     if ( stickydirmode(".AppleDouble", DIRBITS | mode, dropbox) < 0 )
423         return( -1 );
424
425 setdirmode_noadouble:
426     /* XXX: need to preserve special modes */
427     if ( stickydirmode(".", DIRBITS | mode, dropbox) < 0 )
428         return( -1 );
429     return( 0 );
430 }
431
432 int setdeskowner( uid, gid )
433 const uid_t     uid;
434 const gid_t     gid;
435 {
436     char                wd[ MAXPATHLEN + 1];
437     char                modbuf[12 + 1], *m;
438     struct dirent       *deskp, *subp;
439     DIR                 *desk, *sub;
440
441     if ( getcwd( wd, MAXPATHLEN ) == NULL ) {
442         return( -1 );
443     }
444     if ( chdir( ".AppleDesktop" ) < 0 ) {
445         return( -1 );
446     }
447     if (( desk = opendir( "." )) == NULL ) {
448         if ( chdir( wd ) < 0 ) {
449             LOG(log_error, logtype_afpd, "setdeskowner: chdir %s: %s", wd, strerror(errno) );
450         }
451         return( -1 );
452     }
453     for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) {
454         if ( strcmp( deskp->d_name, "." ) == 0 ||
455                 strcmp( deskp->d_name, ".." ) == 0 ||
456                 strlen( deskp->d_name ) > 2 ) {
457             continue;
458         }
459         strcpy( modbuf, deskp->d_name );
460         strcat( modbuf, "/" );
461         m = strchr( modbuf, '\0' );
462         if (( sub = opendir( deskp->d_name )) == NULL ) {
463             continue;
464         }
465         for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) {
466             if ( strcmp( subp->d_name, "." ) == 0 ||
467                     strcmp( subp->d_name, ".." ) == 0 ) {
468                 continue;
469             }
470             *m = '\0';
471             strcat( modbuf, subp->d_name );
472             /* XXX: add special any uid, ignore group bits */
473             if ( chown( modbuf, uid, gid ) < 0 && errno != EPERM ) {
474                 LOG(log_error, logtype_afpd, "setdeskown: chown %s: %s",
475                     modbuf, strerror(errno) );
476             }
477         }
478         closedir( sub );
479         /* XXX: add special any uid, ignore group bits */
480         if ( chown( deskp->d_name, uid, gid ) < 0 && errno != EPERM ) {
481             LOG(log_error, logtype_afpd, "setdeskowner: chown %s: %s",
482                 deskp->d_name, strerror(errno) );
483         }
484     }
485     closedir( desk );
486     if ( chdir( wd ) < 0 ) {
487         LOG(log_error, logtype_afpd, "setdeskowner: chdir %s: %s", wd, strerror(errno) );
488         return -1;
489     }
490     if ( chown( ".AppleDesktop", uid, gid ) < 0 && errno != EPERM ) {
491         LOG(log_error, logtype_afpd, "setdeskowner: chown .AppleDesktop: %s",
492             strerror(errno) );
493     }
494     return( 0 );
495 }
496
497
498 /* uid/gid == 0 need to be handled as special cases. they really mean
499  * that user/group should inherit from other, but that doesn't fit
500  * into the unix permission scheme. we can get around this by
501  * co-opting some bits. */
502 int setdirowner( uid, gid, noadouble )
503 const uid_t     uid;
504 const gid_t     gid;
505 const int   noadouble;
506 {
507     char                buf[ MAXPATHLEN + 1];
508     struct stat         st;
509     char                *m;
510     struct dirent       *dirp;
511     DIR                 *dir;
512
513     if (( dir = opendir( "." )) == NULL ) {
514         return( -1 );
515     }
516     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
517         if ( *dirp->d_name == '.' ) {
518             continue;
519         };
520         if ( stat( dirp->d_name, &st ) < 0 ) {
521             LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s",
522                 dirp->d_name, strerror(errno) );
523             continue;
524         }
525         if (( st.st_mode & S_IFMT ) == S_IFREG ) {
526             if ( chown( dirp->d_name, uid, gid ) < 0 && errno != EPERM ) {
527                 LOG(log_debug, logtype_afpd, "setdirowner: chown %s: %s",
528                     dirp->d_name, strerror(errno) );
529                 /* return ( -1 ); Sometimes this is okay */
530             }
531         }
532     }
533     closedir( dir );
534     if (( dir = opendir( ".AppleDouble" )) == NULL ) {
535         if (noadouble)
536             goto setdirowner_noadouble;
537         return( -1 );
538     }
539     strcpy( buf, ".AppleDouble" );
540     strcat( buf, "/" );
541     m = strchr( buf, '\0' );
542     for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
543         if ( strcmp( dirp->d_name, "." ) == 0 ||
544                 strcmp( dirp->d_name, ".." ) == 0 ) {
545             continue;
546         }
547         *m = '\0';
548         strcat( buf, dirp->d_name );
549         if ( chown( buf, uid, gid ) < 0 && errno != EPERM ) {
550             LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
551                 uid, gid, buf, strerror(errno) );
552             /* return ( -1 ); Sometimes this is okay */
553         }
554     }
555     closedir( dir );
556
557     /*
558      * We cheat: we know that chown doesn't do anything.
559      */
560     if ( stat( ".AppleDouble", &st ) < 0 ) {
561         LOG(log_error, logtype_afpd, "setdirowner: stat .AppleDouble: %s", strerror(errno) );
562         return( -1 );
563     }
564     if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 &&
565             errno != EPERM ) {
566         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d .AppleDouble: %s",
567             uid, gid, strerror(errno) );
568         /* return ( -1 ); Sometimes this is okay */
569     }
570
571 setdirowner_noadouble:
572     if ( stat( ".", &st ) < 0 ) {
573         return( -1 );
574     }
575     if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 &&
576             errno != EPERM ) {
577         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d .: %s",
578             uid, gid, strerror(errno) );
579     }
580
581     return( 0 );
582 }