2 Unix SMB/CIFS mplementation.
5 Copyright (C) Stefan Metzmacher 2006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "dsdb/samdb/samdb.h"
25 #include "lib/util/dlinklist.h"
26 #include "librpc/gen_ndr/drsuapi.h"
28 #define _PREFIX(uint32, oid) {uint32,oid,sizeof(oid)}
33 } prefix_mappings[] = {
34 _PREFIX(0x00000000, "2.5.4."),
35 _PREFIX(0x00010000, "2.5.6."),
36 _PREFIX(0x00020000, "1.2.840.113556.1.2."),
37 _PREFIX(0x00030000, "1.2.840.113556.1.3."),
38 _PREFIX(0x00080000, "2.5.5."),
39 _PREFIX(0x00090000, "1.2.840.113556.1.4."),
40 _PREFIX(0x000A0000, "1.2.840.113556.1.5."),
41 _PREFIX(0x00140000, "2.16.840.1.113730.3."),
42 _PREFIX(0x00150000, "0.9.2342.19200300.100.1."),
43 _PREFIX(0x00160000, "2.16.840.1.113730.3.1."),
44 _PREFIX(0x00170000, "1.2.840.113556.1.5.7000."),
45 _PREFIX(0x00180000, "2.5.21."),
46 _PREFIX(0x00190000, "2.5.18."),
47 _PREFIX(0x001A0000, "2.5.20."),
48 _PREFIX(0x001B0000, "1.3.6.1.4.1.1466.101.119."),
49 _PREFIX(0x001C0000, "2.16.840.1.113730.3.2."),
50 _PREFIX(0x001D0000, "1.3.6.1.4.1.250.1."),
51 _PREFIX(0x001E0000, "1.2.840.113549.1.9."),
52 _PREFIX(0x001F0000, "0.9.2342.19200300.100.4."),
55 WERROR dsdb_map_oid2int(const char *in, uint32_t *out)
59 for (i=0; i < ARRAY_SIZE(prefix_mappings); i++) {
64 if (strncmp(prefix_mappings[i].oid, in, prefix_mappings[i].oid_len - 1) != 0) {
68 val_str = in + prefix_mappings[i].oid_len - 1;
72 if (val_str[0] == '\0') {
73 return WERR_INVALID_PARAM;
76 /* two '.' chars are invalid */
77 if (val_str[0] == '.') {
78 return WERR_INVALID_PARAM;
81 val = strtoul(val_str, &end_str, 10);
82 if (end_str[0] == '.' && end_str[1] != '\0') {
84 * if it's a '.' and not the last char
85 * then maybe an other mapping apply
88 } else if (end_str[0] != '\0') {
89 return WERR_INVALID_PARAM;
90 } else if (val > 0xFFFF) {
91 return WERR_INVALID_PARAM;
94 *out = prefix_mappings[i].uint32 | val;
98 return WERR_DS_NO_MSDS_INTID;
101 WERROR dsdb_map_int2oid(uint32_t in, TALLOC_CTX *mem_ctx, const char **out)
105 for (i=0; i < ARRAY_SIZE(prefix_mappings); i++) {
107 if (prefix_mappings[i].uint32 != (in & 0xFFFF0000)) {
111 val = talloc_asprintf(mem_ctx, "%s%u",
112 prefix_mappings[i].oid,
114 W_ERROR_HAVE_NO_MEMORY(val);
120 return WERR_DS_NO_MSDS_INTID;
123 #define GET_STRING_LDB(msg, attr, mem_ctx, p, elem, strict) do { \
124 (p)->elem = samdb_result_string(msg, attr, NULL);\
125 if (strict && (p)->elem == NULL) { \
126 d_printf("%s: %s == NULL\n", __location__, attr); \
127 return WERR_INVALID_PARAM; \
129 talloc_steal(mem_ctx, (p)->elem); \
132 #define GET_BOOL_LDB(msg, attr, p, elem, strict) do { \
134 str = samdb_result_string(msg, attr, NULL);\
137 d_printf("%s: %s == NULL\n", __location__, attr); \
138 return WERR_INVALID_PARAM; \
142 } else if (strcasecmp("TRUE", str) == 0) { \
144 } else if (strcasecmp("FALSE", str) == 0) { \
147 d_printf("%s: %s == %s\n", __location__, attr, str); \
148 return WERR_INVALID_PARAM; \
152 #define GET_UINT32_LDB(msg, attr, p, elem) do { \
153 (p)->elem = samdb_result_uint(msg, attr, 0);\
156 #define GET_GUID_LDB(msg, attr, p, elem) do { \
157 (p)->elem = samdb_result_guid(msg, attr);\
160 #define GET_BLOB_LDB(msg, attr, mem_ctx, p, elem) do { \
161 const struct ldb_val *_val;\
162 _val = ldb_msg_find_ldb_val(msg, attr);\
165 talloc_steal(mem_ctx, (p)->elem.data);\
167 ZERO_STRUCT((p)->elem);\
171 WERROR dsdb_attribute_from_ldb(struct ldb_message *msg, TALLOC_CTX *mem_ctx, struct dsdb_attribute *attr)
175 GET_STRING_LDB(msg, "cn", mem_ctx, attr, cn, True);
176 GET_STRING_LDB(msg, "lDAPDisplayName", mem_ctx, attr, lDAPDisplayName, True);
177 GET_STRING_LDB(msg, "attributeID", mem_ctx, attr, attributeID_oid, True);
178 status = dsdb_map_oid2int(attr->attributeID_oid, &attr->attributeID_id);
179 if (!W_ERROR_IS_OK(status)) {
180 DEBUG(0,("%s: '%s': unable to map attributeID '%s': %s\n",
181 __location__, attr->lDAPDisplayName, attr->attributeID_oid,
182 win_errstr(status)));
185 GET_GUID_LDB(msg, "schemaIDGUID", attr, schemaIDGUID);
186 GET_UINT32_LDB(msg, "mAPIID", attr, mAPIID);
188 GET_GUID_LDB(msg, "attributeSecurityGUID", attr, attributeSecurityGUID);
190 GET_UINT32_LDB(msg, "searchFlags", attr, searchFlags);
191 GET_UINT32_LDB(msg, "systemFlags", attr, systemFlags);
192 GET_BOOL_LDB(msg, "isMemberOfPartialAttributeSet", attr, isMemberOfPartialAttributeSet, False);
193 GET_UINT32_LDB(msg, "linkID", attr, linkID);
195 GET_STRING_LDB(msg, "attributeSyntax", mem_ctx, attr, attributeSyntax_oid, True);
196 status = dsdb_map_oid2int(attr->attributeSyntax_oid, &attr->attributeSyntax_id);
197 if (!W_ERROR_IS_OK(status)) {
198 DEBUG(0,("%s: '%s': unable to map attributeSyntax '%s': %s\n",
199 __location__, attr->lDAPDisplayName, attr->attributeSyntax_oid,
200 win_errstr(status)));
203 GET_UINT32_LDB(msg, "oMSyntax", attr, oMSyntax);
204 GET_BLOB_LDB(msg, "oMObjectClass", mem_ctx, attr, oMObjectClass);
206 GET_BOOL_LDB(msg, "isSingleValued", attr, isSingleValued, True);
207 GET_UINT32_LDB(msg, "rangeLower", attr, rangeLower);
208 GET_UINT32_LDB(msg, "rangeUpper", attr, rangeUpper);
209 GET_BOOL_LDB(msg, "extendedCharsAllowed", attr, extendedCharsAllowed, False);
211 GET_UINT32_LDB(msg, "schemaFlagsEx", attr, schemaFlagsEx);
212 GET_BLOB_LDB(msg, "msDs-Schema-Extensions", mem_ctx, attr, msDs_Schema_Extensions);
214 GET_BOOL_LDB(msg, "showInAdvancedViewOnly", attr, showInAdvancedViewOnly, False);
215 GET_STRING_LDB(msg, "adminDisplayName", mem_ctx, attr, adminDisplayName, False);
216 GET_STRING_LDB(msg, "adminDescription", mem_ctx, attr, adminDescription, False);
217 GET_STRING_LDB(msg, "classDisplayName", mem_ctx, attr, classDisplayName, False);
218 GET_BOOL_LDB(msg, "isEphemeral", attr, isEphemeral, False);
219 GET_BOOL_LDB(msg, "isDefunct", attr, isDefunct, False);
220 GET_BOOL_LDB(msg, "systemOnly", attr, systemOnly, False);
225 WERROR dsdb_class_from_ldb(struct ldb_message *msg, TALLOC_CTX *mem_ctx, struct dsdb_class *obj)
229 GET_STRING_LDB(msg, "cn", mem_ctx, obj, cn, True);
230 GET_STRING_LDB(msg, "lDAPDisplayName", mem_ctx, obj, lDAPDisplayName, True);
231 GET_STRING_LDB(msg, "governsID", mem_ctx, obj, governsID_oid, True);
232 status = dsdb_map_oid2int(obj->governsID_oid, &obj->governsID_id);
233 if (!W_ERROR_IS_OK(status)) {
234 DEBUG(0,("%s: '%s': unable to map governsID '%s': %s\n",
235 __location__, obj->lDAPDisplayName, obj->governsID_oid,
236 win_errstr(status)));
239 GET_GUID_LDB(msg, "schemaIDGUID", obj, schemaIDGUID);
241 GET_UINT32_LDB(msg, "objectClassCategory", obj, objectClassCategory);
242 GET_STRING_LDB(msg, "rDNAttID", mem_ctx, obj, rDNAttID, False);
243 GET_STRING_LDB(msg, "defaultObjectCategory", mem_ctx, obj, defaultObjectCategory, True);
245 GET_STRING_LDB(msg, "subClassOf", mem_ctx, obj, subClassOf, True);
247 GET_STRING_LDB(msg, "systemAuxiliaryClass", mem_ctx, obj, systemAuxiliaryClass, False);
248 obj->systemPossSuperiors= NULL;
249 obj->systemMustContain = NULL;
250 obj->systemMayContain = NULL;
252 GET_STRING_LDB(msg, "auxiliaryClass", mem_ctx, obj, auxiliaryClass, False);
253 obj->possSuperiors = NULL;
254 obj->mustContain = NULL;
255 obj->mayContain = NULL;
257 GET_STRING_LDB(msg, "defaultSecurityDescriptor", mem_ctx, obj, defaultSecurityDescriptor, False);
259 GET_UINT32_LDB(msg, "schemaFlagsEx", obj, schemaFlagsEx);
260 GET_BLOB_LDB(msg, "msDs-Schema-Extensions", mem_ctx, obj, msDs_Schema_Extensions);
262 GET_BOOL_LDB(msg, "showInAdvancedViewOnly", obj, showInAdvancedViewOnly, False);
263 GET_STRING_LDB(msg, "adminDisplayName", mem_ctx, obj, adminDisplayName, False);
264 GET_STRING_LDB(msg, "adminDescription", mem_ctx, obj, adminDescription, False);
265 GET_STRING_LDB(msg, "classDisplayName", mem_ctx, obj, classDisplayName, False);
266 GET_BOOL_LDB(msg, "defaultHidingValue", obj, defaultHidingValue, False);
267 GET_BOOL_LDB(msg, "isDefunct", obj, isDefunct, False);
268 GET_BOOL_LDB(msg, "systemOnly", obj, systemOnly, False);
273 static const struct {
276 } name_mappings[] = {
278 { "name", "1.2.840.113556.1.4.1" },
279 { "lDAPDisplayName", "1.2.840.113556.1.2.460" },
280 { "attributeID", "1.2.840.113556.1.2.30" },
281 { "schemaIDGUID", "1.2.840.113556.1.4.148" },
282 { "mAPIID", "1.2.840.113556.1.2.49" },
283 { "attributeSecurityGUID", "1.2.840.113556.1.4.149" },
284 { "searchFlags", "1.2.840.113556.1.2.334" },
285 { "systemFlags", "1.2.840.113556.1.4.375" },
286 { "isMemberOfPartialAttributeSet", "1.2.840.113556.1.4.639" },
287 { "linkID", "1.2.840.113556.1.2.50" },
288 { "attributeSyntax", "1.2.840.113556.1.2.30" },
289 { "oMSyntax", "1.2.840.113556.1.2.231" },
290 { "oMObjectClass", "1.2.840.113556.1.2.218" },
291 { "isSingleValued", "1.2.840.113556.1.2.33" },
292 { "rangeLower", "1.2.840.113556.1.2.34" },
293 { "rangeUpper", "1.2.840.113556.1.2.35" },
294 { "extendedCharsAllowed", "1.2.840.113556.1.2.380" },
295 { "schemaFlagsEx", "1.2.840.113556.1.4.120" },
296 { "msDs-Schema-Extensions", "1.2.840.113556.1.4.1440" },
297 { "showInAdvancedViewOnly", "1.2.840.113556.1.2.169" },
298 { "adminDisplayName", "1.2.840.113556.1.2.194" },
299 { "adminDescription", "1.2.840.113556.1.2.226" },
300 { "classDisplayName", "1.2.840.113556.1.4.610" },
301 { "isEphemeral", "1.2.840.113556.1.4.1212" },
302 { "isDefunct", "1.2.840.113556.1.4.661" },
303 { "systemOnly", "1.2.840.113556.1.4.170" },
304 { "governsID", "1.2.840.113556.1.2.22" },
305 { "objectClassCategory", "1.2.840.113556.1.2.370" },
306 { "rDNAttID", "1.2.840.113556.1.2.26" },
307 { "defaultObjectCategory", "1.2.840.113556.1.4.783" },
308 { "subClassOf", "1.2.840.113556.1.2.21" },
309 { "systemAuxiliaryClass", "1.2.840.113556.1.4.198" },
310 { "systemPossSuperiors", "1.2.840.113556.1.4.195" },
311 { "systemMustContain", "1.2.840.113556.1.4.197" },
312 { "systemMayContain", "1.2.840.113556.1.4.196" },
313 { "auxiliaryClass", "1.2.840.113556.1.2.351" },
314 { "possSuperiors", "1.2.840.113556.1.2.8" },
315 { "mustContain", "1.2.840.113556.1.2.24" },
316 { "mayContain", "1.2.840.113556.1.2.25" },
317 { "defaultSecurityDescriptor", "1.2.840.113556.1.4.224" },
318 { "defaultHidingValue", "1.2.840.113556.1.4.518" },
321 static struct drsuapi_DsReplicaAttribute *dsdb_find_object_attr_name(struct drsuapi_DsReplicaObject *obj,
327 const char *oid = NULL;
329 for(i=0; i < ARRAY_SIZE(name_mappings); i++) {
330 if (strcmp(name_mappings[i].name, name) != 0) continue;
332 oid = name_mappings[i].oid;
340 status = dsdb_map_oid2int(oid, &id);
341 if (!W_ERROR_IS_OK(status)) {
345 for (i=0; i < obj->attribute_ctr.num_attributes; i++) {
346 if (obj->attribute_ctr.attributes[i].attid != id) continue;
349 return &obj->attribute_ctr.attributes[i];
355 #define GET_STRING_DS(r, attr, mem_ctx, p, elem, strict) do { \
356 struct drsuapi_DsReplicaAttribute *_a; \
357 _a = dsdb_find_object_attr_name(r, attr, NULL); \
358 if (strict && !_a) { \
359 d_printf("%s: %s == NULL\n", __location__, attr); \
360 return WERR_INVALID_PARAM; \
362 if (strict && _a->value_ctr.unicode_string.num_values != 1) { \
363 d_printf("%s: %s num_values == %u\n", __location__, attr, \
364 _a->value_ctr.unicode_string.num_values); \
365 return WERR_INVALID_PARAM; \
367 if (_a && _a->value_ctr.unicode_string.num_values >= 1) { \
368 (p)->elem = talloc_steal(mem_ctx, _a->value_ctr.unicode_string.values[0].string);\
374 #define GET_BOOL_DS(r, attr, p, elem, strict) do { \
375 struct drsuapi_DsReplicaAttribute *_a; \
376 _a = dsdb_find_object_attr_name(r, attr, NULL); \
377 if (strict && !_a) { \
378 d_printf("%s: %s == NULL\n", __location__, attr); \
379 return WERR_INVALID_PARAM; \
381 if (strict && _a->value_ctr.uint32.num_values != 1) { \
382 d_printf("%s: %s num_values == %u\n", __location__, attr, \
383 _a->value_ctr.uint32.num_values); \
384 return WERR_INVALID_PARAM; \
386 if (strict && !_a->value_ctr.uint32.values[0].value) { \
387 d_printf("%s: %s value == NULL\n", __location__, attr); \
388 return WERR_INVALID_PARAM; \
390 if (_a && _a->value_ctr.uint32.num_values >= 1 \
391 && _a->value_ctr.uint32.values[0].value) { \
392 (p)->elem = (*_a->value_ctr.uint32.values[0].value?True:False);\
398 #define GET_UINT32_DS(r, attr, p, elem) do { \
399 struct drsuapi_DsReplicaAttribute *_a; \
400 _a = dsdb_find_object_attr_name(r, attr, NULL); \
401 if (_a && _a->value_ctr.uint32.num_values >= 1 \
402 && _a->value_ctr.uint32.values[0].value) { \
403 (p)->elem = *_a->value_ctr.uint32.values[0].value;\
409 #define GET_GUID_DS(r, attr, p, elem) do { \
410 struct drsuapi_DsReplicaAttribute *_a; \
411 _a = dsdb_find_object_attr_name(r, attr, NULL); \
412 if (_a && _a->value_ctr.guid.num_values >= 1 \
413 && _a->value_ctr.guid.values[0].guid) { \
414 (p)->elem = *_a->value_ctr.guid.values[0].guid;\
416 ZERO_STRUCT((p)->elem);\
420 #define GET_BLOB_DS(r, attr, mem_ctx, p, elem) do { \
421 struct drsuapi_DsReplicaAttribute *_a; \
422 _a = dsdb_find_object_attr_name(r, attr, NULL); \
423 if (_a && _a->value_ctr.data_blob.num_values >= 1 \
424 && _a->value_ctr.data_blob.values[0].data) { \
425 (p)->elem = *_a->value_ctr.data_blob.values[0].data;\
426 talloc_steal(mem_ctx, (p)->elem.data); \
428 ZERO_STRUCT((p)->elem);\
432 WERROR dsdb_attribute_from_drsuapi(struct drsuapi_DsReplicaObject *r, TALLOC_CTX *mem_ctx, struct dsdb_attribute *attr)
436 GET_STRING_DS(r, "name", mem_ctx, attr, cn, True);
437 GET_STRING_DS(r, "lDAPDisplayName", mem_ctx, attr, lDAPDisplayName, True);
438 GET_UINT32_DS(r, "attributeID", attr, attributeID_id);
439 status = dsdb_map_int2oid(attr->attributeID_id, mem_ctx, &attr->attributeID_oid);
440 if (!W_ERROR_IS_OK(status)) {
441 DEBUG(0,("%s: '%s': unable to map attributeID 0x%08X: %s\n",
442 __location__, attr->lDAPDisplayName, attr->attributeID_id,
443 win_errstr(status)));
446 GET_GUID_DS(r, "schemaIDGUID", attr, schemaIDGUID);
447 GET_UINT32_DS(r, "mAPIID", attr, mAPIID);
449 GET_GUID_DS(r, "attributeSecurityGUID", attr, attributeSecurityGUID);
451 GET_UINT32_DS(r, "searchFlags", attr, searchFlags);
452 GET_UINT32_DS(r, "systemFlags", attr, systemFlags);
453 GET_BOOL_DS(r, "isMemberOfPartialAttributeSet", attr, isMemberOfPartialAttributeSet, False);
454 GET_UINT32_DS(r, "linkID", attr, linkID);
456 GET_UINT32_DS(r, "attributeSyntax", attr, attributeSyntax_id);
457 status = dsdb_map_int2oid(attr->attributeSyntax_id, mem_ctx, &attr->attributeSyntax_oid);
458 if (!W_ERROR_IS_OK(status)) {
459 DEBUG(0,("%s: '%s': unable to map attributeSyntax 0x%08X: %s\n",
460 __location__, attr->lDAPDisplayName, attr->attributeSyntax_id,
461 win_errstr(status)));
464 GET_UINT32_DS(r, "oMSyntax", attr, oMSyntax);
465 GET_BLOB_DS(r, "oMObjectClass", mem_ctx, attr, oMObjectClass);
467 GET_BOOL_DS(r, "isSingleValued", attr, isSingleValued, True);
468 GET_UINT32_DS(r, "rangeLower", attr, rangeLower);
469 GET_UINT32_DS(r, "rangeUpper", attr, rangeUpper);
470 GET_BOOL_DS(r, "extendedCharsAllowed", attr, extendedCharsAllowed, False);
472 GET_UINT32_DS(r, "schemaFlagsEx", attr, schemaFlagsEx);
473 GET_BLOB_DS(r, "msDs-Schema-Extensions", mem_ctx, attr, msDs_Schema_Extensions);
475 GET_BOOL_DS(r, "showInAdvancedViewOnly", attr, showInAdvancedViewOnly, False);
476 GET_STRING_DS(r, "adminDisplayName", mem_ctx, attr, adminDisplayName, False);
477 GET_STRING_DS(r, "adminDescription", mem_ctx, attr, adminDescription, False);
478 GET_STRING_DS(r, "classDisplayName", mem_ctx, attr, classDisplayName, False);
479 GET_BOOL_DS(r, "isEphemeral", attr, isEphemeral, False);
480 GET_BOOL_DS(r, "isDefunct", attr, isDefunct, False);
481 GET_BOOL_DS(r, "systemOnly", attr, systemOnly, False);
486 WERROR dsdb_class_from_drsuapi(struct drsuapi_DsReplicaObject *r, TALLOC_CTX *mem_ctx, struct dsdb_class *obj)
490 GET_STRING_DS(r, "name", mem_ctx, obj, cn, True);
491 GET_STRING_DS(r, "lDAPDisplayName", mem_ctx, obj, lDAPDisplayName, True);
492 GET_UINT32_DS(r, "governsID", obj, governsID_id);
493 status = dsdb_map_int2oid(obj->governsID_id, mem_ctx, &obj->governsID_oid);
494 if (!W_ERROR_IS_OK(status)) {
495 DEBUG(0,("%s: '%s': unable to map governsID 0x%08X: %s\n",
496 __location__, obj->lDAPDisplayName, obj->governsID_id,
497 win_errstr(status)));
500 GET_GUID_DS(r, "schemaIDGUID", obj, schemaIDGUID);
502 GET_UINT32_DS(r, "objectClassCategory", obj, objectClassCategory);
503 GET_STRING_DS(r, "rDNAttID", mem_ctx, obj, rDNAttID, False);
504 GET_STRING_DS(r, "defaultObjectCategory", mem_ctx, obj, defaultObjectCategory, True);
506 GET_STRING_DS(r, "subClassOf", mem_ctx, obj, subClassOf, True);
508 GET_STRING_DS(r, "systemAuxiliaryClass", mem_ctx, obj, systemAuxiliaryClass, False);
509 obj->systemPossSuperiors= NULL;
510 obj->systemMustContain = NULL;
511 obj->systemMayContain = NULL;
513 GET_STRING_DS(r, "auxiliaryClass", mem_ctx, obj, auxiliaryClass, False);
514 obj->possSuperiors = NULL;
515 obj->mustContain = NULL;
516 obj->mayContain = NULL;
518 GET_STRING_DS(r, "defaultSecurityDescriptor", mem_ctx, obj, defaultSecurityDescriptor, False);
520 GET_UINT32_DS(r, "schemaFlagsEx", obj, schemaFlagsEx);
521 GET_BLOB_DS(r, "msDs-Schema-Extensions", mem_ctx, obj, msDs_Schema_Extensions);
523 GET_BOOL_DS(r, "showInAdvancedViewOnly", obj, showInAdvancedViewOnly, False);
524 GET_STRING_DS(r, "adminDisplayName", mem_ctx, obj, adminDisplayName, False);
525 GET_STRING_DS(r, "adminDescription", mem_ctx, obj, adminDescription, False);
526 GET_STRING_DS(r, "classDisplayName", mem_ctx, obj, classDisplayName, False);
527 GET_BOOL_DS(r, "defaultHidingValue", obj, defaultHidingValue, False);
528 GET_BOOL_DS(r, "isDefunct", obj, isDefunct, False);
529 GET_BOOL_DS(r, "systemOnly", obj, systemOnly, False);