source: trunk/lib/winsec.c @ 286

Last change on this file since 286 was 286, checked in by tim, 9 years ago

Fixed a NULL pointer dereference and one dangling pointer, triggered by corrupt security descriptors.
Thanks AFL!

  • Property svn:keywords set to Id
File size: 14.6 KB
RevLine 
[169]1/*
[261]2 * Copyright (C) 2005-2006,2009-2011 Timothy D. Morgan
[132]3 * Copyright (C) 1992-2005 Samba development team
4 *               (see individual files under Subversion for details.)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
[133]19 * $Id: winsec.c 286 2015-08-23 04:09:08Z tim $
[132]20 */
21
[169]22/** @file */
23
[147]24#include "winsec.h"
[132]25
26
[147]27/******************************************************************************
28 * Non-talloc() interface for parsing a descriptor.
29 ******************************************************************************/
30WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len)
31{
32  return winsec_parse_desc(NULL, buf, buf_len);
33}
[132]34
[147]35
[134]36/******************************************************************************
[147]37 * Free a descriptor.  Not needed if using talloc and a parent context is freed.
38 ******************************************************************************/
39void winsec_free_descriptor(WINSEC_DESC* desc)
40{
41  talloc_free(desc);
42}
43
44
45/******************************************************************************
[134]46 * Parses a WINSEC_DESC structure and substructures.
47 ******************************************************************************/
[147]48WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, 
49                               const uint8_t* buf, uint32_t buf_len)
[132]50{
[134]51  WINSEC_DESC* ret_val;
[132]52
[134]53  if (buf == NULL || buf_len <  WINSEC_DESC_HEADER_SIZE)
54    return NULL;
[148]55
[147]56  if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL)
57    return NULL;
[133]58
[134]59  ret_val->revision = buf[0];
60  ret_val->sbz1 = buf[1];
61  ret_val->control = SVAL(buf, 0x2);
[133]62
[257]63  /* XXX: should probably reject any non-self relative */
[134]64  if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
65    fprintf(stderr, "DEBUG: NOT self-relative!\n");
[132]66
[134]67  ret_val->off_owner_sid = IVAL(buf, 0x4);
68  ret_val->off_grp_sid = IVAL(buf, 0x8);
69  ret_val->off_sacl = IVAL(buf, 0xC);
70  ret_val->off_dacl = IVAL(buf, 0x10);
[133]71
[134]72  /* A basic sanity check to ensure our offsets are within our buffer.
73   * Additional length checking is done in secondary parsing functions.
74   */
75  if((ret_val->off_owner_sid >= buf_len)
76     || (ret_val->off_grp_sid >= buf_len)
77     || (ret_val->off_sacl >= buf_len)
78     || (ret_val->off_dacl >= buf_len))
[133]79  {
[147]80    talloc_free(ret_val);
[134]81    return NULL;
[133]82  }
[132]83
[148]84  if(ret_val->off_owner_sid == 0)
85    ret_val->owner_sid = NULL;
86  else
[133]87  {
[147]88    ret_val->owner_sid = winsec_parse_dom_sid(ret_val, 
89                                              buf + ret_val->off_owner_sid,
[134]90                                              buf_len - ret_val->off_owner_sid);
91    if(ret_val->owner_sid == NULL)
[133]92    {
[147]93      talloc_free(ret_val);
[134]94      return NULL;
[133]95    }
96  }
[132]97
[148]98  if(ret_val->off_grp_sid == 0) 
99    ret_val->grp_sid = NULL;
100  else
[133]101  {
[147]102    ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid,
[134]103                                            buf_len - ret_val->off_grp_sid);
104    if(ret_val->grp_sid == NULL)
[133]105    {
[147]106      talloc_free(ret_val);
[134]107      return NULL;
[133]108    }
109  }
[132]110
[148]111  if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl)
[133]112  {
[147]113    ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl,
[134]114                                     buf_len - ret_val->off_sacl);
115    if(ret_val->sacl == NULL)
[133]116    {
[147]117      talloc_free(ret_val);
[134]118      return NULL;
[133]119    }
120  }
[148]121  else
122    ret_val->sacl = NULL;
[132]123
[148]124  if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0) 
[133]125  {
[147]126    ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl,
[134]127                                     buf_len - ret_val->off_dacl);
128    if(ret_val->dacl == NULL)
[133]129    {
[147]130      talloc_free(ret_val);
[134]131      return NULL;
[133]132    }
133  }
[148]134  else
135    ret_val->dacl = NULL;
[132]136
[134]137  return ret_val;
[132]138}
139
140
[134]141/******************************************************************************
142 * Parses a WINSEC_ACL structure and all substructures.
143 ******************************************************************************/
[147]144WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
145                             const uint8_t* buf, uint32_t buf_len)
[132]146{
[134]147  uint32_t i, offset;
148  WINSEC_ACL* ret_val;
[132]149
[134]150  /*
151   * Note that the size is always a multiple of 4 bytes due to the
152   * nature of the data structure.
153   */
154  if (buf == NULL || buf_len < 8)
155    return NULL;
[148]156
[147]157  if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL)
158    return NULL;
[134]159 
160  ret_val->revision = SVAL(buf, 0x0);
161  ret_val->size     = SVAL(buf, 0x2);
162  ret_val->num_aces = IVAL(buf, 0x4);
[132]163
[134]164  /* The num_aces can be at most around 4k because anything greater
165   * wouldn't fit in the 16 bit size even if every ace was as small as
166   * possible.
167   */
168  if((ret_val->size > buf_len) || (ret_val->num_aces > 4095))
169  {
[147]170    talloc_free(ret_val);
[134]171    return NULL;
172  }
[132]173
[134]174  /* Even if the num_aces is zero, allocate memory as there's a difference
175   * between a non-present DACL (allow all access) and a DACL with no ACE's
176   * (allow no access).
177   */
[147]178  if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*, 
179                                   ret_val->num_aces+1)) == NULL)
[133]180  {
[147]181    talloc_free(ret_val);
[134]182    return NULL;
[133]183  }
[132]184
[134]185  offset = 8;
186  for(i=0; i < ret_val->num_aces; i++)
187  {
[147]188    ret_val->aces[i] = winsec_parse_ace(ret_val->aces, 
189                                        buf+offset, buf_len-offset);
[134]190    if(ret_val->aces[i] == NULL)
191    {
[147]192      talloc_free(ret_val);
[134]193      return NULL;
194    }
[132]195
[134]196    offset += ret_val->aces[i]->size;
197    if(offset > buf_len)
198    {
[147]199      talloc_free(ret_val);
[134]200      return NULL;
201    }
202  }
[148]203  ret_val->aces[ret_val->num_aces] = NULL;
[132]204
[134]205  return ret_val;
[132]206}
207
208
[134]209/******************************************************************************
210 * Parses a WINSEC_ACE structure and all substructures.
211 ******************************************************************************/
[147]212WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
213                             const uint8_t* buf, uint32_t buf_len)
[132]214{
[134]215  uint32_t offset;
216  WINSEC_ACE* ret_val;
[132]217
[134]218  if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE)
219    return NULL;
[132]220
[147]221  if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL)
[134]222    return NULL;
[132]223
[134]224  ret_val->type = buf[0];
225  ret_val->flags = buf[1];
226  ret_val->size = SVAL(buf, 0x2);
227  ret_val->access_mask = IVAL(buf, 0x4);
[253]228  ret_val->obj_guid = NULL;
229  ret_val->inh_guid = NULL;
230 
[134]231  offset = 0x8;
[132]232
233  /* check whether object access is present */
[134]234  if (winsec_ace_object(ret_val->type))
[132]235  {
[134]236    ret_val->obj_flags = IVAL(buf, offset);
237    offset += 4;
[132]238
[134]239    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT)
240    {
[147]241      ret_val->obj_guid = winsec_parse_uuid(ret_val, 
242                                            buf+offset, buf_len-offset);
[134]243      if(ret_val->obj_guid == NULL)
244      {
[147]245        talloc_free(ret_val);
[134]246        return NULL;
247      }
248      offset += sizeof(WINSEC_UUID);
249    }
[132]250
[134]251    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
252    {
[147]253      ret_val->inh_guid = winsec_parse_uuid(ret_val, 
254                                            buf+offset, buf_len-offset);
[134]255      if(ret_val->inh_guid == NULL)
256      {
[147]257        talloc_free(ret_val);
[134]258        return NULL;
259      }
260      offset += sizeof(WINSEC_UUID);
261    }
262  }
[132]263
[147]264  ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
[134]265  if(ret_val->trustee == NULL)
266  {
[147]267    talloc_free(ret_val);
268    return NULL;
[132]269  }
[134]270 
271  return ret_val;
[132]272}
273
274
[134]275/******************************************************************************
276 * Parses a WINSEC_DOM_SID structure.
277 ******************************************************************************/
[147]278WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
279                                     const uint8_t* buf, uint32_t buf_len)
[132]280{
[134]281  uint32_t i;
282  WINSEC_DOM_SID* ret_val;
[132]283
[134]284  if(buf == NULL || buf_len < 8)
285    return NULL;
[132]286
[147]287  /*  if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/
288  if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL)
[134]289    return NULL;
[132]290
[134]291  ret_val->sid_rev_num = buf[0];
292  ret_val->num_auths = buf[1];
293  memcpy(ret_val->id_auth, buf+2, 6);
[132]294
[134]295  /* XXX: should really issue a warning here... */
296  if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS)
297    ret_val->num_auths = WINSEC_MAX_SUBAUTHS;
298
299  if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8)
[132]300  {
[147]301    talloc_free(ret_val);
[134]302    return NULL;
[132]303  }
[134]304 
305  for(i=0; i < ret_val->num_auths; i++)
306    ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t));
[132]307
[134]308  return ret_val;
309}
[132]310
311
[134]312/******************************************************************************
313 * Parses a WINSEC_UUID struct.
314 ******************************************************************************/
[147]315WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
316                               const uint8_t* buf, uint32_t buf_len)
[134]317{
318  WINSEC_UUID* ret_val;
319
320  if(buf == NULL || buf_len < sizeof(WINSEC_UUID))
[133]321    return false;
[132]322
[147]323  if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL)
[134]324    return NULL;
325 
326  ret_val->time_low = IVAL(buf, 0x0);
327  ret_val->time_mid = SVAL(buf, 0x4);
328  ret_val->time_hi_and_version = SVAL(buf, 0x6);
329 
330  memcpy(ret_val->clock_seq, buf+0x8, 2);
331  memcpy(ret_val->node, buf+0xB, 6);
[132]332
[134]333  return ret_val;
[132]334}
335
336
[134]337/******************************************************************************
338 * Calculates the size of a SID.
339 ******************************************************************************/
340size_t winsec_sid_size(const WINSEC_DOM_SID* sid)
[132]341{
[133]342  if (sid == NULL)
343    return 0;
[132]344
[134]345  return sid->num_auths * sizeof(uint32_t) + 8;
[133]346}
[132]347
348
[134]349/******************************************************************************
350 * Compare the auth portion of two SIDs.
351 ******************************************************************************/
352int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
[133]353{
354  int i;
[132]355
[133]356  if (sid1 == sid2)
357    return 0;
358  if (!sid1)
359    return -1;
360  if (!sid2)
361    return 1;
[132]362
[133]363  if (sid1->sid_rev_num != sid2->sid_rev_num)
364    return sid1->sid_rev_num - sid2->sid_rev_num;
[132]365
[133]366  for (i = 0; i < 6; i++)
367    if (sid1->id_auth[i] != sid2->id_auth[i])
368      return sid1->id_auth[i] - sid2->id_auth[i];
[132]369
[133]370  return 0;
371}
[132]372
373
[134]374/******************************************************************************
375 * Compare two SIDs.
376 ******************************************************************************/
377int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
[133]378{
379  int i;
[132]380
[133]381  if (sid1 == sid2)
382    return 0;
383  if (!sid1)
384    return -1;
385  if (!sid2)
386    return 1;
[132]387
[133]388  /* Compare most likely different rids, first: i.e start at end */
389  if (sid1->num_auths != sid2->num_auths)
390    return sid1->num_auths - sid2->num_auths;
[132]391
[133]392  for (i = sid1->num_auths-1; i >= 0; --i)
393    if (sid1->sub_auths[i] != sid2->sub_auths[i])
394      return sid1->sub_auths[i] - sid2->sub_auths[i];
[132]395
[134]396  return winsec_sid_compare_auth(sid1, sid2);
[133]397}
[132]398
399
[134]400/******************************************************************************
401 * Compare two SIDs.
402 ******************************************************************************/
403bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
[133]404{
[134]405  return winsec_sid_compare(sid1, sid2) == 0;
[133]406}
[132]407
408
[134]409/******************************************************************************
[253]410 ******************************************************************************/
411char* winsec_sid2str(const WINSEC_DOM_SID* sid)
412{
413  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
414  uint32_t left = size;
[286]415  uint8_t comps;
416  char* ret_val;
417
418  if(sid == NULL)
419    return NULL;
420  comps = sid->num_auths;
421
422  ret_val = malloc(size);
[253]423  if(ret_val == NULL)
424    return NULL;
425
426  if(comps > WINSEC_MAX_SUBAUTHS)
427    comps = WINSEC_MAX_SUBAUTHS;
428
429  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
430
431  for (i = 0; i < comps; i++) 
432    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
433
434  return ret_val;
435}
436
437
438/******************************************************************************
[134]439 * Compares two WINSEC_DESC structures.
440 ******************************************************************************/
441bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
[133]442{
[134]443  /* Trivial cases */
444  if (!s1 && !s2)
[133]445    return true;
[134]446  if (!s1 || !s2)
447    return false;
[132]448
[134]449  /* Check top level stuff */
450  if (s1->revision != s2->revision)
451    return false;
[132]452
[134]453  if (s1->control != s2->control)
[132]454    return false;
455
[134]456  /* Check owner and group */
457  if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid))
458    return false;
459
460  if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid)) 
461    return false;
462
463  /* Check ACLs present in one but not the other */
464  if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
465      (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
[132]466  { return false; }
467
[134]468  /* Sigh - we have to do it the hard way by iterating over all
469     the ACEs in the ACLs */
470  if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl)) 
[132]471    return false;
472
473  return true;
474}
475
476
[134]477
478/******************************************************************************
479 * Compares two WINSEC_ACL structures.
480 ******************************************************************************/
481bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2)
[132]482{
483  unsigned int i, j;
484
485  /* Trivial cases */
486  if (!s1 && !s2) 
487    return true;
488  if (!s1 || !s2) 
489    return false;
490
491  /* Check top level stuff */
492  if (s1->revision != s2->revision)
493    return false;
494
495  if (s1->num_aces != s2->num_aces)
496    return false;
497
498  /* The ACEs could be in any order so check each ACE in s1 against
499     each ACE in s2. */
500
[134]501  for (i = 0; i < s1->num_aces; i++)
[132]502  {
503    bool found = false;
504
505    for (j = 0; j < s2->num_aces; j++) 
506    {
[134]507      if (winsec_ace_equal(s1->aces[i], s2->aces[j])) 
[132]508      {
509        found = true;
510        break;
511      }
512    }
513
514    if (!found)
515      return false;
516  }
517
518  return true;
519}
520
521
[134]522/******************************************************************************
523 * Compares two WINSEC_ACE structures.
524 ******************************************************************************/
525bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2)
[132]526{
527  /* Trivial cases */
[134]528  if (!s1 && !s2) 
[132]529    return true;
[134]530  if (!s1 || !s2) 
[132]531    return false;
532
533  /* Check top level stuff */
[134]534  if (s1->type != s2->type || s1->flags != s2->flags ||
535      s1->access_mask != s2->access_mask)
536  { return false; }
[132]537
[134]538  /* Check SID */
539  if (!winsec_sid_equal(s1->trustee, s2->trustee))
[132]540    return false;
541
[134]542  return true;
543}
[132]544
545
[134]546/******************************************************************************
547 * Check if ACE has OBJECT type.
548 ******************************************************************************/
549bool winsec_ace_object(uint8_t type)
550{
551  if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
552      type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
553      type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
554      type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) 
555  { return true; }
[132]556
[134]557  return false;
[132]558}
Note: See TracBrowser for help on using the repository browser.