Changeset 253


Ignore:
Timestamp:
06/12/11 22:27:42 (13 years ago)
Author:
tim
Message:

added preliminary interface to security descriptors in pyregfi
misc bug fixes

Files:
11 edited

Legend:

Unmodified
Added
Removed
  • test/pyregfi-smoketest.py

    r252 r253  
    222222    for t in threads:
    223223        t.join()
     224
     225
     226def loopSecurity(hive, fh):
     227    start = hive.root.fetch_security()
     228    print(start.descriptor.group)
     229    cur = start.next_security()
     230
     231    while cur != start:
     232        print(start.descriptor.group)
     233        cur = cur.next_security()
     234
    224235   
     236def iterSecurity(hive, fh):
     237    stat = 0
     238    for k in hive:
     239        security = k.fetch_security()
     240        stat += security.ref_count
     241        stat += len(security.descriptor.owner)
     242        stat += len(security.descriptor.group)
     243        if security.descriptor.sacl:
     244            for ace in security.descriptor.sacl:
     245                stat += ace.flags
     246                if ace.object:
     247                    stat += ace.object.int
     248                if ace.inherited_object:
     249                    stat += ace.inherited_object.int
     250
     251        if security.descriptor.dacl:
     252            for ace in security.descriptor.dacl:
     253                stat += ace.flags
     254                if ace.object:
     255                    stat += ace.object.int
     256                if ace.inherited_object:
     257                    stat += ace.inherited_object.int
     258    print("  Security stat: %d" % stat)
    225259
    226260tests = {
     
    233267    "iterCallbackIO":iterCallbackIO,
    234268    "iterMultithread":iterMultithread,
     269    "loopSecurity":loopSecurity,
     270    "iterSecurity":iterSecurity,
    235271    }
    236272
  • trunk/SConstruct

    r246 r253  
    6161if sys.version_info[0] == 2:
    6262   install_items.append('pyregfi2-install.log')
    63    env.Command('pyregfi2-install.log', ['python/pyregfi/__init__.py', 'python/pyregfi/structures.py'],
     63   env.Command('pyregfi2-install.log', ['python/pyregfi/__init__.py',
     64                                        'python/pyregfi/structures.py',
     65                                        'python/pyregfi/winsec.py'],
    6466               "python pyregfi-distutils.py install | tee pyregfi2-install.log")
    6567
     
    6769if python_path != '':
    6870   install_items.append('pyregfi3-install.log')
    69    env.Command('pyregfi3-install.log', ['python/pyregfi/__init__.py', 'python/pyregfi/structures.py'],
     71   env.Command('pyregfi3-install.log', ['python/pyregfi/__init__.py',
     72                                        'python/pyregfi/structures.py',
     73                                        'python/pyregfi/winsec.py'],
    7074               "python3 pyregfi-distutils.py install | tee pyregfi3-install.log")
    7175
  • trunk/include/lru_cache.h

    r201 r253  
    3737#include <talloc.h>
    3838
    39 /* GCC-specific macro for library exports */
    40 #ifdef _EXPORT
    41 #undef _EXPORT
    42 #endif
    43 #define _EXPORT __attribute__((visibility("default")))
     39#include "compat.h"
     40
    4441
    4542struct lru_cache_element;
     
    7471 * XXX: finish documenting.
    7572 */
    76 _EXPORT
     73_EXPORT()
    7774lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
    7875
     
    8178 * XXX: finish documenting.
    8279 */
    83 _EXPORT
     80_EXPORT()
    8481lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
    8582                                uint32_t secret, bool talloc_data);
     
    8986 * XXX: finish documenting.
    9087 */
    91 _EXPORT
     88_EXPORT()
    9289void lru_cache_destroy(lru_cache* ht);
    9390
     
    9693 * XXX: finish documenting.
    9794 */
    98 _EXPORT
     95_EXPORT()
    9996bool lru_cache_update(lru_cache* ht, const void* index,
    10097                      uint32_t index_len, void* data);
     
    106103 *         If no data was found at index, NULL is returned.
    107104 */
    108 _EXPORT
     105_EXPORT()
    109106void* lru_cache_find(lru_cache* ht, const void* index,
    110107                     uint32_t index_len);
     
    118115 *         at index.
    119116 */
    120 _EXPORT
     117_EXPORT()
    121118bool lru_cache_remove(lru_cache* ht, const void* index,
    122119                      uint32_t index_len);
  • trunk/include/range_list.h

    r201 r253  
    3838#include <talloc.h>
    3939
    40 /* GCC-specific macro for library exports */
    41 #ifdef _EXPORT
    42 #undef _EXPORT
    43 #endif
    44 #define _EXPORT __attribute__((visibility("default")))
     40#include "compat.h"
    4541
    4642typedef struct _range_list_element
     
    6561 * @return A newly allocated range_list, or NULL if an error occurred.
    6662 */
    67 _EXPORT
     63_EXPORT()
    6864range_list* range_list_new();
    6965
     
    7672 * @param rl the range_list to be free()d.
    7773 */
    78 _EXPORT
     74_EXPORT()
    7975void range_list_free(range_list* rl);
    8076
     
    8682 * @return The number of elements currently in the list.
    8783 */
    88 _EXPORT
     84_EXPORT()
    8985uint32_t range_list_size(const range_list* rl);
    9086
     
    106102 * errors may also be possible.
    107103 */
    108 _EXPORT
     104_EXPORT()
    109105bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
    110106
     
    119115 * @return true if the element was successfully removed, false otherwise.
    120116 */
    121 _EXPORT
     117_EXPORT()
    122118bool range_list_remove(range_list* rl, uint32_t index);
    123119
     
    131127 *         available.
    132128 */
    133 _EXPORT
     129_EXPORT()
    134130const range_list_element* range_list_get(const range_list* rl, uint32_t index);
    135131
     
    142138 * @return A matching element index or a negative value if none could be found.
    143139 */
    144 _EXPORT
     140_EXPORT()
    145141int32_t range_list_find(const range_list* rl, uint32_t offset);
    146142
     
    157153 *        element was never set.
    158154 */
    159 _EXPORT
     155_EXPORT()
    160156void* range_list_find_data(const range_list* rl, uint32_t offset);
    161157
     
    179175 * @return true if the element was successfully split, false otherwise.
    180176 */
    181 _EXPORT
     177_EXPORT()
    182178bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
    183179
     
    192188 * @return true if the specified range exists and is complete, false otherwise.
    193189 */
    194 _EXPORT
     190_EXPORT()
    195191bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
    196192
  • trunk/include/regfi.h

    r252 r253  
    7474
    7575/* regfi headers */
    76 #include <byteorder.h>
    77 #include <winsec.h>
    78 #include <void_stack.h>
    79 #include <range_list.h>
    80 #include <lru_cache.h>
    81 
    82 /* GCC-specific macro for library exports */
    83 #ifdef _EXPORT
    84 #undef _EXPORT
    85 #endif
    86 #ifdef REGFI_WIN32
    87 #define _EXPORT() __declspec(dllexport)
    88 #else
    89 #define _EXPORT() __attribute__((visibility("default")))
    90 #endif
    91 
    92 #ifndef EOVERFLOW
    93 # define EOVERFLOW E2BIG
    94 #endif
     76#include "compat.h"
     77#include "byteorder.h"
     78#include "winsec.h"
     79#include "void_stack.h"
     80#include "range_list.h"
     81#include "lru_cache.h"
    9582
    9683/******************************************************************************/
     
    10941081
    10951082
     1083/** Returns the next SK (security) record referenced by the supplied SK record
     1084 *
     1085 * @param file the file from which sk is derived
     1086 * @param sk   the SK record whose next sibling SK record is desired
     1087 *
     1088 * @return A read-only SK structure, or NULL on failure.
     1089 *
     1090 * @note
     1091 * SK records are included in a circular, doubly-linked list.
     1092 * To iterate over all SK records, be sure to check for the repetition of
     1093 * the SK record you started with to determine when all have been traversed.
     1094 *
     1095 * @ingroup regfiBase
     1096 */
     1097_EXPORT()
     1098const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk);
     1099
     1100
     1101/** Returns the previous SK (security) record referenced by the supplied SK record
     1102 *
     1103 * @param file the file from which sk is derived
     1104 * @param sk   the SK record whose previous sibling SK record is desired
     1105 *
     1106 * @return A read-only SK structure, or NULL on failure.
     1107 *
     1108 * @note
     1109 * SK records are included in a circular, doubly-linked list.
     1110 * To iterate over all SK records, be sure to check for the repetition of
     1111 * the SK record you started with to determine when all have been traversed.
     1112 *
     1113 * @ingroup regfiBase
     1114 */
     1115_EXPORT()
     1116const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk);
     1117
     1118
    10961119/** Retrieves data for a given value.
    10971120 *
     
    15421565_EXPORT()
    15431566const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
    1544                                   bool strict);
     1567                              bool strict);
    15451568
    15461569
  • trunk/include/void_stack.h

    r201 r253  
    3434#include <talloc.h>
    3535
    36 /* GCC-specific macro for library exports */
    37 #ifdef _EXPORT
    38 #undef _EXPORT
    39 #endif
    40 #define _EXPORT __attribute__((visibility("default")))
     36#include "compat.h"
    4137
    4238/** XXX: document this. */
     
    6561 *         or NULL if an error occurred.
    6662 */
    67 _EXPORT
     63_EXPORT()
    6864void_stack* void_stack_new(unsigned short max_size);
    6965
     
    7571 * @return a pointer to the duplicate void_stack, or NULL if an error occurred.
    7672 */
    77 _EXPORT
     73_EXPORT()
    7874void_stack* void_stack_copy(const void_stack* v);
    7975
     
    8682 *         (which will be in reverse order), or NULL if an error occurred.
    8783 */
    88 _EXPORT
     84_EXPORT()
    8985void_stack* void_stack_copy_reverse(const void_stack* v);
    9086
     
    9591 * @param stack the stack to be free()d.
    9692 */
    97 _EXPORT
     93_EXPORT()
    9894void void_stack_free(void_stack* stack);
    9995
     
    108104 * @param stack the stack to be free()d.
    109105 */
    110 _EXPORT
     106_EXPORT()
    111107void void_stack_free_deep(void_stack* stack);
    112108
     
    118114 * @return the number of elements currently on the stack.
    119115 */
    120 _EXPORT
     116_EXPORT()
    121117unsigned short void_stack_size(const void_stack* stack);
    122118
     
    129125 *         on the stack.
    130126 */
    131 _EXPORT
     127_EXPORT()
    132128void* void_stack_pop(void_stack* stack);
    133129
     
    140136 * @return true if the element was successfully added, false otherwise.
    141137 */
    142 _EXPORT
     138_EXPORT()
    143139bool void_stack_push(void_stack* stack, void* e);
    144140
     
    151147 *         no elements exist in the stack.
    152148 */
    153 _EXPORT
     149_EXPORT()
    154150const void* void_stack_cur(const void_stack* stack);
    155151
     
    161157 * @return a new void_stack_iterator, or NULL if an error occurred.
    162158 */
    163 _EXPORT
     159_EXPORT()
    164160void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
    165161
     
    171167 * @param iter the void_stack_iterator to be free()d.
    172168 */
    173 _EXPORT
     169_EXPORT()
    174170void void_stack_iterator_free(void_stack_iterator* iter);
    175171
     
    183179 * @return a pointer to the next element.
    184180 */
    185 _EXPORT
     181_EXPORT()
    186182const void* void_stack_iterator_next(void_stack_iterator* iter);
    187183
  • trunk/include/winsec.h

    r201 r253  
    4646#include <talloc.h>
    4747
     48#include "compat.h"
    4849#include "byteorder.h"
    49 
    50 /* GCC-specific macro for library exports */
    51 #ifdef _EXPORT
    52 #undef _EXPORT
    53 #endif
    54 #define _EXPORT __attribute__((visibility("default")))
    5550
    5651
     
    223218 * XXX: finish documenting
    224219 */
    225 _EXPORT
     220_EXPORT()
    226221WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
    227222
     
    231226 * XXX: finish documenting
    232227 */
    233 _EXPORT
     228_EXPORT()
    234229void winsec_free_descriptor(WINSEC_DESC* desc);
    235230
     
    238233 * XXX: finish documenting
    239234 */
    240 _EXPORT
     235_EXPORT()
    241236WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
    242237                               const uint8_t* buf, uint32_t buf_len);
     
    246241 * XXX: finish documenting
    247242 */
    248 _EXPORT
     243_EXPORT()
    249244WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
    250245                             const uint8_t* buf, uint32_t buf_len);
     
    254249 * XXX: finish documenting
    255250 */
    256 _EXPORT
     251_EXPORT()
    257252WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
    258253                             const uint8_t* buf, uint32_t buf_len);
     
    262257 * XXX: finish documenting
    263258 */
    264 _EXPORT
     259_EXPORT()
    265260WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
    266261                                     const uint8_t* buf, uint32_t buf_len);
     
    270265 * XXX: finish documenting
    271266 */
    272 _EXPORT
     267_EXPORT()
    273268WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
    274269                               const uint8_t* buf, uint32_t buf_len);
     
    279274 * XXX: finish documenting
    280275 */
    281 _EXPORT
     276_EXPORT()
    282277size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
    283278
     
    286281 * XXX: finish documenting
    287282 */
    288 _EXPORT
     283_EXPORT()
    289284int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
    290285
     
    293288 * XXX: finish documenting
    294289 */
    295 _EXPORT
     290_EXPORT()
    296291int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
    297292
     
    300295 * XXX: finish documenting
    301296 */
    302 _EXPORT
     297_EXPORT()
    303298bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
    304299
     
    307302 * XXX: finish documenting
    308303 */
    309 _EXPORT
     304_EXPORT()
     305char* winsec_sid2str(const WINSEC_DOM_SID* sid);
     306
     307/**
     308 *
     309 * XXX: finish documenting
     310 */
     311_EXPORT()
    310312bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
    311313
     
    314316 * XXX: finish documenting
    315317 */
    316 _EXPORT
     318_EXPORT()
    317319bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
    318320
     
    321323 * XXX: finish documenting
    322324 */
    323 _EXPORT
     325_EXPORT()
    324326bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
    325327
     
    328330 * XXX: finish documenting
    329331 */
    330 _EXPORT
     332_EXPORT()
    331333bool winsec_ace_object(uint8_t type);
    332334
  • trunk/lib/regfi.c

    r252 r253  
    403403
    404404
    405 char* regfi_sid2str(WINSEC_DOM_SID* sid)
    406 {
    407   uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
    408   uint32_t left = size;
    409   uint8_t comps = sid->num_auths;
    410   char* ret_val = malloc(size);
    411  
    412   if(ret_val == NULL)
    413     return NULL;
    414 
    415   if(comps > WINSEC_MAX_SUBAUTHS)
    416     comps = WINSEC_MAX_SUBAUTHS;
    417 
    418   left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
    419 
    420   for (i = 0; i < comps; i++)
    421     left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
    422 
    423   return ret_val;
    424 }
    425 
    426 
    427405char* regfi_get_acl(WINSEC_ACL* acl)
    428406{
     
    440418  for (i = 0; i < acl->num_aces && !failed; i++)
    441419  {
    442     sid_str = regfi_sid2str(acl->aces[i]->trustee);
     420    sid_str = winsec_sid2str(acl->aces[i]->trustee);
    443421    type_str = regfi_ace_type2str(acl->aces[i]->type);
    444422    perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
     
    505483char* regfi_get_owner(WINSEC_DESC *sec_desc)
    506484{
    507   return regfi_sid2str(sec_desc->owner_sid);
     485  return winsec_sid2str(sec_desc->owner_sid);
    508486}
    509487
     
    511489char* regfi_get_group(WINSEC_DESC *sec_desc)
    512490{
    513   return regfi_sid2str(sec_desc->grp_sid);
     491  return winsec_sid2str(sec_desc->grp_sid);
    514492}
    515493
     
    15001478    }
    15011479  }
     1480  else
     1481    ret_val = talloc_reference(NULL, ret_val);
    15021482
    15031483 unlock:
     
    20672047
    20682048  return regfi_load_sk(file, key->sk_off + REGFI_REGF_SIZE, true);
     2049}
     2050
     2051
     2052/******************************************************************************
     2053 *****************************************************************************/
     2054const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk)
     2055{
     2056  if(sk == NULL || sk->next_sk_off == REGFI_OFFSET_NONE)
     2057    return NULL;
     2058
     2059  return regfi_load_sk(file, sk->next_sk_off + REGFI_REGF_SIZE, true);
     2060}
     2061
     2062
     2063/******************************************************************************
     2064 *****************************************************************************/
     2065const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk)
     2066{
     2067  if(sk == NULL || sk->prev_sk_off == REGFI_OFFSET_NONE)
     2068    return NULL;
     2069
     2070  return regfi_load_sk(file, sk->prev_sk_off + REGFI_REGF_SIZE, true);
    20692071}
    20702072
  • trunk/lib/winsec.c

    r169 r253  
    226226  ret_val->size = SVAL(buf, 0x2);
    227227  ret_val->access_mask = IVAL(buf, 0x4);
    228 
     228  ret_val->obj_guid = NULL;
     229  ret_val->inh_guid = NULL;
     230 
    229231  offset = 0x8;
    230232
     
    246248      offset += sizeof(WINSEC_UUID);
    247249    }
    248     else
    249       ret_val->obj_guid = NULL;
    250250
    251251    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
     
    260260      offset += sizeof(WINSEC_UUID);
    261261    }
    262     else
    263       ret_val->inh_guid = NULL;
    264262  }
    265263
     
    410408
    411409/******************************************************************************
     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;
     415  uint8_t comps = sid->num_auths;
     416  char* ret_val = malloc(size);
     417 
     418  if(ret_val == NULL)
     419    return NULL;
     420
     421  if(comps > WINSEC_MAX_SUBAUTHS)
     422    comps = WINSEC_MAX_SUBAUTHS;
     423
     424  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
     425
     426  for (i = 0; i < comps; i++)
     427    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
     428
     429  return ret_val;
     430}
     431
     432
     433/******************************************************************************
    412434 * Compares two WINSEC_DESC structures.
    413435 ******************************************************************************/
  • trunk/python/pyregfi/__init__.py

    r252 r253  
    295295
    296296
    297 ## Registry security record and descriptor
    298 # XXX: Access to security descriptors not yet implemented
     297
     298## Represents a registry SK record which contains a security descriptor
     299#
    299300class Security(_StructureWrapper):
    300     pass
     301    ## Number of keys referencing this SK record
     302    ref_count = 1
     303
     304    ## The absolute file offset of the SK record's cell in the Hive file
     305    offset = 0xCAFEBABE
     306
     307    ## The @ref SecurityDescriptor for this SK record
     308    descriptor = object()
     309
     310    def __init__(self, hive, base):
     311        super(Security, self).__init__(hive, base)
     312        # XXX: add checks for NULL pointers
     313        self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents)
     314
     315    ## Loads the "previous" Security record in the hive
     316    #
     317    # @note
     318    # SK records are included in a circular, doubly-linked list.
     319    # To iterate over all SK records, be sure to check for the repetition of
     320    # the SK record you started with to determine when all have been traversed.
     321    def next_security(self):
     322        return Security(self._hive,
     323                        regfi.regfi_next_sk(self._hive.file, self._base))
     324
     325    ## Loads the "previous" Security record in the hive
     326    #
     327    # @note
     328    # SK records are included in a circular, doubly-linked list.
     329    # To iterate over all SK records, be sure to check for the repetition of
     330    # the SK record you started with to determine when all have been traversed.
     331    def prev_security(self):
     332        return Security(self._hive,
     333                        regfi.regfi_prev_sk(self._hive.file, self._base))
     334
    301335
    302336## Abstract class for ValueList and SubkeyList
     
    10381072del Key.name,Key.name_raw,Key.offset,Key.modified,Key.flags
    10391073del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
     1074del Security.ref_count,Security.offset,Security.descriptor
  • trunk/python/pyregfi/structures.py

    r252 r253  
    2121REGFI_DATA_TYPE = c_uint32
    2222REGFI_NTTIME = c_uint64
     23
     24REGFI_REGF_SIZE = 0x1000
    2325
    2426# Prototype everything first so we don't have to worry about reference order
     
    9496read_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), POINTER(c_char), c_size_t, use_errno=True)
    9597
     98
     99from winsec import *
    96100
    97101REGFI_VK._fields_ = [('offset', c_uint32),
     
    113117REGFI_SK._fields_ = [('offset', c_uint32),
    114118                     ('cell_size', c_uint32),
    115                      ('sec_desc', c_void_p), #XXX
     119                     ('sec_desc', POINTER(WINSEC_DESC)),
    116120                     ('hbin_off', c_uint32),
    117121                     ('prev_sk_off', c_uint32),
     
    261265regfi.regfi_fetch_sk.restype = POINTER(REGFI_SK)
    262266
     267regfi.regfi_next_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
     268regfi.regfi_next_sk.restype = POINTER(REGFI_SK)
     269
     270regfi.regfi_prev_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
     271regfi.regfi_prev_sk.restype = POINTER(REGFI_SK)
     272
    263273regfi.regfi_fetch_data.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_VK)]
    264274regfi.regfi_fetch_data.restype = POINTER(REGFI_DATA)
     
    283293regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK)
    284294
    285 regfi.regfi_nt2unix_time.argtypes = [POINTER(REGFI_NTTIME)]
     295regfi.regfi_nt2unix_time.argtypes = [REGFI_NTTIME]
    286296regfi.regfi_nt2unix_time.restype = c_double
    287297
Note: See TracChangeset for help on using the changeset viewer.