1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * kadmin/ldap_util/kdb5_ldap_util.c 10 * 11 * (C) Copyright 1990,1991, 1996 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * 34 * Edit a KDC database. 35 */ 36 37 /* 38 * Copyright (C) 1998 by the FundsXpress, INC. 39 * 40 * All rights reserved. 41 * 42 * Export of this software from the United States of America may require 43 * a specific license from the United States Government. It is the 44 * responsibility of any person or organization contemplating export to 45 * obtain such a license before exporting. 46 * 47 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 48 * distribute this software and its documentation for any purpose and 49 * without fee is hereby granted, provided that the above copyright 50 * notice appear in all copies and that both that copyright notice and 51 * this permission notice appear in supporting documentation, and that 52 * the name of FundsXpress. not be used in advertising or publicity pertaining 53 * to distribution of the software without specific, written prior 54 * permission. FundsXpress makes no representations about the suitability of 55 * this software for any purpose. It is provided "as is" without express 56 * or implied warranty. 57 * 58 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 59 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 60 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 61 */ 62 63 /* Copyright (c) 2004-2005, Novell, Inc. 64 * All rights reserved. 65 * 66 * Redistribution and use in source and binary forms, with or without 67 * modification, are permitted provided that the following conditions are met: 68 * 69 * * Redistributions of source code must retain the above copyright notice, 70 * this list of conditions and the following disclaimer. 71 * * Redistributions in binary form must reproduce the above copyright 72 * notice, this list of conditions and the following disclaimer in the 73 * documentation and/or other materials provided with the distribution. 74 * * The copyright holder's name is not used to endorse or promote products 75 * derived from this software without specific prior written permission. 76 * 77 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 78 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 80 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 81 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 82 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 83 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 84 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 85 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 86 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 87 * POSSIBILITY OF SUCH DAMAGE. 88 */ 89 90 #include <stdio.h> 91 #include <time.h> 92 93 #include <k5-int.h> 94 #include <kadm5/admin.h> 95 #include <adm_proto.h> 96 #include <libintl.h> 97 #include <locale.h> 98 #include "kdb5_ldap_util.h" 99 100 typedef void (*cmd_func)(int, char **); 101 int cmd_index(char *name); 102 103 char *mkey_password = 0; 104 int exit_status = 0; 105 krb5_context util_context; 106 kadm5_config_params global_params; 107 krb5_boolean db_inited = FALSE; 108 109 char *progname; 110 krb5_boolean manual_mkey = FALSE; 111 112 /* 113 * This function prints the usage of kdb5_ldap_util, which is 114 * the LDAP configuration utility. 115 */ 116 void usage() 117 { 118 fprintf(stderr, "%s: " 119 "kdb5_ldap_util [-D user_dn [-w passwd]] [-H ldapuri]\n" 120 "\tcmd [cmd_options]\n" 121 122 /* Create realm */ 123 "create [-subtrees subtree_dn_list] [-sscope search_scope] [-containerref container_reference_dn]\n" 124 #ifdef HAVE_EDIRECTORY 125 "\t\t[-kdcdn kdc_service_list] [-admindn admin_service_list]\n" 126 "\t\t[-pwddn passwd_service_list]\n" 127 #endif 128 "\t\t[-m|-P password|-sf stashfilename] [-k mkeytype] [-s]\n" 129 "\t\t[-maxtktlife max_ticket_life] [-maxrenewlife max_renewable_ticket_life]\n" 130 "\t\t[ticket_flags] [-r realm]\n" 131 132 /* modify realm */ 133 "modify [-subtrees subtree_dn_list] [-sscope search_scope] [-containerref container_reference_dn]\n" 134 #ifdef HAVE_EDIRECTORY 135 "\t\t[-kdcdn kdc_service_list |\n" 136 "\t\t[-clearkdcdn kdc_service_list] [-addkdcdn kdc_service_list]]\n" 137 "\t\t[-admindn admin_service_list | [-clearadmindn admin_service_list]\n" 138 "\t\t[-addadmindn admin_service_list]] [-pwddn passwd_service_list |\n" 139 "\t\t[-clearpwddn passwd_service_list] [-addpwddn passwd_service_list]]\n" 140 #endif 141 "\t\t[-maxtktlife max_ticket_life] [-maxrenewlife max_renewable_ticket_life]\n" 142 "\t\t[ticket_flags] [-r realm]\n" 143 /* View realm */ 144 "view [-r realm]\n" 145 146 /* Destroy realm */ 147 "destroy [-f] [-r realm]\n" 148 149 /* List realms */ 150 "list\n" 151 152 #ifdef HAVE_EDIRECTORY 153 /* Create Service */ 154 "create_service {-kdc|-admin|-pwd} [-servicehost service_host_list]\n" 155 "\t\t[-realm realm_list] \n" 156 "\t\t[-randpw|-fileonly] [-f filename] service_dn\n" 157 158 /* Modify service */ 159 "modify_service [-servicehost service_host_list |\n" 160 "\t\t[-clearservicehost service_host_list]\n" 161 "\t\t[-addservicehost service_host_list]]\n" 162 "\t\t[-realm realm_list | [-clearrealm realm_list]\n" 163 "\t\t[-addrealm realm_list]] service_dn\n" 164 165 /* View Service */ 166 "view_service service_dn\n" 167 168 /* Destroy Service */ 169 "destroy_service [-force] [-f stashfilename] service_dn\n" 170 171 /* List services */ 172 "list_service [-basedn base_dn]\n" 173 174 /* Set Service password */ 175 "setsrvpw [-randpw|-fileonly] [-f filename] service_dn\n" 176 177 #else 178 179 /* Stash the service password */ 180 "stashsrvpw [-f filename] service_dn\n" 181 182 #endif 183 184 /* Create policy */ 185 "create_policy [-r realm] [-maxtktlife max_ticket_life]\n" 186 "\t\t[-maxrenewlife max_renewable_ticket_life] [ticket_flags] policy\n" 187 188 /* Modify policy */ 189 "modify_policy [-r realm] [-maxtktlife max_ticket_life]\n" 190 "\t\t[-maxrenewlife max_renewable_ticket_life] [ticket_flags] policy\n" 191 192 /* View policy */ 193 "view_policy [-r realm] policy\n" 194 195 /* Destroy policy */ 196 "destroy_policy [-r realm] [-force] policy\n" 197 198 /* List policies */ 199 "list_policy [-r realm]\n", 200 gettext("Usage")); 201 } 202 203 void db_usage (int type) { 204 /* 205 * This should print usage of 'type' command. For now, we will print usage 206 * of all commands. 207 */ 208 usage (); 209 } 210 211 /* The help messages for all sub-commands should be in the 212 * same order as listed in this table. 213 */ 214 static struct _cmd_table { 215 char *name; 216 cmd_func func; 217 int opendb; 218 } cmd_table[] = { 219 {"create", kdb5_ldap_create, 1}, 220 {"modify", kdb5_ldap_modify, 1}, 221 {"view", kdb5_ldap_view, 1}, 222 {"destroy", kdb5_ldap_destroy, 1}, 223 {"list", kdb5_ldap_list, 1}, 224 #ifdef HAVE_EDIRECTORY 225 {"create_service", kdb5_ldap_create_service, 1}, 226 {"modify_service", kdb5_ldap_modify_service, 1}, 227 {"view_service", kdb5_ldap_view_service, 1}, 228 {"destroy_service", kdb5_ldap_destroy_service, 1}, 229 {"list_service",kdb5_ldap_list_services,1}, 230 {"setsrvpw", kdb5_ldap_set_service_password, 0}, 231 #else 232 {"stashsrvpw", kdb5_ldap_stash_service_password, 0}, 233 #endif 234 {"create_policy", kdb5_ldap_create_policy, 1}, 235 {"modify_policy", kdb5_ldap_modify_policy, 1}, 236 {"view_policy", kdb5_ldap_view_policy, 1}, 237 {"destroy_policy", kdb5_ldap_destroy_policy, 1}, 238 {"list_policy", kdb5_ldap_list_policies, 1}, 239 {NULL, NULL, 0}, 240 }; 241 242 243 /* 244 * The function cmd_lookup returns the structure matching the 245 * command name and returns NULL if nothing matches. 246 */ 247 static struct _cmd_table *cmd_lookup(name) 248 char *name; 249 { 250 int i; 251 252 for (i = 0; cmd_table[i].name != NULL; i++) 253 if (strcmp(cmd_table[i].name, name) == 0) 254 return &cmd_table[i]; 255 256 return NULL; 257 } 258 259 260 /* 261 * The function cmd_index provides the offset of the command 262 * in the command table, which can be used to get the corresponding 263 * help from the help message table. 264 */ 265 int cmd_index(name) 266 char *name; 267 { 268 int i; 269 270 if (name == NULL) 271 return -1; 272 273 for (i = 0; cmd_table[i].name != NULL; i++) 274 if (strcmp(cmd_table[i].name, name) == 0) 275 return i; 276 277 return -1; 278 } 279 280 static void extended_com_err_fn (const char *myprog, errcode_t code, 281 const char *fmt, va_list args) 282 { 283 const char *emsg; 284 /* Solaris Kerberos: code should be like that in kdb5_util.c */ 285 if (code) { 286 emsg = krb5_get_error_message (util_context, code); 287 fprintf (stderr, "%s: %s ", myprog, emsg); 288 krb5_free_error_message (util_context, emsg); 289 } else { 290 fprintf (stderr, "%s: ", myprog); 291 } 292 vfprintf (stderr, fmt, args); 293 fprintf (stderr, "\n"); 294 } 295 296 int main(argc, argv) 297 int argc; 298 char *argv[]; 299 { 300 struct _cmd_table *cmd = NULL; 301 char *koptarg = NULL, **cmd_argv = NULL; 302 int cmd_argc = 0; 303 krb5_error_code retval; 304 int usage_print = 0; 305 int gp_is_static = 1; 306 krb5_error_code db_retval = 1; 307 char *bind_dn = NULL; 308 char *passwd = NULL; 309 char *ldap_server = NULL; 310 unsigned int ldapmask = 0; 311 unsigned int passwd_len = 0; 312 char *prompt = NULL; 313 kdb5_dal_handle *dal_handle = NULL; 314 krb5_ldap_context *ldap_context=NULL; 315 char *value = NULL, *conf_section = NULL; 316 krb5_boolean realm_name_required = TRUE; 317 krb5_boolean print_help_message = FALSE; 318 319 retval = krb5_init_context(&util_context); 320 set_com_err_hook(extended_com_err_fn); 321 if (retval) { 322 com_err (progname, retval, gettext("while initializing Kerberos code")); 323 exit_status++; 324 goto cleanup; 325 } 326 327 progname = (strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]); 328 329 cmd_argv = (char **) malloc(sizeof(char *)*argc); 330 if (cmd_argv == NULL) { 331 com_err(progname, ENOMEM, gettext("while creating sub-command arguments")); 332 exit_status++; 333 goto cleanup; 334 } 335 memset(cmd_argv, 0, sizeof(char *)*argc); 336 cmd_argc = 1; 337 338 memset(&global_params, 0, sizeof(kadm5_config_params)); 339 340 argv++; argc--; 341 while (*argv) { 342 if (strcmp(*argv, "--help") == 0) { 343 print_help_message = TRUE; 344 } 345 if (strcmp(*argv, "-P") == 0 && ARG_VAL) { 346 mkey_password = koptarg; 347 manual_mkey = TRUE; 348 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { 349 global_params.realm = koptarg; 350 global_params.mask |= KADM5_CONFIG_REALM; 351 /* not sure this is really necessary */ 352 if ((retval = krb5_set_default_realm(util_context, 353 global_params.realm))) { 354 com_err(progname, retval, gettext("while setting default realm name")); 355 exit_status++; 356 goto cleanup; 357 } 358 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { 359 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) 360 com_err(argv[0], 0, gettext("%s is an invalid enctype"), koptarg); 361 else 362 global_params.mask |= KADM5_CONFIG_ENCTYPE; 363 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { 364 global_params.mkey_name = koptarg; 365 global_params.mask |= KADM5_CONFIG_MKEY_NAME; 366 } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) { 367 global_params.stash_file = koptarg; 368 global_params.mask |= KADM5_CONFIG_STASH_FILE; 369 } else if (strcmp(*argv, "-m") == 0) { 370 manual_mkey = TRUE; 371 global_params.mkey_from_kbd = 1; 372 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 373 } else if (strcmp(*argv, "-D") == 0 && ARG_VAL) { 374 bind_dn = koptarg; 375 if (bind_dn == NULL) { 376 com_err(progname, ENOMEM, gettext("while reading ldap parameters")); 377 exit_status++; 378 goto cleanup; 379 } 380 ldapmask |= CMD_LDAP_D; 381 } else if (strcmp(*argv, "-w") == 0 && ARG_VAL) { 382 passwd = strdup(koptarg); 383 if (passwd == NULL) { 384 com_err(progname, ENOMEM, gettext("while reading ldap parameters")); 385 exit_status++; 386 goto cleanup; 387 } 388 ldapmask |= CMD_LDAP_W; 389 } else if (strcmp(*argv, "-H") == 0 && ARG_VAL) { 390 ldap_server = koptarg; 391 if (ldap_server == NULL) { 392 com_err(progname, ENOMEM, gettext("while reading ldap parameters")); 393 exit_status++; 394 goto cleanup; 395 } 396 ldapmask |= CMD_LDAP_H; 397 } else if (cmd_lookup(*argv) != NULL) { 398 if (cmd_argv[0] == NULL) 399 cmd_argv[0] = *argv; 400 else { 401 free(cmd_argv); 402 cmd_argv = NULL; 403 usage(); 404 goto cleanup; 405 } 406 } else { 407 cmd_argv[cmd_argc++] = *argv; 408 } 409 argv++; argc--; 410 } 411 412 if (cmd_argv[0] == NULL) { 413 free(cmd_argv); 414 cmd_argv = NULL; 415 usage(); 416 goto cleanup; 417 } 418 419 /* if we need to print the help message (because of --help option) 420 * we will print the help corresponding to the sub-command. 421 */ 422 if (print_help_message) { 423 char *cmd_name = cmd_argv[0]; 424 free(cmd_argv); 425 cmd_argv = NULL; 426 usage(); 427 goto cleanup; 428 } 429 430 /* We need to check for the presence of default realm name only in 431 * the case of realm related operations like create, destroy etc. 432 */ 433 if ((strcmp(cmd_argv[0], "list") == 0) || 434 (strcmp(cmd_argv[0], "stashsrvpw") == 0)) { 435 realm_name_required = FALSE; 436 } 437 438 if (!util_context->default_realm) { 439 char *temp = NULL; 440 retval = krb5_get_default_realm(util_context, &temp); 441 if (retval) { 442 if (realm_name_required) { 443 com_err (progname, retval, gettext("while getting default realm")); 444 exit_status++; 445 goto cleanup; 446 } 447 } else 448 util_context->default_realm = temp; 449 } 450 /* If we have the realm name, we can safely say that 451 * realm_name is required so that we don't neglect any information. 452 */ 453 else 454 realm_name_required = TRUE; 455 456 retval = profile_get_string(util_context->profile, KDB_REALM_SECTION, 457 util_context->default_realm, KDB_MODULE_POINTER, 458 NULL, 459 &value); 460 461 if (!(value)) { 462 retval = profile_get_string(util_context->profile, KDB_MODULE_DEF_SECTION, 463 KDB_MODULE_POINTER, NULL, 464 NULL, 465 &value); 466 if (!(value)) { 467 if (util_context->default_realm) 468 conf_section = strdup(util_context->default_realm); 469 } else { 470 conf_section = strdup(value); 471 free(value); 472 } 473 } else { 474 conf_section = strdup(value); 475 free(value); 476 } 477 478 if (realm_name_required) { 479 /* Solaris kerberos: using older kadm5_get_config_params interface */ 480 retval = kadm5_get_config_params(util_context, NULL, NULL, 481 &global_params, &global_params); 482 if (retval) { 483 com_err(argv[0], retval, gettext("while retreiving configuration parameters")); 484 exit_status++; 485 goto cleanup; 486 } 487 gp_is_static = 0; 488 } 489 490 if ((retval = krb5_ldap_lib_init()) != 0) { 491 com_err(argv[0], retval, gettext("while initializing error handling")); 492 exit_status++; 493 goto cleanup; 494 } 495 496 /* Initialize the ldap context */ 497 ldap_context = calloc(sizeof(krb5_ldap_context), 1); 498 if (ldap_context == NULL) { 499 com_err(argv[0], ENOMEM, gettext("while initializing ldap handle")); 500 exit_status++; 501 goto cleanup; 502 } 503 504 ldap_context->kcontext = util_context; 505 506 /* If LDAP parameters are specified, replace them with the values from config */ 507 if (ldapmask & CMD_LDAP_D) { 508 /* If password is not specified, prompt for it */ 509 if (passwd == NULL) { 510 passwd = (char *)malloc(MAX_PASSWD_LEN); 511 if (passwd == NULL) { 512 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 513 exit_status++; 514 goto cleanup; 515 } 516 prompt = (char *)malloc(MAX_PASSWD_PROMPT_LEN); 517 if (prompt == NULL) { 518 free(passwd); 519 passwd = NULL; 520 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 521 exit_status++; 522 goto cleanup; 523 } 524 memset(passwd, 0, sizeof(passwd)); 525 passwd_len = MAX_PASSWD_LEN - 1; 526 snprintf(prompt, MAX_PASSWD_PROMPT_LEN, gettext("Password for \"%s\""), bind_dn); 527 528 db_retval = krb5_read_password(util_context, prompt, NULL, passwd, &passwd_len); 529 530 if ((db_retval) || (passwd_len == 0)) { 531 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 532 free(passwd); 533 passwd = NULL; 534 exit_status++; 535 goto cleanup; 536 } 537 } 538 539 ldap_context->bind_pwd = passwd; 540 } 541 542 /* If ldaphost is specified, release entry filled by configuration & use this */ 543 if (ldapmask & CMD_LDAP_H) { 544 545 ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (2, sizeof (krb5_ldap_server_info *)) ; 546 if (ldap_context->server_info_list == NULL) { 547 com_err(argv[0], ENOMEM, gettext("while initializing server list")); 548 exit_status++; 549 goto cleanup; 550 } 551 552 ldap_context->server_info_list[0] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info)); 553 if (ldap_context->server_info_list[0] == NULL) { 554 com_err(argv[0], ENOMEM, gettext("while initializing server list")); 555 exit_status++; 556 goto cleanup; 557 } 558 559 ldap_context->server_info_list[0]->server_status = NOTSET; 560 561 ldap_context->server_info_list[0]->server_name = strdup(ldap_server); 562 if (ldap_context->server_info_list[0]->server_name == NULL) { 563 com_err(argv[0], ENOMEM, gettext("while initializing server list")); 564 exit_status++; 565 goto cleanup; 566 } 567 } 568 if (bind_dn) { 569 ldap_context->bind_dn = strdup(bind_dn); 570 if (ldap_context->bind_dn == NULL) { 571 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 572 exit_status++; 573 goto cleanup; 574 } 575 } else 576 ldap_context->bind_dn = NULL; 577 578 ldap_context->service_type = SERVICE_DN_TYPE_CLIENT; 579 580 if (realm_name_required) { 581 if ((global_params.enctype != ENCTYPE_UNKNOWN) && 582 (!krb5_c_valid_enctype(global_params.enctype))) { 583 com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, 584 gettext("while setting up enctype %d"), global_params.enctype); 585 } 586 } 587 588 cmd = cmd_lookup(cmd_argv[0]); 589 590 /* Setup DAL handle to access the database */ 591 dal_handle = calloc((size_t)1, sizeof(kdb5_dal_handle)); 592 if (dal_handle == NULL) { 593 goto cleanup; 594 } 595 dal_handle->db_context = ldap_context; 596 util_context->db_context = (void *) dal_handle; 597 598 db_retval = krb5_ldap_read_server_params(util_context, conf_section, KRB5_KDB_SRV_TYPE_OTHER); 599 if (db_retval) { 600 com_err(argv[0], db_retval, gettext("while reading ldap configuration")); 601 exit_status++; 602 goto cleanup; 603 } 604 605 if (cmd->opendb) { 606 db_retval = krb5_ldap_db_init(util_context, ldap_context); 607 if (db_retval) { 608 com_err(progname, db_retval, gettext("while initializing database")); 609 exit_status++; 610 goto cleanup; 611 } 612 db_inited = TRUE; 613 } 614 (*cmd->func)(cmd_argc, cmd_argv); 615 616 goto cleanup; 617 618 cleanup: 619 if (passwd) 620 memset(passwd, 0, sizeof(passwd)); 621 if (ldap_context && ldap_context->bind_pwd) 622 memset(ldap_context->bind_pwd, 0, sizeof(ldap_context->bind_pwd)); 623 624 if (util_context) { 625 if (gp_is_static == 0) 626 kadm5_free_config_params(util_context, &global_params); 627 krb5_ldap_close(util_context); 628 krb5_free_context(util_context); 629 } 630 631 if (cmd_argv) 632 free(cmd_argv); 633 if (prompt) 634 free(prompt); 635 if (conf_section) 636 free(conf_section); 637 if (dal_handle) 638 free(dal_handle); 639 640 if (usage_print) { 641 usage(); 642 } 643 644 return exit_status; 645 } 646