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