1/* 2 * Copyright (c) 1990,1993 Regents of The University of Michigan. 3 * All Rights Reserved. See COPYRIGHT. 4 */ 5 6#ifdef HAVE_CONFIG_H 7#include "config.h" 8#endif /* HAVE_CONFIG_H */ 9 10#include <stdio.h> 11#include <stdlib.h> 12#include <inttypes.h> 13 14/* STDC check */ 15#ifdef STDC_HEADERS 16#include <string.h> 17#else /* STDC_HEADERS */ 18 19#ifndef HAVE_STRCHR 20#define strchr index 21#define strrchr index 22#endif /* HAVE_STRCHR */ 23char *strchr (), *strrchr (); 24 25#ifndef HAVE_MEMCPY 26#define memcpy(d,s,n) bcopy ((s), (d), (n)) 27#define memmove(d,s,n) bcopy ((s), (d), (n)) 28#endif /* ! HAVE_MEMCPY */ 29#endif /* STDC_HEADERS */ 30 31#include <errno.h> 32#include <limits.h> 33#include <sys/param.h> 34#include <atalk/logger.h> 35#include <atalk/adouble.h> 36#include <atalk/vfs.h> 37#include <atalk/afp.h> 38#include <atalk/util.h> 39#include <atalk/unix.h> 40#include <atalk/acl.h> 41 42#include "auth.h" 43#include "directory.h" 44#include "volume.h" 45#include "unix.h" 46#include "fork.h" 47#ifdef HAVE_ACLS 48#include "acls.h" 49#endif 50 51/* 52 * Get the free space on a partition. 53 */ 54int ustatfs_getvolspace(const struct vol *vol, VolSpace *bfree, VolSpace *btotal, u_int32_t *bsize) 55{ 56 VolSpace maxVolSpace = UINT64_MAX; 57 58#ifdef ultrix 59 struct fs_data sfs; 60#else /*ultrix*/ 61 struct statfs sfs; 62#endif /*ultrix*/ 63 64 if ( statfs( vol->v_path, &sfs ) < 0 ) { 65 LOG(log_error, logtype_afpd, "ustatfs_getvolspace unable to stat %s", vol->v_path); 66 return( AFPERR_PARAM ); 67 } 68 69#ifdef ultrix 70 *bfree = (VolSpace) sfs.fd_req.bfreen; 71 *bsize = 1024; 72#else /* !ultrix */ 73 *bfree = (VolSpace) sfs.f_bavail; 74 *bsize = sfs.f_frsize; 75#endif /* ultrix */ 76 77 if ( *bfree > maxVolSpace / *bsize ) { 78 *bfree = maxVolSpace; 79 } else { 80 *bfree *= *bsize; 81 } 82 83#ifdef ultrix 84 *btotal = (VolSpace) 85 ( sfs.fd_req.btot - ( sfs.fd_req.bfree - sfs.fd_req.bfreen )); 86#else /* !ultrix */ 87 *btotal = (VolSpace) 88 ( sfs.f_blocks - ( sfs.f_bfree - sfs.f_bavail )); 89#endif /* ultrix */ 90 91 /* see similar block above comments */ 92 if ( *btotal > maxVolSpace / *bsize ) { 93 *btotal = maxVolSpace; 94 } else { 95 *btotal *= *bsize; 96 } 97 98 return( AFP_OK ); 99} 100 101static int utombits(mode_t bits) 102{ 103 int mbits; 104 105 mbits = 0; 106 107 mbits |= ( bits & ( S_IREAD >> 6 )) ? AR_UREAD : 0; 108 mbits |= ( bits & ( S_IWRITE >> 6 )) ? AR_UWRITE : 0; 109 /* Do we really need this? */ 110 mbits |= ( bits & ( S_IEXEC >> 6) ) ? AR_USEARCH : 0; 111 112 return( mbits ); 113} 114 115/* -------------------------------- 116 cf AFP 3.0 page 63 117*/ 118void utommode(struct stat *stat, struct maccess *ma) 119{ 120mode_t mode; 121 122 mode = stat->st_mode; 123 ma->ma_world = utombits( mode ); 124 mode = mode >> 3; 125 126 ma->ma_group = utombits( mode ); 127 mode = mode >> 3; 128 129 ma->ma_owner = utombits( mode ); 130 131 /* ma_user is a union of all permissions but we must follow 132 * unix perm 133 */ 134 if ( (uuid == stat->st_uid) || (uuid == 0)) { 135 ma->ma_user = ma->ma_owner | AR_UOWN; 136 } 137 else if ( gmem( stat->st_gid )) { 138 ma->ma_user = ma->ma_group; 139 } 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 150 * ditto seems to care if st_uid is 0 ? 151 * was ma->ma_user & AR_UWRITE 152 * but 0 as owner is a can of worms. 153 */ 154 if ( !stat->st_uid ) { 155 ma->ma_user |= AR_UOWN; 156 } 157} 158 159#ifdef accessmode 160 161#undef accessmode 162#endif 163/* 164 * Calculate the mode for a directory using a stat() call to 165 * estimate permission. 166 * 167 * Note: the previous method, using access(), does not work correctly 168 * over NFS. 169 * 170 * dir parameter is used by AFS 171 */ 172void accessmode(const struct vol *vol, char *path, struct maccess *ma, struct dir *dir _U_, struct stat *st) 173{ 174 struct stat sb; 175 176 ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0; 177 if (!st) { 178 if (ostat(path, &sb, vol_syml_opt(vol)) != 0) 179 return; 180 st = &sb; 181 } 182 utommode( st, ma ); 183#ifdef HAVE_ACLS 184 acltoownermode(vol, path, st, ma); 185#endif 186} 187 188int gmem(const gid_t gid) 189{ 190 int i; 191 192 for ( i = 0; i < ngroups; i++ ) { 193 if ( groups[ i ] == gid ) { 194 return( 1 ); 195 } 196 } 197 return( 0 ); 198} 199 200static mode_t mtoubits(u_char bits) 201{ 202 mode_t mode; 203 204 mode = 0; 205 206 mode |= ( bits & AR_UREAD ) ? ( (S_IREAD | S_IEXEC) >> 6 ) : 0; 207 mode |= ( bits & AR_UWRITE ) ? ( (S_IWRITE | S_IEXEC) >> 6 ) : 0; 208 /* I don't think there's a way to set the SEARCH bit by itself on a Mac 209 mode |= ( bits & AR_USEARCH ) ? ( S_IEXEC >> 6 ) : 0; */ 210 211 return( mode ); 212} 213 214/* ---------------------------------- 215 from the finder's share windows (menu--> File--> sharing...) 216 and from AFP 3.0 spec page 63 217 the mac mode should be save somewhere 218*/ 219mode_t mtoumode(struct maccess *ma) 220{ 221 mode_t mode; 222 223 mode = 0; 224 mode |= mtoubits( ma->ma_owner |ma->ma_world); 225 mode = mode << 3; 226 227 mode |= mtoubits( ma->ma_group |ma->ma_world); 228 mode = mode << 3; 229 230 mode |= mtoubits( ma->ma_world ); 231 232 return( mode ); 233} 234 235#define EXEC_MODE (S_IXGRP | S_IXUSR | S_IXOTH) 236 237/* Using chmod_acl() instead of ochmod is ok here */ 238int setdeskmode(const mode_t mode) 239{ 240 char wd[ MAXPATHLEN + 1]; 241 struct stat st; 242 char modbuf[ 12 + 1], *m; 243 struct dirent *deskp, *subp; 244 DIR *desk, *sub; 245 246 if (!dir_rx_set(mode)) { 247 /* want to remove read and search access to owner it will screw the volume */ 248 return -1 ; 249 } 250 if ( getcwd( wd , MAXPATHLEN) == NULL ) { 251 return( -1 ); 252 } 253 if ( chdir( ".AppleDesktop" ) < 0 ) { 254 return( -1 ); 255 } 256 if (( desk = opendir( "." )) == NULL ) { 257 if ( chdir( wd ) < 0 ) { 258 LOG(log_error, logtype_afpd, "setdeskmode: chdir %s: %s", wd, strerror(errno) ); 259 } 260 return( -1 ); 261 } 262 for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) { 263 if ( strcmp( deskp->d_name, "." ) == 0 || 264 strcmp( deskp->d_name, ".." ) == 0 || strlen( deskp->d_name ) > 2 ) { 265 continue; 266 } 267 strcpy( modbuf, deskp->d_name ); 268 strcat( modbuf, "/" ); 269 m = strchr( modbuf, '\0' ); 270 if (( sub = opendir( deskp->d_name )) == NULL ) { 271 continue; 272 } 273 for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) { 274 if ( strcmp( subp->d_name, "." ) == 0 || 275 strcmp( subp->d_name, ".." ) == 0 ) { 276 continue; 277 } 278 *m = '\0'; 279 strcat( modbuf, subp->d_name ); 280 /* XXX: need to preserve special modes */ 281 if (stat(modbuf, &st) < 0) { 282 LOG(log_error, logtype_afpd, "setdeskmode: stat %s: %s",fullpathname(modbuf), strerror(errno) ); 283 continue; 284 } 285 286 if (S_ISDIR(st.st_mode)) { 287 if ( chmod_acl( modbuf, (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) { 288 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(modbuf), strerror(errno) ); 289 } 290 } else if ( chmod_acl( modbuf, mode & ~(default_options.umask | EXEC_MODE) ) < 0 && errno != EPERM ) { 291 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(modbuf), strerror(errno) ); 292 } 293 294 } 295 closedir( sub ); 296 /* XXX: need to preserve special modes */ 297 if ( chmod_acl( deskp->d_name, (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) { 298 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(deskp->d_name), strerror(errno) ); 299 } 300 } 301 closedir( desk ); 302 if ( chdir( wd ) < 0 ) { 303 LOG(log_error, logtype_afpd, "setdeskmode: chdir %s: %s", wd, strerror(errno) ); 304 return -1; 305 } 306 /* XXX: need to preserve special modes */ 307 if ( chmod_acl( ".AppleDesktop", (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) { 308 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s", fullpathname(".AppleDesktop"),strerror(errno) ); 309 } 310 return( 0 ); 311} 312 313/* --------------------- */ 314int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode) 315{ 316 if (!path->st_valid) { 317 of_stat(vol, path); 318 } 319 320 if (path->st_errno) { 321 return -1; 322 } 323 324 mode |= vol->v_fperm; 325 326 if (setfilmode(vol, path->u_name, mode, &path->st) < 0) 327 return -1; 328 /* we need to set write perm if read set for resource fork */ 329 return vol->vfs->vfs_setfilmode(vol, path->u_name, mode, &path->st); 330} 331 332 333/* --------------------- */ 334int setdirunixmode(const struct vol *vol, const char *name, mode_t mode) 335{ 336 337 int dropbox = (vol->v_flags & AFPVOL_DROPBOX); 338 339 LOG(log_debug, logtype_afpd, "setdirunixmode('%s', mode:%04o) {v_dperm:%04o}", 340 fullpathname(name), mode, vol->v_dperm); 341 342 mode |= vol->v_dperm; 343 344 if (dir_rx_set(mode)) { 345 /* extending right? dir first then .AppleDouble in rf_setdirmode */ 346 if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 347 return -1; 348 } 349 if (vol->vfs->vfs_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) { 350 return -1 ; 351 } 352 if (!dir_rx_set(mode)) { 353 if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 354 return -1; 355 } 356 return 0; 357} 358 359/* --------------------- */ 360int setdirmode(const struct vol *vol, const char *name, mode_t mode) 361{ 362 struct stat st; 363 struct dirent *dirp; 364 DIR *dir; 365 mode_t hf_mode; 366 int osx = vol->v_adouble == AD_VERSION2_OSX; 367 int dropbox = (vol->v_flags & AFPVOL_DROPBOX); 368 369 mode |= vol->v_dperm; 370 hf_mode = ad_hf_mode(mode); 371 372 if (dir_rx_set(mode)) { 373 /* extending right? dir first */ 374 if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 375 return -1; 376 } 377 378 if (( dir = opendir( name )) == NULL ) { 379 LOG(log_error, logtype_afpd, "setdirmode: opendir: %s", fullpathname(name), strerror(errno) ); 380 return( -1 ); 381 } 382 383 for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) { 384 /* FIXME */ 385 if ( *dirp->d_name == '.' && (!osx || dirp->d_name[1] != '_')) { 386 continue; 387 } 388 if (ostat(dirp->d_name, &st, vol_syml_opt(vol)) < 0 ) { 389 LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s",dirp->d_name, strerror(errno) ); 390 continue; 391 } 392 393 if (!S_ISDIR(st.st_mode)) { 394 int setmode = (osx && *dirp->d_name == '.')?hf_mode:mode; 395 396 if (setfilmode(vol, dirp->d_name, setmode, &st) < 0) { 397 closedir( dir ); 398 LOG(log_error, logtype_afpd, "setdirmode: chmod %s: %s",dirp->d_name, strerror(errno) ); 399 return -1; 400 } 401 } 402 } 403 closedir( dir ); 404 405 if (vol->vfs->vfs_setdirmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) { 406 return -1 ; 407 } 408 409 if (!dir_rx_set(mode)) { 410 if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 411 return -1; 412 } 413 return( 0 ); 414} 415 416/* ----------------------------- */ 417int setdeskowner(const uid_t uid, const gid_t gid) 418{ 419 char wd[ MAXPATHLEN + 1]; 420 char modbuf[12 + 1], *m; 421 struct dirent *deskp, *subp; 422 DIR *desk, *sub; 423 424 if ( getcwd( wd, MAXPATHLEN ) == NULL ) { 425 return( -1 ); 426 } 427 if ( chdir( ".AppleDesktop" ) < 0 ) { 428 return( -1 ); 429 } 430 if (( desk = opendir( "." )) == NULL ) { 431 if ( chdir( wd ) < 0 ) { 432 LOG(log_error, logtype_afpd, "setdeskowner: chdir %s: %s", wd, strerror(errno) ); 433 } 434 return( -1 ); 435 } 436 for ( deskp = readdir( desk ); deskp != NULL; deskp = readdir( desk )) { 437 if ( strcmp( deskp->d_name, "." ) == 0 || 438 strcmp( deskp->d_name, ".." ) == 0 || 439 strlen( deskp->d_name ) > 2 ) { 440 continue; 441 } 442 strcpy( modbuf, deskp->d_name ); 443 strcat( modbuf, "/" ); 444 m = strchr( modbuf, '\0' ); 445 if (( sub = opendir( deskp->d_name )) == NULL ) { 446 continue; 447 } 448 for ( subp = readdir( sub ); subp != NULL; subp = readdir( sub )) { 449 if ( strcmp( subp->d_name, "." ) == 0 || 450 strcmp( subp->d_name, ".." ) == 0 ) { 451 continue; 452 } 453 *m = '\0'; 454 strcat( modbuf, subp->d_name ); 455 /* XXX: add special any uid, ignore group bits */ 456 if ( chown( modbuf, uid, gid ) < 0 && errno != EPERM ) { 457 LOG(log_error, logtype_afpd, "setdeskown: chown %s: %s", fullpathname(modbuf), strerror(errno) ); 458 } 459 } 460 closedir( sub ); 461 /* XXX: add special any uid, ignore group bits */ 462 if ( chown( deskp->d_name, uid, gid ) < 0 && errno != EPERM ) { 463 LOG(log_error, logtype_afpd, "setdeskowner: chown %s: %s", 464 deskp->d_name, strerror(errno) ); 465 } 466 } 467 closedir( desk ); 468 if ( chdir( wd ) < 0 ) { 469 LOG(log_error, logtype_afpd, "setdeskowner: chdir %s: %s", wd, strerror(errno) ); 470 return -1; 471 } 472 if ( chown( ".AppleDesktop", uid, gid ) < 0 && errno != EPERM ) { 473 LOG(log_error, logtype_afpd, "setdeskowner: chown %s: %s", fullpathname(".AppleDouble"), strerror(errno) ); 474 } 475 return( 0 ); 476} 477 478/* ----------------------------- */ 479int setfilowner(const struct vol *vol, const uid_t uid, const gid_t gid, struct path* path) 480{ 481 482 if (!path->st_valid) { 483 of_stat(vol, path); 484 } 485 486 if (path->st_errno) { 487 return -1; 488 } 489 490 if (ochown( path->u_name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) { 491 LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s", 492 uid, gid, path->u_name, strerror(errno) ); 493 return -1; 494 } 495 496 if (vol->vfs->vfs_chown(vol, path->u_name, uid, gid ) < 0 && errno != EPERM) { 497 LOG(log_debug, logtype_afpd, "setfilowner: rf_chown %d/%d %s: %s", 498 uid, gid, path->u_name, strerror(errno) ); 499 return -1; 500 } 501 502 return 0; 503} 504 505/* --------------------------------- 506 * uid/gid == 0 need to be handled as special cases. they really mean 507 * that user/group should inherit from other, but that doesn't fit 508 * into the unix permission scheme. we can get around this by 509 * co-opting some bits. */ 510int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const gid_t gid) 511{ 512 struct stat st; 513 struct dirent *dirp; 514 DIR *dir; 515 int osx = vol->v_adouble == AD_VERSION2_OSX; 516 517 if (( dir = opendir( name )) == NULL ) { 518 return( -1 ); 519 } 520 for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) { 521 if ( *dirp->d_name == '.' && (!osx || dirp->d_name[1] != '_')) { 522 continue; 523 } 524 if (ostat(dirp->d_name, &st, vol_syml_opt(vol)) < 0 ) { 525 LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", 526 fullpathname(dirp->d_name), strerror(errno) ); 527 continue; 528 } 529 if (( st.st_mode & S_IFMT ) == S_IFREG ) { 530 if (ochown(dirp->d_name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) { 531 LOG(log_debug, logtype_afpd, "setdirowner: chown %s: %s", 532 fullpathname(dirp->d_name), strerror(errno) ); 533 /* return ( -1 ); Sometimes this is okay */ 534 } 535 } 536 } 537 closedir( dir ); 538 539 if (vol->vfs->vfs_setdirowner(vol, name, uid, gid) < 0) { 540 return -1; 541 } 542 543 if (ostat(".", &st, vol_syml_opt(vol)) < 0 ) { 544 return( -1 ); 545 } 546 if ( gid && gid != st.st_gid && ochown(".", uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) { 547 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 548 uid, gid, fullpathname("."), strerror(errno) ); 549 } 550 551 return( 0 ); 552} 553 554#if 0 555/* recursive chown()ing of a directory */ 556static int recursive_chown(const char *path, uid_t uid, gid_t gid) { 557 struct stat sbuf; 558 DIR *odir = NULL; 559 struct dirent *entry; 560 char *name; 561 int ret = 0; 562 char newpath[PATH_MAX+1]; 563 newpath[PATH_MAX] = '\0'; 564 565 if (chown(path, uid, gid) < 0) { 566 LOG(log_error, logtype_afpd, "cannot chown() file [%s] (uid = %d): %s", path, uid, strerror(errno)); 567 return -1; 568 } 569 570 if (stat(path, &sbuf) < 0) { 571 LOG(log_error, logtype_afpd, "cannot chown() file [%s] (uid = %d): %s", path, uid, strerror(errno)); 572 return -1; 573 } 574 575 if (S_ISDIR(sbuf.st_mode)) { 576 odir = opendir(path); 577 if (odir == NULL) { 578 LOG(log_error, logtype_afpd, "cannot opendir() [%s] (uid = %d): %s", path, uid, strerror(errno)); 579 goto recursive_chown_end; 580 } 581 while (NULL != (entry=readdir(odir)) ) { 582 name = entry->d_name; 583 if (name[0] == '.' && name[1] == '\0') 584 continue; 585 if (name[0] == '.' && name[1] == '.' && name[2] == '\0') 586 continue; 587 sprintf(newpath, "%s/%s", path, name); 588 if (recursive_chown(newpath, uid, gid) < 0) 589 ret = -1; 590 } /* while */ 591 } /* if */ 592 593recursive_chown_end: 594 if (odir != NULL) { 595 closedir(odir); 596 } 597 return ret; 598} 599#endif 600 601