"Fossies" - the Fresh Open Source Software Archive

Member "ntfs-3g_ntfsprogs-2017.3.23/libntfs-3g/efs.c" (23 Mar 2017, 11088 Bytes) of package /linux/misc/ntfs-3g_ntfsprogs-2017.3.23.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "efs.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3g_ntfsprogs-2016.2.22_vs_3g_ntfsprogs-2017.3.23.

    1 /**
    2  * efs.c - Limited processing of encrypted files
    3  *
    4  *  This module is part of ntfs-3g library
    5  *
    6  * Copyright (c)      2009 Martin Bene
    7  * Copyright (c)      2009-2010 Jean-Pierre Andre
    8  *
    9  * This program/include file is free software; you can redistribute it and/or
   10  * modify it under the terms of the GNU General Public License as published
   11  * by the Free Software Foundation; either version 2 of the License, or
   12  * (at your option) any later version.
   13  *
   14  * This program/include file is distributed in the hope that it will be
   15  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
   16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17  * GNU General Public License for more details.
   18  *
   19  * You should have received a copy of the GNU General Public License
   20  * along with this program (in the main directory of the NTFS-3G
   21  * distribution in the file COPYING); if not, write to the Free Software
   22  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23  */
   24 
   25 #ifdef HAVE_CONFIG_H
   26 #include "config.h"
   27 #endif
   28 
   29 #ifdef HAVE_STDLIB_H
   30 #include <stdlib.h>
   31 #endif
   32 #ifdef HAVE_ERRNO_H
   33 #include <errno.h>
   34 #endif
   35 #ifdef HAVE_STRING_H
   36 #include <string.h>
   37 #endif
   38 #ifdef HAVE_SYS_STAT_H
   39 #include <sys/stat.h>
   40 #endif
   41 
   42 #ifdef HAVE_SYS_SYSMACROS_H
   43 #include <sys/sysmacros.h>
   44 #endif
   45 
   46 #include "types.h"
   47 #include "debug.h"
   48 #include "attrib.h"
   49 #include "inode.h"
   50 #include "dir.h"
   51 #include "efs.h"
   52 #include "index.h"
   53 #include "logging.h"
   54 #include "misc.h"
   55 #include "efs.h"
   56 #include "xattrs.h"
   57 
   58 static ntfschar logged_utility_stream_name[] = {
   59     const_cpu_to_le16('$'),
   60     const_cpu_to_le16('E'),
   61     const_cpu_to_le16('F'),
   62     const_cpu_to_le16('S'),
   63     const_cpu_to_le16(0)
   64 } ;
   65 
   66 
   67 /*
   68  *      Get the ntfs EFS info into an extended attribute
   69  */
   70 
   71 int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size)
   72 {
   73     EFS_ATTR_HEADER *efs_info;
   74     s64 attr_size = 0;
   75 
   76     if (ni) {
   77         if (ni->flags & FILE_ATTR_ENCRYPTED) {
   78             efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni,
   79                 AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0,
   80                 &attr_size);
   81             if (efs_info
   82                 && (le32_to_cpu(efs_info->length) == attr_size)) {
   83                 if (attr_size <= (s64)size) {
   84                     if (value)
   85                         memcpy(value,efs_info,attr_size);
   86                     else {
   87                         errno = EFAULT;
   88                         attr_size = 0;
   89                     }
   90                 } else
   91                     if (size) {
   92                         errno = ERANGE;
   93                         attr_size = 0;
   94                     }
   95                 free (efs_info);
   96             } else {
   97                 if (efs_info) {
   98                     free(efs_info);
   99                     ntfs_log_error("Bad efs_info for inode %lld\n",
  100                         (long long)ni->mft_no);
  101                 } else {
  102                     ntfs_log_error("Could not get efsinfo"
  103                         " for inode %lld\n",
  104                         (long long)ni->mft_no);
  105                 }
  106                 errno = EIO;
  107                 attr_size = 0;
  108             }
  109         } else {
  110             errno = ENODATA;
  111             ntfs_log_trace("Inode %lld is not encrypted\n",
  112                 (long long)ni->mft_no); 
  113         }
  114     }
  115     return (attr_size ? (int)attr_size : -errno);
  116 }
  117 
  118 /*
  119  *      Fix all encrypted AT_DATA attributes of an inode
  120  *
  121  *  The fix may require making an attribute non resident, which
  122  *  requires more space in the MFT record, and may cause some
  123  *  attribute to be expelled and the full record to be reorganized.
  124  *  When this happens, the search for data attributes has to be
  125  *  reinitialized.
  126  *
  127  *  Returns zero if successful.
  128  *      -1 if there is a problem.
  129  */
  130 
  131 static int fixup_loop(ntfs_inode *ni)
  132 {
  133     ntfs_attr_search_ctx *ctx;
  134     ntfs_attr *na;
  135     ATTR_RECORD *a;
  136     BOOL restart;
  137     int cnt;
  138     int maxcnt;
  139     int res = 0;
  140 
  141     maxcnt = 0;
  142     do {
  143         restart = FALSE;
  144         ctx = ntfs_attr_get_search_ctx(ni, NULL);
  145         if (!ctx) {
  146             ntfs_log_error("Failed to get ctx for efs\n");
  147             res = -1;
  148         }
  149         cnt = 0;
  150         while (!restart && !res
  151             && !ntfs_attr_lookup(AT_DATA, NULL, 0, 
  152                    CASE_SENSITIVE, 0, NULL, 0, ctx)) {
  153             cnt++;
  154             a = ctx->attr;
  155             na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
  156                 (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
  157                 a->name_length);
  158             if (!na) {
  159                 ntfs_log_error("can't open DATA Attribute\n");
  160                 res = -1;
  161             }
  162             if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) {
  163                 if (!NAttrNonResident(na)
  164                    && ntfs_attr_make_non_resident(na, ctx)) {
  165                 /*
  166                  * ntfs_attr_make_non_resident fails if there
  167                  * is not enough space in the MFT record.
  168                  * When this happens, force making non-resident
  169                  * so that some other attribute is expelled.
  170                  */
  171                     if (ntfs_attr_force_non_resident(na)) {
  172                         res = -1;
  173                     } else {
  174                     /* make sure there is some progress */
  175                         if (cnt <= maxcnt) {
  176                             errno = EIO;
  177                             ntfs_log_error("Multiple failure"
  178                                 " making non resident\n");
  179                             res = -1;
  180                         } else {
  181                             ntfs_attr_put_search_ctx(ctx);
  182                             ctx = (ntfs_attr_search_ctx*)NULL;
  183                             restart = TRUE;
  184                             maxcnt = cnt;
  185                         }
  186                     }
  187                 }
  188                 if (!restart && !res
  189                     && ntfs_efs_fixup_attribute(ctx, na)) {
  190                     ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
  191                     res = -1;
  192                 }
  193             }
  194         if (na)
  195             ntfs_attr_close(na);
  196         }
  197     } while (restart && !res);
  198     if (ctx)
  199         ntfs_attr_put_search_ctx(ctx);
  200     return (res);
  201 }
  202 
  203 /*
  204  *      Set the efs data from an extended attribute
  205  *  Warning : the new data is not checked
  206  *  Returns 0, or -1 if there is a problem
  207  */
  208 
  209 int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
  210             int flags)
  211             
  212 {
  213     int res;
  214     int written;
  215     ntfs_attr *na;
  216     const EFS_ATTR_HEADER *info_header;
  217 
  218     res = 0;
  219     if (ni && value && size) {
  220         if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) {
  221             if (ni->flags & FILE_ATTR_ENCRYPTED) {
  222                 ntfs_log_trace("Inode %lld already encrypted\n",
  223                         (long long)ni->mft_no);
  224                 errno = EEXIST;
  225             } else {
  226                 /*
  227                  * Possible problem : if encrypted file was
  228                  * restored in a compressed directory, it was
  229                  * restored as compressed.
  230                  * TODO : decompress first.
  231                  */
  232                 ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
  233                     (long long)ni->mft_no);
  234                 errno = EIO;
  235             }
  236             return -1;
  237         }
  238         info_header = (const EFS_ATTR_HEADER*)value;
  239             /* make sure we get a likely efsinfo */
  240         if (le32_to_cpu(info_header->length) != size) {
  241             errno = EINVAL;
  242             return (-1);
  243         }
  244         if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM,
  245                 (ntfschar*)NULL,0)) {
  246             if (!(flags & XATTR_REPLACE)) {
  247             /*
  248              * no logged_utility_stream attribute : add one,
  249              * apparently, this does not feed the new value in
  250              */
  251                 res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM,
  252                     logged_utility_stream_name,4,
  253                     (u8*)NULL,(s64)size);
  254             } else {
  255                 errno = ENODATA;
  256                 res = -1;
  257             }
  258         } else {
  259             errno = EEXIST;
  260             res = -1;
  261         }
  262         if (!res) {
  263             /*
  264              * open and update the existing efs data
  265              */
  266             na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM,
  267                 logged_utility_stream_name, 4);
  268             if (na) {
  269                 /* resize attribute */
  270                 res = ntfs_attr_truncate(na, (s64)size);
  271                 /* overwrite value if any */
  272                 if (!res && value) {
  273                     written = (int)ntfs_attr_pwrite(na,
  274                          (s64)0, (s64)size, value);
  275                     if (written != (s64)size) {
  276                         ntfs_log_error("Failed to "
  277                             "update efs data\n");
  278                         errno = EIO;
  279                         res = -1;
  280                     }
  281                 }
  282                 ntfs_attr_close(na);
  283             } else
  284                 res = -1;
  285         }
  286         if (!res) {
  287             /* Don't handle AT_DATA Attribute(s) if inode is a directory */
  288             if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
  289                 /* iterate over AT_DATA attributes */
  290                             /* set encrypted flag, truncate attribute to match padding bytes */
  291             
  292             if (fixup_loop(ni))
  293                 return -1;
  294             }
  295             ni->flags |= FILE_ATTR_ENCRYPTED;
  296             NInoSetDirty(ni);
  297             NInoFileNameSetDirty(ni);
  298         }
  299     } else {
  300         errno = EINVAL;
  301         res = -1;
  302     }
  303     return (res ? -1 : 0);
  304 }
  305 
  306 /*
  307  *              Fixup raw encrypted AT_DATA Attribute
  308  *     read padding length from last two bytes
  309  *     truncate attribute, make non-resident,
  310  *     set data size to match padding length
  311  *     set ATTR_IS_ENCRYPTED flag on attribute 
  312  *
  313  *  Return 0 if successful
  314  *      -1 if failed (errno tells why)
  315  */
  316 
  317 int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) 
  318 {
  319     s64 newsize;
  320     s64 oldsize;
  321     le16 appended_bytes;
  322     u16 padding_length;
  323     ntfs_inode *ni;
  324     BOOL close_ctx = FALSE;
  325 
  326     if (!na) {
  327         ntfs_log_error("no na specified for efs_fixup_attribute\n");
  328         goto err_out;
  329     }
  330     if (!ctx) {
  331         ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
  332         if (!ctx) {
  333             ntfs_log_error("Failed to get ctx for efs\n");
  334             goto err_out;
  335         }
  336         close_ctx = TRUE;
  337         if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, 
  338                 CASE_SENSITIVE, 0, NULL, 0, ctx)) {
  339             ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
  340             goto err_out;
  341         }
  342     } else {
  343         if (!NAttrNonResident(na)) {
  344             ntfs_log_error("Cannot make non resident"
  345                 " when a context has been allocated\n");
  346             goto err_out;
  347         }
  348     }
  349 
  350         /* no extra bytes are added to void attributes */
  351     oldsize = na->data_size;
  352     if (oldsize) {
  353         /* make sure size is valid for a raw encrypted stream */
  354         if ((oldsize & 511) != 2) {
  355             ntfs_log_error("Bad raw encrypted stream\n");
  356             goto err_out;
  357         }
  358         /* read padding length from last two bytes of attribute */
  359         if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) {
  360             ntfs_log_error("Error reading padding length\n");
  361             goto err_out;
  362         }
  363         padding_length = le16_to_cpu(appended_bytes);
  364         if (padding_length > 511 || padding_length > na->data_size-2) {
  365             errno = EINVAL;
  366             ntfs_log_error("invalid padding length %d for data_size %lld\n",
  367                  padding_length, (long long)oldsize);
  368             goto err_out;
  369         }
  370         newsize = oldsize - padding_length - 2;
  371         /*
  372          * truncate attribute to possibly free clusters allocated 
  373          * for the last two bytes, but do not truncate to new size
  374          * to avoid losing useful data
  375          */
  376         if (ntfs_attr_truncate(na, oldsize - 2)) {
  377             ntfs_log_error("Error truncating attribute\n");
  378             goto err_out;
  379         }
  380     } else
  381         newsize = 0;
  382 
  383     /*
  384      * Encrypted AT_DATA Attributes MUST be non-resident
  385      * This has to be done after the attribute is resized, as
  386      * resizing down to zero may cause the attribute to be made
  387      * resident.
  388      */
  389     if (!NAttrNonResident(na)
  390         && ntfs_attr_make_non_resident(na, ctx)) {
  391         if (!close_ctx
  392             || ntfs_attr_force_non_resident(na)) {
  393             ntfs_log_error("Error making DATA attribute non-resident\n");
  394             goto err_out;
  395         } else {
  396             /*
  397              * must reinitialize context after forcing
  398              * non-resident. We need a context for updating
  399              * the state, and at this point, we are sure
  400              * the context is not used elsewhere.
  401              */
  402             ntfs_attr_reinit_search_ctx(ctx);
  403             if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, 
  404                     CASE_SENSITIVE, 0, NULL, 0, ctx)) {
  405                 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
  406                 goto err_out;
  407             }
  408         }
  409     }
  410     ni = na->ni;
  411     if (!na->name_len) {
  412         ni->data_size = newsize;
  413         ni->allocated_size = na->allocated_size;
  414     }
  415     NInoSetDirty(ni);
  416     NInoFileNameSetDirty(ni);
  417 
  418     ctx->attr->data_size = cpu_to_sle64(newsize);
  419     if (sle64_to_cpu(ctx->attr->initialized_size) > newsize)
  420         ctx->attr->initialized_size = ctx->attr->data_size;
  421     ctx->attr->flags |= ATTR_IS_ENCRYPTED;
  422     if (close_ctx)
  423         ntfs_attr_put_search_ctx(ctx);
  424         
  425     return (0);
  426 err_out:
  427     if (close_ctx && ctx)
  428         ntfs_attr_put_search_ctx(ctx);
  429     return (-1);
  430 }