Line data Source code
1 : /*
2 : Samba4 module loading module
3 :
4 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
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; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : 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. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /*
21 : * Name: ldb
22 : *
23 : * Component: Samba4 module loading module
24 : *
25 : * Description: Implement a single 'module' in the ldb database,
26 : * which loads the remaining modules based on 'choice of configuration' attributes
27 : *
28 : * This is to avoid forcing a reprovision of the ldb databases when we change the internal structure of the code
29 : *
30 : * Author: Andrew Bartlett
31 : */
32 :
33 : #include "includes.h"
34 : #include <ldb.h>
35 : #include <ldb_errors.h>
36 : #include <ldb_module.h>
37 : #include "dsdb/samdb/ldb_modules/util.h"
38 : #include "dsdb/samdb/samdb.h"
39 : #include "librpc/ndr/libndr.h"
40 : #include "auth/credentials/credentials.h"
41 : #include "param/secrets.h"
42 : #include "lib/ldb-samba/ldb_wrap.h"
43 :
44 108019 : static int read_at_rootdse_record(struct ldb_context *ldb, struct ldb_module *module, TALLOC_CTX *mem_ctx,
45 : struct ldb_message **msg, struct ldb_request *parent)
46 : {
47 : int ret;
48 : static const char *rootdse_attrs[] = { "defaultNamingContext", "configurationNamingContext", "schemaNamingContext", NULL };
49 : struct ldb_result *rootdse_res;
50 : struct ldb_dn *rootdse_dn;
51 108019 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
52 108019 : if (!tmp_ctx) {
53 0 : return ldb_oom(ldb);
54 : }
55 :
56 108019 : rootdse_dn = ldb_dn_new(tmp_ctx, ldb, "@ROOTDSE");
57 108019 : if (!rootdse_dn) {
58 0 : talloc_free(tmp_ctx);
59 0 : return ldb_oom(ldb);
60 : }
61 :
62 108019 : ret = dsdb_module_search_dn(module, tmp_ctx, &rootdse_res, rootdse_dn,
63 : rootdse_attrs, DSDB_FLAG_NEXT_MODULE, parent);
64 108019 : if (ret != LDB_SUCCESS) {
65 6 : talloc_free(tmp_ctx);
66 6 : return ret;
67 : }
68 :
69 108013 : talloc_steal(mem_ctx, rootdse_res->msgs);
70 108013 : *msg = rootdse_res->msgs[0];
71 :
72 108013 : talloc_free(tmp_ctx);
73 :
74 108013 : return ret;
75 : }
76 :
77 216026 : static int prepare_modules_line(struct ldb_context *ldb,
78 : TALLOC_CTX *mem_ctx,
79 : const struct ldb_message *rootdse_msg,
80 : struct ldb_message *msg, const char *backend_attr,
81 : const char *backend_mod, const char **backend_mod_list)
82 : {
83 : int ret;
84 : const char **backend_full_list;
85 : const char *backend_dn;
86 : char *mod_list_string;
87 : char *full_string;
88 216026 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
89 216026 : if (!tmp_ctx) {
90 0 : return ldb_oom(ldb);
91 : }
92 :
93 216026 : if (backend_attr) {
94 108013 : backend_dn = ldb_msg_find_attr_as_string(rootdse_msg, backend_attr, NULL);
95 108013 : if (!backend_dn) {
96 0 : ldb_asprintf_errstring(ldb,
97 : "samba_dsdb_init: "
98 : "unable to read %s from %s:%s",
99 0 : backend_attr, ldb_dn_get_linearized(rootdse_msg->dn),
100 : ldb_errstring(ldb));
101 0 : return LDB_ERR_CONSTRAINT_VIOLATION;
102 : }
103 : } else {
104 108013 : backend_dn = "*";
105 : }
106 :
107 216026 : if (backend_mod) {
108 108013 : char **b = str_list_make_single(tmp_ctx, backend_mod);
109 108013 : backend_full_list = discard_const_p(const char *, b);
110 : } else {
111 108013 : char **b = str_list_make_empty(tmp_ctx);
112 108013 : backend_full_list = discard_const_p(const char *, b);
113 : }
114 216026 : if (!backend_full_list) {
115 0 : talloc_free(tmp_ctx);
116 0 : return ldb_oom(ldb);
117 : }
118 :
119 216026 : backend_full_list = str_list_append_const(backend_full_list, backend_mod_list);
120 216026 : if (!backend_full_list) {
121 0 : talloc_free(tmp_ctx);
122 0 : return ldb_oom(ldb);
123 : }
124 :
125 216026 : mod_list_string = str_list_join(tmp_ctx, backend_full_list, ',');
126 :
127 : /* str_list_append allocates on NULL */
128 216026 : talloc_free(backend_full_list);
129 :
130 216026 : if (!mod_list_string) {
131 0 : talloc_free(tmp_ctx);
132 0 : return ldb_oom(ldb);
133 : }
134 :
135 216026 : full_string = talloc_asprintf(tmp_ctx, "%s:%s", backend_dn, mod_list_string);
136 216026 : ret = ldb_msg_add_steal_string(msg, "modules", full_string);
137 216026 : talloc_free(tmp_ctx);
138 216026 : return ret;
139 : }
140 :
141 108013 : static bool check_required_features(struct ldb_message_element *el)
142 : {
143 108013 : if (el != NULL) {
144 : int k;
145 95636 : DATA_BLOB esf = data_blob_string_const(
146 : SAMBA_ENCRYPTED_SECRETS_FEATURE);
147 95636 : DATA_BLOB lmdbl1 = data_blob_string_const(
148 : SAMBA_LMDB_LEVEL_ONE_FEATURE);
149 221579 : for (k = 0; k < el->num_values; k++) {
150 156250 : if ((data_blob_cmp(&esf, &el->values[k]) != 0) &&
151 30307 : (data_blob_cmp(&lmdbl1, &el->values[k]) != 0)) {
152 0 : return false;
153 : }
154 : }
155 : }
156 108013 : return true;
157 : }
158 :
159 108019 : static int samba_dsdb_init(struct ldb_module *module)
160 : {
161 108019 : struct ldb_context *ldb = ldb_module_get_ctx(module);
162 : int ret, lock_ret, len, i, j;
163 108019 : TALLOC_CTX *tmp_ctx = talloc_new(module);
164 : struct ldb_result *res;
165 108019 : struct ldb_message *rootdse_msg = NULL, *partition_msg;
166 : struct ldb_dn *samba_dsdb_dn, *partition_dn, *indexlist_dn;
167 : struct ldb_module *backend_module, *module_chain;
168 : const char **final_module_list, **reverse_module_list;
169 : /*
170 : Add modules to the list to activate them by default
171 : beware often order is important
172 :
173 : Some Known ordering constraints:
174 : - rootdse must be first, as it makes redirects from "" -> cn=rootdse
175 : - extended_dn_in must be before objectclass.c, as it resolves the DN
176 : - objectclass must be before password_hash and samldb since these LDB
177 : modules require the expanded "objectClass" list
178 : - objectclass must be before descriptor and acl, as both assume that
179 : objectClass values are sorted
180 : - objectclass_attrs must be behind operational in order to see all
181 : attributes (the operational module protects and therefore
182 : suppresses per default some important ones)
183 : - partition must be last
184 : - each partition has its own module list then
185 :
186 : The list is presented here as a set of declarations to show the
187 : stack visually - the code below then handles the creation of the list
188 : based on the parameters loaded from the database.
189 : */
190 : static const char *modules_list1[] = {"resolve_oids",
191 : "rootdse",
192 : "dsdb_notification",
193 : "schema_load",
194 : "lazy_commit",
195 : "dirsync",
196 : "dsdb_paged_results",
197 : "vlv",
198 : "ranged_results",
199 : "anr",
200 : "server_sort",
201 : "asq",
202 : "extended_dn_store",
203 : NULL };
204 : /* extended_dn_in or extended_dn_in_openldap goes here */
205 : static const char *modules_list1a[] = {"audit_log",
206 : "objectclass",
207 : "tombstone_reanimate",
208 : "descriptor",
209 : "acl",
210 : "aclread",
211 : "samldb",
212 : "password_hash",
213 : "instancetype",
214 : "objectclass_attrs",
215 : NULL };
216 :
217 : const char **link_modules;
218 : static const char *tdb_modules_list[] = {
219 : "rdn_name",
220 : "subtree_delete",
221 : "repl_meta_data",
222 : "group_audit_log",
223 : "encrypted_secrets",
224 : "operational",
225 : "unique_object_sids",
226 : "subtree_rename",
227 : "linked_attributes",
228 : NULL};
229 :
230 : const char *extended_dn_module;
231 108019 : const char *extended_dn_module_ldb = "extended_dn_out_ldb";
232 108019 : const char *extended_dn_in_module = "extended_dn_in";
233 :
234 : static const char *modules_list2[] = {"dns_notify",
235 : "show_deleted",
236 : "new_partition",
237 : "partition",
238 : NULL };
239 :
240 : const char **backend_modules;
241 : static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR,
242 : SAMBA_REQUIRED_FEATURES_ATTR, NULL };
243 : static const char *indexlist_attrs[] = { SAMBA_FEATURES_SUPPORTED_FLAG, NULL };
244 :
245 108019 : const char *current_supportedFeatures[] = {SAMBA_SORTED_LINKS_FEATURE};
246 :
247 108019 : if (!tmp_ctx) {
248 0 : return ldb_oom(ldb);
249 : }
250 :
251 108019 : ret = ldb_register_samba_handlers(ldb);
252 108019 : if (ret != LDB_SUCCESS) {
253 0 : talloc_free(tmp_ctx);
254 0 : return ret;
255 : }
256 :
257 108019 : samba_dsdb_dn = ldb_dn_new(tmp_ctx, ldb, "@SAMBA_DSDB");
258 108019 : if (!samba_dsdb_dn) {
259 0 : talloc_free(tmp_ctx);
260 0 : return ldb_oom(ldb);
261 : }
262 :
263 108019 : indexlist_dn = ldb_dn_new(tmp_ctx, ldb, "@INDEXLIST");
264 108019 : if (!samba_dsdb_dn) {
265 0 : talloc_free(tmp_ctx);
266 0 : return ldb_oom(ldb);
267 : }
268 :
269 108019 : partition_dn = ldb_dn_new(tmp_ctx, ldb, DSDB_PARTITION_DN);
270 108019 : if (!partition_dn) {
271 0 : talloc_free(tmp_ctx);
272 0 : return ldb_oom(ldb);
273 : }
274 :
275 : #define CHECK_LDB_RET(check_ret) \
276 : do { \
277 : if (check_ret != LDB_SUCCESS) { \
278 : talloc_free(tmp_ctx); \
279 : return check_ret; \
280 : } \
281 : } while (0)
282 :
283 108019 : ret = dsdb_module_search_dn(module, tmp_ctx, &res, samba_dsdb_dn,
284 : samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
285 108019 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
286 : /* do nothing, a very old db being upgraded */
287 108013 : } else if (ret == LDB_SUCCESS) {
288 : struct ldb_message_element *requiredFeatures;
289 : struct ldb_message_element *old_compatibleFeatures;
290 :
291 108013 : requiredFeatures = ldb_msg_find_element(res->msgs[0], SAMBA_REQUIRED_FEATURES_ATTR);
292 108013 : if (!check_required_features(requiredFeatures)) {
293 0 : ldb_set_errstring(
294 : ldb,
295 : "This Samba database was created with "
296 : "a newer Samba version and is marked "
297 : "with extra requiredFeatures in "
298 : "@SAMBA_DSDB. This database can not "
299 : "safely be read by this Samba version");
300 0 : return LDB_ERR_OPERATIONS_ERROR;
301 : }
302 :
303 108013 : old_compatibleFeatures = ldb_msg_find_element(res->msgs[0],
304 : SAMBA_COMPATIBLE_FEATURES_ATTR);
305 :
306 108013 : if (old_compatibleFeatures) {
307 : struct ldb_message *features_msg;
308 : struct ldb_message_element *features_el;
309 107788 : int samba_options_supported = 0;
310 107788 : ret = dsdb_module_search_dn(module, tmp_ctx, &res,
311 : indexlist_dn,
312 : indexlist_attrs,
313 : DSDB_FLAG_NEXT_MODULE, NULL);
314 107788 : if (ret == LDB_SUCCESS) {
315 : samba_options_supported
316 107641 : = ldb_msg_find_attr_as_int(res->msgs[0],
317 : SAMBA_FEATURES_SUPPORTED_FLAG,
318 : 0);
319 :
320 147 : } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
321 : /*
322 : * If we don't have @INDEXLIST yet, then we
323 : * are so early in set-up that we know this is
324 : * a blank DB, so no need to wripe out old
325 : * features
326 : */
327 147 : samba_options_supported = 1;
328 : }
329 :
330 107788 : features_msg = ldb_msg_new(res);
331 107788 : if (features_msg == NULL) {
332 0 : return ldb_module_operr(module);
333 : }
334 107788 : features_msg->dn = samba_dsdb_dn;
335 :
336 107788 : ldb_msg_add_empty(features_msg, SAMBA_COMPATIBLE_FEATURES_ATTR,
337 : LDB_FLAG_MOD_DELETE, &features_el);
338 :
339 107788 : if (samba_options_supported == 1) {
340 269278 : for (i = 0;
341 215576 : old_compatibleFeatures && i < old_compatibleFeatures->num_values;
342 107788 : i++) {
343 188533 : for (j = 0;
344 27043 : j < ARRAY_SIZE(current_supportedFeatures); j++) {
345 107788 : if (strcmp((char *)old_compatibleFeatures->values[i].data,
346 : current_supportedFeatures[j]) == 0) {
347 107788 : break;
348 : }
349 : }
350 107788 : if (j == ARRAY_SIZE(current_supportedFeatures)) {
351 : /*
352 : * Add to list of features to remove
353 : * (rather than all features)
354 : */
355 0 : ret = ldb_msg_add_value(features_msg, SAMBA_COMPATIBLE_FEATURES_ATTR,
356 0 : &old_compatibleFeatures->values[i],
357 : NULL);
358 0 : if (ret != LDB_SUCCESS) {
359 0 : return ret;
360 : }
361 : }
362 : }
363 :
364 107788 : if (features_el->num_values > 0) {
365 : /* Delete by list */
366 0 : ret = ldb_next_start_trans(module);
367 0 : if (ret != LDB_SUCCESS) {
368 0 : return ret;
369 : }
370 0 : ret = dsdb_module_modify(module, features_msg, DSDB_FLAG_NEXT_MODULE, NULL);
371 0 : if (ret != LDB_SUCCESS) {
372 0 : ldb_next_del_trans(module);
373 0 : return ret;
374 : }
375 0 : ret = ldb_next_end_trans(module);
376 0 : if (ret != LDB_SUCCESS) {
377 0 : return ret;
378 : }
379 : }
380 : } else {
381 : /* Delete all */
382 0 : ret = ldb_next_start_trans(module);
383 0 : if (ret != LDB_SUCCESS) {
384 0 : return ret;
385 : }
386 0 : ret = dsdb_module_modify(module, features_msg, DSDB_FLAG_NEXT_MODULE, NULL);
387 0 : if (ret != LDB_SUCCESS) {
388 0 : ldb_next_del_trans(module);
389 0 : return ret;
390 : }
391 0 : ret = ldb_next_end_trans(module);
392 0 : if (ret != LDB_SUCCESS) {
393 0 : return ret;
394 : }
395 : }
396 : }
397 :
398 : } else {
399 0 : talloc_free(tmp_ctx);
400 0 : return ret;
401 : }
402 :
403 108019 : backend_modules = NULL;
404 108019 : extended_dn_module = extended_dn_module_ldb;
405 108019 : link_modules = tdb_modules_list;
406 :
407 : #define CHECK_MODULE_LIST \
408 : do { \
409 : if (!final_module_list) { \
410 : talloc_free(tmp_ctx); \
411 : return ldb_oom(ldb); \
412 : } \
413 : } while (0)
414 :
415 108019 : final_module_list = str_list_copy_const(tmp_ctx, modules_list1);
416 108019 : CHECK_MODULE_LIST;
417 :
418 108019 : final_module_list = str_list_add_const(final_module_list, extended_dn_in_module);
419 108019 : CHECK_MODULE_LIST;
420 :
421 108019 : final_module_list = str_list_append_const(final_module_list, modules_list1a);
422 108019 : CHECK_MODULE_LIST;
423 :
424 108019 : final_module_list = str_list_append_const(final_module_list, link_modules);
425 108019 : CHECK_MODULE_LIST;
426 :
427 108019 : final_module_list = str_list_add_const(final_module_list, extended_dn_module);
428 108019 : CHECK_MODULE_LIST;
429 :
430 108019 : final_module_list = str_list_append_const(final_module_list, modules_list2);
431 108019 : CHECK_MODULE_LIST;
432 :
433 :
434 108019 : ret = read_at_rootdse_record(ldb, module, tmp_ctx, &rootdse_msg, NULL);
435 108019 : CHECK_LDB_RET(ret);
436 :
437 108013 : partition_msg = ldb_msg_new(tmp_ctx);
438 108013 : partition_msg->dn = ldb_dn_new(partition_msg, ldb, "@" DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME);
439 :
440 108013 : ret = prepare_modules_line(ldb, tmp_ctx,
441 : rootdse_msg,
442 : partition_msg, "schemaNamingContext",
443 : "schema_data", backend_modules);
444 108013 : CHECK_LDB_RET(ret);
445 :
446 108013 : ret = prepare_modules_line(ldb, tmp_ctx,
447 : rootdse_msg,
448 : partition_msg, NULL,
449 : NULL, backend_modules);
450 108013 : CHECK_LDB_RET(ret);
451 :
452 108013 : ret = ldb_set_opaque(ldb, DSDB_OPAQUE_PARTITION_MODULE_MSG_OPAQUE_NAME, partition_msg);
453 108013 : CHECK_LDB_RET(ret);
454 :
455 108013 : talloc_steal(ldb, partition_msg);
456 :
457 : /* Now prepare the module chain. Oddly, we must give it to
458 : * ldb_module_load_list in REVERSE */
459 1135647 : for (len = 0; final_module_list[len]; len++) { /* noop */};
460 :
461 108013 : reverse_module_list = talloc_array(tmp_ctx, const char *, len+1);
462 108013 : if (!reverse_module_list) {
463 0 : talloc_free(tmp_ctx);
464 0 : return ldb_oom(ldb);
465 : }
466 4212507 : for (i=0; i < len; i++) {
467 4104494 : reverse_module_list[i] = final_module_list[(len - 1) - i];
468 : }
469 108013 : reverse_module_list[i] = NULL;
470 :
471 : /* The backend (at least until the partitions module
472 : * reconfigures things) is the next module in the currently
473 : * loaded chain */
474 108013 : backend_module = ldb_module_next(module);
475 108013 : ret = ldb_module_load_list(ldb, reverse_module_list, backend_module, &module_chain);
476 108013 : CHECK_LDB_RET(ret);
477 :
478 108013 : talloc_free(tmp_ctx);
479 : /* Set this as the 'next' module, so that we effectively append it to
480 : * module chain */
481 108013 : ldb_module_set_next(module, module_chain);
482 :
483 108013 : ret = ldb_next_read_lock(module);
484 108013 : if (ret != LDB_SUCCESS) {
485 0 : return ret;
486 : }
487 :
488 108013 : ret = ldb_next_init(module);
489 :
490 108013 : lock_ret = ldb_next_read_unlock(module);
491 :
492 108013 : if (lock_ret != LDB_SUCCESS) {
493 0 : return lock_ret;
494 : }
495 :
496 108013 : return ret;
497 : }
498 :
499 : static const struct ldb_module_ops ldb_samba_dsdb_module_ops = {
500 : .name = "samba_dsdb",
501 : .init_context = samba_dsdb_init,
502 : };
503 :
504 0 : static struct ldb_message *dsdb_flags_ignore_fixup(TALLOC_CTX *mem_ctx,
505 : const struct ldb_message *_msg)
506 : {
507 0 : struct ldb_message *msg = NULL;
508 : unsigned int i;
509 :
510 : /* we have to copy the message as the caller might have it as a const */
511 0 : msg = ldb_msg_copy_shallow(mem_ctx, _msg);
512 0 : if (msg == NULL) {
513 0 : return NULL;
514 : }
515 :
516 0 : for (i=0; i < msg->num_elements;) {
517 0 : struct ldb_message_element *e = &msg->elements[i];
518 :
519 0 : if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
520 0 : i++;
521 0 : continue;
522 : }
523 :
524 0 : e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
525 :
526 0 : if (e->num_values != 0) {
527 0 : i++;
528 0 : continue;
529 : }
530 :
531 0 : ldb_msg_remove_element(msg, e);
532 : }
533 :
534 0 : return msg;
535 : }
536 :
537 0 : static int dsdb_flags_ignore_add(struct ldb_module *module, struct ldb_request *req)
538 : {
539 0 : struct ldb_context *ldb = ldb_module_get_ctx(module);
540 0 : struct ldb_request *down_req = NULL;
541 0 : struct ldb_message *msg = NULL;
542 : int ret;
543 :
544 0 : msg = dsdb_flags_ignore_fixup(req, req->op.add.message);
545 0 : if (msg == NULL) {
546 0 : return ldb_module_oom(module);
547 : }
548 :
549 0 : ret = ldb_build_add_req(&down_req, ldb, req,
550 : msg,
551 : req->controls,
552 : req, dsdb_next_callback,
553 : req);
554 0 : LDB_REQ_SET_LOCATION(down_req);
555 0 : if (ret != LDB_SUCCESS) {
556 0 : return ret;
557 : }
558 :
559 : /* go on with the call chain */
560 0 : return ldb_next_request(module, down_req);
561 : }
562 :
563 0 : static int dsdb_flags_ignore_modify(struct ldb_module *module, struct ldb_request *req)
564 : {
565 0 : struct ldb_context *ldb = ldb_module_get_ctx(module);
566 0 : struct ldb_request *down_req = NULL;
567 0 : struct ldb_message *msg = NULL;
568 : int ret;
569 :
570 0 : msg = dsdb_flags_ignore_fixup(req, req->op.mod.message);
571 0 : if (msg == NULL) {
572 0 : return ldb_module_oom(module);
573 : }
574 :
575 0 : ret = ldb_build_mod_req(&down_req, ldb, req,
576 : msg,
577 : req->controls,
578 : req, dsdb_next_callback,
579 : req);
580 0 : LDB_REQ_SET_LOCATION(down_req);
581 0 : if (ret != LDB_SUCCESS) {
582 0 : return ret;
583 : }
584 :
585 : /* go on with the call chain */
586 0 : return ldb_next_request(module, down_req);
587 : }
588 :
589 : static const struct ldb_module_ops ldb_dsdb_flags_ignore_module_ops = {
590 : .name = "dsdb_flags_ignore",
591 : .add = dsdb_flags_ignore_add,
592 : .modify = dsdb_flags_ignore_modify,
593 : };
594 :
595 4310 : int ldb_samba_dsdb_module_init(const char *version)
596 : {
597 : int ret;
598 4310 : LDB_MODULE_CHECK_VERSION(version);
599 4310 : ret = ldb_register_module(&ldb_samba_dsdb_module_ops);
600 4310 : if (ret != LDB_SUCCESS) {
601 0 : return ret;
602 : }
603 4310 : ret = ldb_register_module(&ldb_dsdb_flags_ignore_module_ops);
604 4310 : if (ret != LDB_SUCCESS) {
605 0 : return ret;
606 : }
607 4310 : return LDB_SUCCESS;
608 : }
|