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