001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.types; 028 029import java.io.File; 030import java.io.FileNotFoundException; 031import java.io.IOException; 032import java.nio.file.Files; 033import java.nio.file.Path; 034import java.nio.file.attribute.AclFileAttributeView; 035import java.nio.file.attribute.PosixFileAttributeView; 036import java.nio.file.attribute.PosixFilePermission; 037import java.nio.file.attribute.PosixFilePermissions; 038import java.util.Set; 039 040import org.forgerock.i18n.LocalizableMessage; 041import org.forgerock.opendj.ldap.ResultCode; 042 043import static org.opends.messages.UtilityMessages.*; 044 045/** 046 * This class provides a mechanism for setting file permissions in a 047 * more abstract manner than is provided by the underlying operating 048 * system and/or filesystem. It uses a traditional UNIX-style rwx/ugo 049 * representation for the permissions and converts them as necessary 050 * to the scheme used by the underlying platform. It does not provide 051 * any mechanism for getting file permissions, nor does it provide any 052 * way of dealing with file ownership or ACLs. 053 */ 054@org.opends.server.types.PublicAPI( 055 stability=org.opends.server.types.StabilityLevel.VOLATILE, 056 mayInstantiate=true, 057 mayExtend=false, 058 mayInvoke=true) 059public class FilePermission 060{ 061 /** 062 * The bitmask that should be used for indicating whether a file is 063 * readable by its owner. 064 */ 065 public static final int OWNER_READABLE = 0x0100; 066 067 068 069 /** 070 * The bitmask that should be used for indicating whether a file is 071 * writable by its owner. 072 */ 073 public static final int OWNER_WRITABLE = 0x0080; 074 075 076 077 /** 078 * The bitmask that should be used for indicating whether a file is 079 * executable by its owner. 080 */ 081 public static final int OWNER_EXECUTABLE = 0x0040; 082 083 084 085 /** 086 * The bitmask that should be used for indicating whether a file is 087 * readable by members of its group. 088 */ 089 public static final int GROUP_READABLE = 0x0020; 090 091 092 093 /** 094 * The bitmask that should be used for indicating whether a file is 095 * writable by members of its group. 096 */ 097 public static final int GROUP_WRITABLE = 0x0010; 098 099 100 101 /** 102 * The bitmask that should be used for indicating whether a file is 103 * executable by members of its group. 104 */ 105 public static final int GROUP_EXECUTABLE = 0x0008; 106 107 108 109 /** 110 * The bitmask that should be used for indicating whether a file is 111 * readable by users other than the owner or group members. 112 */ 113 public static final int OTHER_READABLE = 0x0004; 114 115 116 117 /** 118 * The bitmask that should be used for indicating whether a file is 119 * writable by users other than the owner or group members. 120 */ 121 public static final int OTHER_WRITABLE = 0x0002; 122 123 124 125 /** 126 * The bitmask that should be used for indicating whether a file is 127 * executable by users other than the owner or group members. 128 */ 129 public static final int OTHER_EXECUTABLE = 0x0001; 130 131 132 133 /** The encoded representation for this file permission. */ 134 private int encodedPermission; 135 136 137 138 /** 139 * Creates a new file permission object with the provided encoded 140 * representation. 141 * 142 * @param encodedPermission The encoded representation for this 143 * file permission. 144 */ 145 public FilePermission(int encodedPermission) 146 { 147 this.encodedPermission = encodedPermission; 148 } 149 150 151 152 /** 153 * Creates a new file permission with the specified rights for the 154 * file owner. Users other than the owner will not have any rights. 155 * 156 * @param ownerReadable Indicates whether the owner should have 157 * the read permission. 158 * @param ownerWritable Indicates whether the owner should have 159 * the write permission. 160 * @param ownerExecutable Indicates whether the owner should have 161 * the execute permission. 162 */ 163 public FilePermission(boolean ownerReadable, boolean ownerWritable, 164 boolean ownerExecutable) 165 { 166 encodedPermission = 0x0000; 167 168 if (ownerReadable) 169 { 170 encodedPermission |= OWNER_READABLE; 171 } 172 173 if (ownerWritable) 174 { 175 encodedPermission |= OWNER_WRITABLE; 176 } 177 178 if (ownerExecutable) 179 { 180 encodedPermission |= OWNER_EXECUTABLE; 181 } 182 } 183 184 185 186 /** 187 * Creates a new file permission with the specified rights for the 188 * file owner, group members, and other users. 189 * 190 * @param ownerReadable Indicates whether the owner should have 191 * the read permission. 192 * @param ownerWritable Indicates whether the owner should have 193 * the write permission. 194 * @param ownerExecutable Indicates whether the owner should have 195 * the execute permission. 196 * @param groupReadable Indicates whether members of the file's 197 * group should have the read permission. 198 * @param groupWritable Indicates whether members of the file's 199 * group should have the write permission. 200 * @param groupExecutable Indicates whether members of the file's 201 * group should have the execute 202 * permission. 203 * @param otherReadable Indicates whether other users should 204 * have the read permission. 205 * @param otherWritable Indicates whether other users should 206 * have the write permission. 207 * @param otherExecutable Indicates whether other users should 208 * have the execute permission. 209 */ 210 public FilePermission(boolean ownerReadable, boolean ownerWritable, 211 boolean ownerExecutable, 212 boolean groupReadable, boolean groupWritable, 213 boolean groupExecutable, 214 boolean otherReadable, boolean otherWritable, 215 boolean otherExecutable) 216 { 217 encodedPermission = 0x0000; 218 219 if (ownerReadable) 220 { 221 encodedPermission |= OWNER_READABLE; 222 } 223 224 if (ownerWritable) 225 { 226 encodedPermission |= OWNER_WRITABLE; 227 } 228 229 if (ownerExecutable) 230 { 231 encodedPermission |= OWNER_EXECUTABLE; 232 } 233 234 if (groupReadable) 235 { 236 encodedPermission |= GROUP_READABLE; 237 } 238 239 if (groupWritable) 240 { 241 encodedPermission |= GROUP_WRITABLE; 242 } 243 244 if (groupExecutable) 245 { 246 encodedPermission |= GROUP_EXECUTABLE; 247 } 248 249 if (otherReadable) 250 { 251 encodedPermission |= OTHER_READABLE; 252 } 253 254 if (otherWritable) 255 { 256 encodedPermission |= OTHER_WRITABLE; 257 } 258 259 if (otherExecutable) 260 { 261 encodedPermission |= OTHER_EXECUTABLE; 262 } 263 } 264 265 266 267 /** 268 * Indicates whether this file permission includes the owner read 269 * permission. 270 * 271 * @return <CODE>true</CODE> if this file permission includes the 272 * owner read permission, or <CODE>false</CODE> if not. 273 */ 274 public boolean isOwnerReadable() 275 { 276 return is(encodedPermission, OWNER_READABLE); 277 } 278 279 280 281 /** 282 * Indicates whether this file permission includes the owner write 283 * permission. 284 * 285 * @return <CODE>true</CODE> if this file permission includes the 286 * owner write permission, or <CODE>false</CODE> if not. 287 */ 288 public boolean isOwnerWritable() 289 { 290 return is(encodedPermission, OWNER_WRITABLE); 291 } 292 293 294 295 /** 296 * Indicates whether this file permission includes the owner execute 297 * permission. 298 * 299 * @return <CODE>true</CODE> if this file permission includes the 300 * owner execute permission, or <CODE>false</CODE> if not. 301 */ 302 public boolean isOwnerExecutable() 303 { 304 return is(encodedPermission, OWNER_EXECUTABLE); 305 } 306 307 308 309 /** 310 * Indicates whether this file permission includes the group read 311 * permission. 312 * 313 * @return <CODE>true</CODE> if this file permission includes the 314 * group read permission, or <CODE>false</CODE> if not. 315 */ 316 public boolean isGroupReadable() 317 { 318 return is(encodedPermission, GROUP_READABLE); 319 } 320 321 322 323 /** 324 * Indicates whether this file permission includes the group write 325 * permission. 326 * 327 * @return <CODE>true</CODE> if this file permission includes the 328 * group write permission, or <CODE>false</CODE> if not. 329 */ 330 public boolean isGroupWritable() 331 { 332 return is(encodedPermission, GROUP_WRITABLE); 333 } 334 335 336 337 /** 338 * Indicates whether this file permission includes the group execute 339 * permission. 340 * 341 * @return <CODE>true</CODE> if this file permission includes the 342 * group execute permission, or <CODE>false</CODE> if not. 343 */ 344 public boolean isGroupExecutable() 345 { 346 return is(encodedPermission, GROUP_EXECUTABLE); 347 } 348 349 350 351 /** 352 * Indicates whether this file permission includes the other read 353 * permission. 354 * 355 * @return <CODE>true</CODE> if this file permission includes the 356 * other read permission, or <CODE>false</CODE> if not. 357 */ 358 public boolean isOtherReadable() 359 { 360 return is(encodedPermission, OTHER_READABLE); 361 } 362 363 364 365 /** 366 * Indicates whether this file permission includes the other write 367 * permission. 368 * 369 * @return <CODE>true</CODE> if this file permission includes the 370 * other write permission, or <CODE>false</CODE> if not. 371 */ 372 public boolean isOtherWritable() 373 { 374 return is(encodedPermission, OTHER_WRITABLE); 375 } 376 377 378 379 /** 380 * Indicates whether this file permission includes the other execute 381 * permission. 382 * 383 * @return <CODE>true</CODE> if this file permission includes the 384 * other execute permission, or <CODE>false</CODE> if not. 385 */ 386 public boolean isOtherExecutable() 387 { 388 return is(encodedPermission, OTHER_EXECUTABLE); 389 } 390 391 private boolean is(int encodedPermissions, int permission) 392 { 393 return (encodedPermissions & permission) == permission; 394 } 395 396 /** 397 * Attempts to set the given permissions on the specified file. If 398 * the underlying platform does not allow the full level of 399 * granularity specified in the permissions, then an attempt will be 400 * made to set them as closely as possible to the provided 401 * permissions, erring on the side of security. 402 * 403 * @param f The file to which the permissions should be applied. 404 * @param p The permissions to apply to the file. 405 * 406 * @return <CODE>true</CODE> if the permissions (or the nearest 407 * equivalent) were successfully applied to the specified 408 * file, or <CODE>false</CODE> if was not possible to set 409 * the permissions on the current platform. 410 * 411 * @throws FileNotFoundException If the specified file does not 412 * exist. 413 * 414 * @throws DirectoryException If a problem occurs while trying to 415 * set the file permissions. 416 */ 417 public static boolean setPermissions(File f, FilePermission p) 418 throws FileNotFoundException, DirectoryException 419 { 420 if (!f.exists()) 421 { 422 throw new FileNotFoundException(ERR_FILEPERM_SET_NO_SUCH_FILE.get(f.getAbsolutePath()).toString()); 423 } 424 Path filePath = f.toPath(); 425 PosixFileAttributeView posix = Files.getFileAttributeView(filePath, PosixFileAttributeView.class); 426 if (posix != null) 427 { 428 StringBuilder posixMode = new StringBuilder(); 429 toPOSIXString(p, posixMode, "", "", ""); 430 Set<PosixFilePermission> perms = PosixFilePermissions.fromString(posixMode.toString()); 431 try 432 { 433 Files.setPosixFilePermissions(filePath, perms); 434 } 435 catch (UnsupportedOperationException | ClassCastException | IOException | SecurityException ex) 436 { 437 throw new DirectoryException(ResultCode.OTHER, ERR_FILEPERM_SET_JAVA_EXCEPTION.get(f.getAbsolutePath()), ex); 438 } 439 return true; 440 } 441 return Files.getFileAttributeView(filePath, AclFileAttributeView.class) != null; 442 } 443 444 /** 445 * Attempts to set the given permissions on the specified file. If 446 * the underlying platform does not allow the full level of 447 * granularity specified in the permissions, then an attempt will be 448 * made to set them as closely as possible to the provided 449 * permissions, erring on the side of security. 450 * 451 * @param f The file to which the permissions should be applied. 452 * @param p The permissions to apply to the file. 453 * 454 * @return <CODE>true</CODE> if the permissions (or the nearest 455 * equivalent) were successfully applied to the specified 456 * file, or <CODE>false</CODE> if was not possible to set 457 * the permissions on the current platform. 458 * 459 * The file is known to exist therefore there is no need for 460 * exists() checks. 461 */ 462 public static boolean setSafePermissions(File f, Integer p) 463 { 464 Path filePath = f.toPath(); 465 PosixFileAttributeView posix = Files.getFileAttributeView(filePath, PosixFileAttributeView.class); 466 if (posix != null) 467 { 468 StringBuilder posixMode = new StringBuilder(); 469 toPOSIXString(new FilePermission(p), posixMode, "", "", ""); 470 Set<PosixFilePermission> perms = PosixFilePermissions.fromString(posixMode.toString()); 471 try 472 { 473 Files.setPosixFilePermissions(filePath, perms); 474 } 475 catch (Exception ex) 476 { 477 return false; 478 } 479 return true; 480 } 481 return Files.getFileAttributeView(filePath, AclFileAttributeView.class) != null; 482 } 483 484 /** 485 * Retrieves a three-character string that is the UNIX mode for the 486 * provided file permission. Each character of the string will be a 487 * numeric digit from zero through seven. 488 * 489 * @param p The permission to retrieve as a UNIX mode string. 490 * 491 * @return The UNIX mode string for the provided permission. 492 */ 493 public static String toUNIXMode(FilePermission p) 494 { 495 StringBuilder buffer = new StringBuilder(3); 496 toUNIXMode(buffer, p); 497 return buffer.toString(); 498 } 499 500 501 502 /** 503 * Appends a three-character string that is the UNIX mode for the 504 * provided file permission to the given buffer. Each character of 505 * the string will be a numeric digit from zero through seven. 506 * 507 * @param buffer The buffer to which the mode string should be 508 * appended. 509 * @param p The permission to retrieve as a UNIX mode string. 510 */ 511 public static void toUNIXMode(StringBuilder buffer, 512 FilePermission p) 513 { 514 byte modeByte = 0x00; 515 if (p.isOwnerReadable()) 516 { 517 modeByte |= 0x04; 518 } 519 if (p.isOwnerWritable()) 520 { 521 modeByte |= 0x02; 522 } 523 if (p.isOwnerExecutable()) 524 { 525 modeByte |= 0x01; 526 } 527 buffer.append(modeByte); 528 529 modeByte = 0x00; 530 if (p.isGroupReadable()) 531 { 532 modeByte |= 0x04; 533 } 534 if (p.isGroupWritable()) 535 { 536 modeByte |= 0x02; 537 } 538 if (p.isGroupExecutable()) 539 { 540 modeByte |= 0x01; 541 } 542 buffer.append(modeByte); 543 544 modeByte = 0x00; 545 if (p.isOtherReadable()) 546 { 547 modeByte |= 0x04; 548 } 549 if (p.isOtherWritable()) 550 { 551 modeByte |= 0x02; 552 } 553 if (p.isOtherExecutable()) 554 { 555 modeByte |= 0x01; 556 } 557 buffer.append(modeByte); 558 } 559 560 561 562 /** 563 * Decodes the provided string as a UNIX mode and retrieves the 564 * corresponding file permission. The mode string must contain 565 * three digits between zero and seven. 566 * 567 * @param modeString The string representation of the UNIX mode to 568 * decode. 569 * 570 * @return The file permission that is equivalent to the given UNIX 571 * mode. 572 * 573 * @throws DirectoryException If the provided string is not a 574 * valid three-digit UNIX mode. 575 */ 576 public static FilePermission decodeUNIXMode(String modeString) 577 throws DirectoryException 578 { 579 if (modeString == null || modeString.length() != 3) 580 { 581 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 582 throw new DirectoryException(ResultCode.OTHER, message); 583 } 584 585 int encodedPermission = 0x0000; 586 switch (modeString.charAt(0)) 587 { 588 case '0': 589 break; 590 case '1': 591 encodedPermission |= OWNER_EXECUTABLE; 592 break; 593 case '2': 594 encodedPermission |= OWNER_WRITABLE; 595 break; 596 case '3': 597 encodedPermission |= OWNER_WRITABLE | OWNER_EXECUTABLE; 598 break; 599 case '4': 600 encodedPermission |= OWNER_READABLE; 601 break; 602 case '5': 603 encodedPermission |= OWNER_READABLE | OWNER_EXECUTABLE; 604 break; 605 case '6': 606 encodedPermission |= OWNER_READABLE | OWNER_WRITABLE; 607 break; 608 case '7': 609 encodedPermission |= OWNER_READABLE | OWNER_WRITABLE | 610 OWNER_EXECUTABLE; 611 break; 612 default: 613 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 614 throw new DirectoryException(ResultCode.OTHER, message); 615 } 616 617 switch (modeString.charAt(1)) 618 { 619 case '0': 620 break; 621 case '1': 622 encodedPermission |= GROUP_EXECUTABLE; 623 break; 624 case '2': 625 encodedPermission |= GROUP_WRITABLE; 626 break; 627 case '3': 628 encodedPermission |= GROUP_WRITABLE | GROUP_EXECUTABLE; 629 break; 630 case '4': 631 encodedPermission |= GROUP_READABLE; 632 break; 633 case '5': 634 encodedPermission |= GROUP_READABLE | GROUP_EXECUTABLE; 635 break; 636 case '6': 637 encodedPermission |= GROUP_READABLE | GROUP_WRITABLE; 638 break; 639 case '7': 640 encodedPermission |= GROUP_READABLE | GROUP_WRITABLE | 641 GROUP_EXECUTABLE; 642 break; 643 default: 644 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 645 throw new DirectoryException(ResultCode.OTHER, message); 646 } 647 648 switch (modeString.charAt(2)) 649 { 650 case '0': 651 break; 652 case '1': 653 encodedPermission |= OTHER_EXECUTABLE; 654 break; 655 case '2': 656 encodedPermission |= OTHER_WRITABLE; 657 break; 658 case '3': 659 encodedPermission |= OTHER_WRITABLE | OTHER_EXECUTABLE; 660 break; 661 case '4': 662 encodedPermission |= OTHER_READABLE; 663 break; 664 case '5': 665 encodedPermission |= OTHER_READABLE | OTHER_EXECUTABLE; 666 break; 667 case '6': 668 encodedPermission |= OTHER_READABLE | OTHER_WRITABLE; 669 break; 670 case '7': 671 encodedPermission |= OTHER_READABLE | OTHER_WRITABLE | 672 OTHER_EXECUTABLE; 673 break; 674 default: 675 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 676 throw new DirectoryException(ResultCode.OTHER, message); 677 } 678 679 return new FilePermission(encodedPermission); 680 } 681 682 683 684 /** 685 * Build a file permissions string in the "rwx" form expected by NIO, 686 * but with optional prefix strings before each three character block. 687 * <p> 688 * For example: "rwxr-xrw-" and "Owner=rwx, Group=r-x", Other=rw-". 689 * 690 * @param p The file permissions to use. 691 * @param buffer The buffer being appended to. 692 * @param owner The owner prefix, must not be null. 693 * @param group The group prefix, must not be null. 694 * @param other The other prefix, must not be null. 695 */ 696 private static void toPOSIXString(FilePermission p, StringBuilder buffer, 697 String owner, String group, String other) 698 { 699 buffer.append(owner); 700 buffer.append(p.isOwnerReadable() ? "r" : "-"); 701 buffer.append(p.isOwnerWritable() ? "w" : "-"); 702 buffer.append(p.isOwnerExecutable() ? "x" : "-"); 703 704 buffer.append(group); 705 buffer.append(p.isGroupReadable() ? "r" : "-"); 706 buffer.append(p.isGroupWritable() ? "w" : "-"); 707 buffer.append(p.isGroupExecutable() ? "x" : "-"); 708 709 buffer.append(other); 710 buffer.append(p.isOtherReadable() ? "r" : "-"); 711 buffer.append(p.isOtherWritable() ? "w" : "-"); 712 buffer.append(p.isOtherExecutable() ? "x" : "-"); 713 } 714 715 716 717 /** 718 * Retrieves a string representation of this file permission. 719 * 720 * @return A string representation of this file permission. 721 */ 722 @Override 723 public String toString() 724 { 725 StringBuilder buffer = new StringBuilder(); 726 toString(buffer); 727 return buffer.toString(); 728 } 729 730 731 732 /** 733 * Appends a string representation of this file permission to the 734 * given buffer. 735 * 736 * @param buffer The buffer to which the data should be appended. 737 */ 738 public void toString(StringBuilder buffer) 739 { 740 toPOSIXString(this, buffer, "Owner=", ", Group=", ", Other="); 741 } 742} 743