source: trunk/lib/winsec.c @ 141

Last change on this file since 141 was 134, checked in by tim, 16 years ago

rewrote winsec library, stripping out Samba dependencies

eliminated remaining Samba prs functions

added support for 'li' subkey list records

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