1 /**
   2  * security.c - Handling security/ACLs in NTFS.  Part of the Linux-NTFS project.
   3  *
   4  * Copyright (c) 2004 Anton Altaparmakov
   5  *
   6  * This program/include file is free software; you can redistribute it and/or
   7  * modify it under the terms of the GNU General Public License as published
   8  * by the Free Software Foundation; either version 2 of the License, or
   9  * (at your option) any later version.
  10  *
  11  * This program/include file is distributed in the hope that it will be
  12  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14  * GNU General Public License for more details.
  15  *
  16  * You should have received a copy of the GNU General Public License
  17  * along with this program (in the main directory of the Linux-NTFS
  18  * distribution in the file COPYING); if not, write to the Free Software
  19  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20  */
  21 
  22 #ifdef HAVE_CONFIG_H
  23 #include "config.h"
  24 #endif
  25 
  26 #ifdef HAVE_STDIO_H
  27 #include <stdio.h>
  28 #endif
  29 #ifdef HAVE_STDLIB_H
  30 #include <stdlib.h>
  31 #endif
  32 #ifdef HAVE_STRING_H
  33 #include <string.h>
  34 #endif
  35 #ifdef HAVE_ERRNO_H
  36 #include <errno.h>
  37 #endif
  38 
  39 #include "compat.h"
  40 #include "types.h"
  41 #include "layout.h"
  42 #include "security.h"
  43 
  44 /*
  45  * The zero GUID.
  46  */
  47 static const GUID __zero_guid = { { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } } };
  48 const GUID *const zero_guid = &__zero_guid;
  49 
  50 /**
  51  * ntfs_guid_is_zero - check if a GUID is zero
  52  * @guid:       [IN] guid to check
  53  *
  54  * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
  55  * and FALSE otherwise.
  56  */
  57 BOOL ntfs_guid_is_zero(const GUID *guid)
  58 {
  59         return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
  60 }
  61 
  62 /**
  63  * ntfs_guid_to_mbs - convert a GUID to a multi byte string
  64  * @guid:       [IN]  guid to convert
  65  * @guid_str:   [OUT] string in which to return the GUID (optional)
  66  *
  67  * Convert the GUID pointed to by @guid to a multi byte string of the form
  68  * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".  Therefore, @guid_str (if not NULL)
  69  * needs to be able to store at least 37 bytes.
  70  *
  71  * If @guid_str is not NULL it will contain the converted GUID on return.  If
  72  * it is NULL a string will be allocated and this will be returned.  The caller
  73  * is responsible for free()ing the string in that case.
  74  *
  75  * On success return the converted string and on failure return NULL with errno
  76  * set to the error code.
  77  */
  78 char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
  79 {
  80         char *_guid_str;
  81         int res;
  82 
  83         if (!guid) {
  84                 errno = EINVAL;
  85                 return NULL;
  86         }
  87         _guid_str = guid_str;
  88         if (!_guid_str) {
  89                 _guid_str = ntfs_malloc(37);
  90                 if (!_guid_str)
  91                         return _guid_str;
  92         }
  93         res = snprintf(_guid_str, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
  94                         "%02x%02x-%02x%02x%02x%02x%02x%02x", guid->raw[0],
  95                         guid->raw[1], guid->raw[2], guid->raw[3], guid->raw[4],
  96                         guid->raw[5], guid->raw[6], guid->raw[7], guid->raw[8],
  97                         guid->raw[9], guid->raw[10], guid->raw[11],
  98                         guid->raw[12], guid->raw[13], guid->raw[14],
  99                         guid->raw[15]);
 100         if (res == 36)
 101                 return _guid_str;
 102         if (!guid_str)
 103                 free(_guid_str);
 104         errno = EINVAL;
 105         return NULL;
 106 }
 107 
 108 /**
 109  * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
 110  * @sid:        [IN]  SID for which to determine the maximum string size
 111  *
 112  * Determine the maximum multi byte string size in bytes which is needed to
 113  * store the standard textual representation of the SID pointed to by @sid.
 114  * See ntfs_sid_to_mbs(), below.
 115  *
 116  * On success return the maximum number of bytes needed to store the multi byte
 117  * string and on failure return -1 with errno set to the error code.
 118  */
 119 int ntfs_sid_to_mbs_size(const SID *sid)
 120 {
 121         int size, i;
 122 
 123         if (!ntfs_sid_is_valid(sid)) {
 124                 errno = EINVAL;
 125                 return -1;
 126         }
 127         /* Start with "S-". */
 128         size = 2;
 129         /*
 130          * Add the SID_REVISION.  Hopefully the compiler will optimize this
 131          * away as SID_REVISION is a constant.
 132          */
 133         for (i = SID_REVISION; i > 0; i /= 10)
 134                 size++;
 135         /* Add the "-". */
 136         size++;
 137         /*
 138          * Add the identifier authority.  If it needs to be in decimal, the
 139          * maximum is 2^32-1 = 4294967295 = 10 characters.  If it needs to be
 140          * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
 141          */
 142         if (!sid->identifier_authority.s.high_part)
 143                 size += 10;
 144         else
 145                 size += 14;
 146         /*
 147          * Finally, add the sub authorities.  For each we have a "-" followed
 148          * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
 149          */
 150         size += (1 + 10) * sid->sub_authority_count;
 151         /* We need the zero byte at the end, too. */
 152         size++;
 153         return size * sizeof(char);
 154 }
 155 
 156 /**
 157  * ntfs_sid_to_mbs - convert a SID to a multi byte string
 158  * @sid:                [IN]  SID to convert
 159  * @sid_str:            [OUT] string in which to return the SID (optional)
 160  * @sid_str_size:       [IN]  size in bytes of @sid_str
 161  *
 162  * Convert the SID pointed to by @sid to its standard textual representation.
 163  * @sid_str (if not NULL) needs to be able to store at least
 164  * ntfs_sid_to_mbs_size() bytes.  @sid_str_size is the size in bytes of
 165  * @sid_str if @sid_str is not NULL.
 166  *
 167  * The standard textual representation of the SID is of the form:
 168  *      S-R-I-S-S...
 169  * Where:
 170  *    - The first "S" is the literal character 'S' identifying the following
 171  *      digits as a SID.
 172  *    - R is the revision level of the SID expressed as a sequence of digits
 173  *      in decimal.
 174  *    - I is the 48-bit identifier_authority, expressed as digits in decimal,
 175  *      if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
 176  *    - S... is one or more sub_authority values, expressed as digits in
 177  *      decimal.
 178  *
 179  * If @sid_str is not NULL it will contain the converted SUID on return.  If it
 180  * is NULL a string will be allocated and this will be returned.  The caller is
 181  * responsible for free()ing the string in that case.
 182  *
 183  * On success return the converted string and on failure return NULL with errno
 184  * set to the error code.
 185  */
 186 char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
 187 {
 188         u64 u;
 189         char *s;
 190         int i, j, cnt;
 191 
 192         /*
 193          * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
 194          * check @sid, too.  8 is the minimum SID string size.
 195          */
 196         if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
 197                 errno = EINVAL;
 198                 return NULL;
 199         }
 200         /* Allocate string if not provided. */
 201         if (!sid_str) {
 202                 cnt = ntfs_sid_to_mbs_size(sid);
 203                 if (cnt < 0)
 204                         return NULL;
 205                 s = ntfs_malloc(cnt);
 206                 if (!s)
 207                         return s;
 208                 sid_str = s;
 209                 /* So we know we allocated it. */
 210                 sid_str_size = 0;
 211         } else {
 212                 s = sid_str;
 213                 cnt = sid_str_size;
 214         }
 215         /* Start with "S-R-". */
 216         i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
 217         if (i < 0 || i >= cnt)
 218                 goto err_out;
 219         s += i;
 220         cnt -= i;
 221         /* Add the identifier authority. */
 222         for (u = i = 0, j = 40; i < 6; i++, j -= 8)
 223                 u += (u64)sid->identifier_authority.value[i] << j;
 224         if (!sid->identifier_authority.s.high_part)
 225                 i = snprintf(s, cnt, "%lu", (unsigned long)u);
 226         else
 227                 i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
 228         if (i < 0 || i >= cnt)
 229                 goto err_out;
 230         s += i;
 231         cnt -= i;
 232         /* Finally, add the sub authorities. */
 233         for (j = 0; j < sid->sub_authority_count; j++) {
 234                 i = snprintf(s, cnt, "-%u", (unsigned int)
 235                                 le32_to_cpu(sid->sub_authority[j]));
 236                 if (i < 0 || i >= cnt)
 237                         goto err_out;
 238                 s += i;
 239                 cnt -= i;
 240         }
 241         return sid_str;
 242 err_out:
 243         if (i >= cnt)
 244                 i = EMSGSIZE;
 245         else
 246                 i = errno;
 247         if (!sid_str_size)
 248                 free(sid_str);
 249         errno = i;
 250         return NULL;
 251 }
 252 
 253 /**
 254  * ntfs_generate_guid - generatates a random current guid.
 255  * @guid:       [OUT]   pointer to a GUID struct to hold the generated guid.
 256  *
 257  * perhaps not a very good random number generator though...
 258  */
 259 void ntfs_generate_guid(GUID *guid)
 260 {
 261         unsigned int i;
 262         u8 *p = (u8 *)guid;
 263 
 264         for (i = 0; i < sizeof(GUID); i++) {
 265                 p[i] = (u8)(random() & 0xFF);
 266                 if (i == 7)
 267                         p[7] = (p[7] & 0x0F) | 0x40;
 268                 if (i == 8)
 269                         p[8] = (p[8] & 0x3F) | 0x80;
 270         }
 271 }
 272