1 /* 2 * Copyright 2008 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 /* 320 * Solaris Kerberos: 321 * Ensure that "progname" is set before calling com_err. 322 */ 323 progname = (strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]); 324 325 retval = krb5_init_context(&util_context); 326 set_com_err_hook(extended_com_err_fn); 327 if (retval) { 328 com_err (progname, retval, gettext("while initializing Kerberos code")); 329 exit_status++; 330 goto cleanup; 331 } 332 333 cmd_argv = (char **) malloc(sizeof(char *)*argc); 334 if (cmd_argv == NULL) { 335 com_err(progname, ENOMEM, gettext("while creating sub-command arguments")); 336 exit_status++; 337 goto cleanup; 338 } 339 memset(cmd_argv, 0, sizeof(char *)*argc); 340 cmd_argc = 1; 341 342 memset(&global_params, 0, sizeof(kadm5_config_params)); 343 344 argv++; argc--; 345 while (*argv) { 346 if (strcmp(*argv, "--help") == 0) { 347 print_help_message = TRUE; 348 } 349 if (strcmp(*argv, "-P") == 0 && ARG_VAL) { 350 mkey_password = koptarg; 351 manual_mkey = TRUE; 352 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { 353 global_params.realm = koptarg; 354 global_params.mask |= KADM5_CONFIG_REALM; 355 /* not sure this is really necessary */ 356 if ((retval = krb5_set_default_realm(util_context, 357 global_params.realm))) { 358 com_err(progname, retval, gettext("while setting default realm name")); 359 exit_status++; 360 goto cleanup; 361 } 362 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { 363 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) 364 com_err(argv[0], 0, gettext("%s is an invalid enctype"), koptarg); 365 else 366 global_params.mask |= KADM5_CONFIG_ENCTYPE; 367 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { 368 global_params.mkey_name = koptarg; 369 global_params.mask |= KADM5_CONFIG_MKEY_NAME; 370 } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) { 371 global_params.stash_file = koptarg; 372 global_params.mask |= KADM5_CONFIG_STASH_FILE; 373 } else if (strcmp(*argv, "-m") == 0) { 374 manual_mkey = TRUE; 375 global_params.mkey_from_kbd = 1; 376 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 377 } else if (strcmp(*argv, "-D") == 0 && ARG_VAL) { 378 bind_dn = koptarg; 379 if (bind_dn == NULL) { 380 com_err(progname, ENOMEM, gettext("while reading ldap parameters")); 381 exit_status++; 382 goto cleanup; 383 } 384 ldapmask |= CMD_LDAP_D; 385 } else if (strcmp(*argv, "-w") == 0 && ARG_VAL) { 386 passwd = strdup(koptarg); 387 if (passwd == NULL) { 388 com_err(progname, ENOMEM, gettext("while reading ldap parameters")); 389 exit_status++; 390 goto cleanup; 391 } 392 ldapmask |= CMD_LDAP_W; 393 } else if (strcmp(*argv, "-H") == 0 && ARG_VAL) { 394 ldap_server = koptarg; 395 if (ldap_server == NULL) { 396 com_err(progname, ENOMEM, gettext("while reading ldap parameters")); 397 exit_status++; 398 goto cleanup; 399 } 400 ldapmask |= CMD_LDAP_H; 401 } else if (cmd_lookup(*argv) != NULL) { 402 if (cmd_argv[0] == NULL) 403 cmd_argv[0] = *argv; 404 else { 405 free(cmd_argv); 406 cmd_argv = NULL; 407 usage(); 408 goto cleanup; 409 } 410 } else { 411 cmd_argv[cmd_argc++] = *argv; 412 } 413 argv++; argc--; 414 } 415 416 if (cmd_argv[0] == NULL) { 417 free(cmd_argv); 418 cmd_argv = NULL; 419 usage(); 420 goto cleanup; 421 } 422 423 /* if we need to print the help message (because of --help option) 424 * we will print the help corresponding to the sub-command. 425 */ 426 if (print_help_message) { 427 char *cmd_name = cmd_argv[0]; 428 free(cmd_argv); 429 cmd_argv = NULL; 430 usage(); 431 goto cleanup; 432 } 433 434 /* We need to check for the presence of default realm name only in 435 * the case of realm related operations like create, destroy etc. 436 */ 437 if ((strcmp(cmd_argv[0], "list") == 0) || 438 (strcmp(cmd_argv[0], "stashsrvpw") == 0)) { 439 realm_name_required = FALSE; 440 } 441 442 if (!util_context->default_realm) { 443 char *temp = NULL; 444 retval = krb5_get_default_realm(util_context, &temp); 445 if (retval) { 446 if (realm_name_required) { 447 com_err (progname, retval, gettext("while getting default realm")); 448 exit_status++; 449 goto cleanup; 450 } 451 } else 452 util_context->default_realm = temp; 453 } 454 /* If we have the realm name, we can safely say that 455 * realm_name is required so that we don't neglect any information. 456 */ 457 else 458 realm_name_required = TRUE; 459 460 retval = profile_get_string(util_context->profile, KDB_REALM_SECTION, 461 util_context->default_realm, KDB_MODULE_POINTER, 462 NULL, 463 &value); 464 465 if (!(value)) { 466 retval = profile_get_string(util_context->profile, KDB_MODULE_DEF_SECTION, 467 KDB_MODULE_POINTER, NULL, 468 NULL, 469 &value); 470 if (!(value)) { 471 if (util_context->default_realm) 472 conf_section = strdup(util_context->default_realm); 473 } else { 474 conf_section = strdup(value); 475 free(value); 476 } 477 } else { 478 conf_section = strdup(value); 479 free(value); 480 } 481 482 if (realm_name_required) { 483 /* Solaris kerberos: using older kadm5_get_config_params interface */ 484 retval = kadm5_get_config_params(util_context, NULL, NULL, 485 &global_params, &global_params); 486 if (retval) { 487 com_err(argv[0], retval, gettext("while retreiving configuration parameters")); 488 exit_status++; 489 goto cleanup; 490 } 491 gp_is_static = 0; 492 } 493 494 if ((retval = krb5_ldap_lib_init()) != 0) { 495 com_err(argv[0], retval, gettext("while initializing error handling")); 496 exit_status++; 497 goto cleanup; 498 } 499 500 /* Initialize the ldap context */ 501 ldap_context = calloc(sizeof(krb5_ldap_context), 1); 502 if (ldap_context == NULL) { 503 com_err(argv[0], ENOMEM, gettext("while initializing ldap handle")); 504 exit_status++; 505 goto cleanup; 506 } 507 508 ldap_context->kcontext = util_context; 509 510 /* If LDAP parameters are specified, replace them with the values from config */ 511 if (ldapmask & CMD_LDAP_D) { 512 /* If password is not specified, prompt for it */ 513 if (passwd == NULL) { 514 passwd = (char *)malloc(MAX_PASSWD_LEN); 515 if (passwd == NULL) { 516 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 517 exit_status++; 518 goto cleanup; 519 } 520 prompt = (char *)malloc(MAX_PASSWD_PROMPT_LEN); 521 if (prompt == NULL) { 522 free(passwd); 523 passwd = NULL; 524 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 525 exit_status++; 526 goto cleanup; 527 } 528 memset(passwd, 0, sizeof(passwd)); 529 passwd_len = MAX_PASSWD_LEN - 1; 530 snprintf(prompt, MAX_PASSWD_PROMPT_LEN, gettext("Password for \"%s\""), bind_dn); 531 532 db_retval = krb5_read_password(util_context, prompt, NULL, passwd, &passwd_len); 533 534 if ((db_retval) || (passwd_len == 0)) { 535 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 536 free(passwd); 537 passwd = NULL; 538 exit_status++; 539 goto cleanup; 540 } 541 } 542 543 ldap_context->bind_pwd = passwd; 544 } 545 546 /* If ldaphost is specified, release entry filled by configuration & use this */ 547 if (ldapmask & CMD_LDAP_H) { 548 549 ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (2, sizeof (krb5_ldap_server_info *)) ; 550 if (ldap_context->server_info_list == NULL) { 551 com_err(argv[0], ENOMEM, gettext("while initializing server list")); 552 exit_status++; 553 goto cleanup; 554 } 555 556 ldap_context->server_info_list[0] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info)); 557 if (ldap_context->server_info_list[0] == NULL) { 558 com_err(argv[0], ENOMEM, gettext("while initializing server list")); 559 exit_status++; 560 goto cleanup; 561 } 562 563 ldap_context->server_info_list[0]->server_status = NOTSET; 564 565 ldap_context->server_info_list[0]->server_name = strdup(ldap_server); 566 if (ldap_context->server_info_list[0]->server_name == NULL) { 567 com_err(argv[0], ENOMEM, gettext("while initializing server list")); 568 exit_status++; 569 goto cleanup; 570 } 571 } 572 if (bind_dn) { 573 ldap_context->bind_dn = strdup(bind_dn); 574 if (ldap_context->bind_dn == NULL) { 575 com_err(argv[0], ENOMEM, gettext("while retrieving ldap configuration")); 576 exit_status++; 577 goto cleanup; 578 } 579 } else 580 ldap_context->bind_dn = NULL; 581 582 ldap_context->service_type = SERVICE_DN_TYPE_CLIENT; 583 584 if (realm_name_required) { 585 if ((global_params.enctype != ENCTYPE_UNKNOWN) && 586 (!krb5_c_valid_enctype(global_params.enctype))) { 587 com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, 588 gettext("while setting up enctype %d"), global_params.enctype); 589 } 590 } 591 592 cmd = cmd_lookup(cmd_argv[0]); 593 594 /* Setup DAL handle to access the database */ 595 dal_handle = calloc((size_t)1, sizeof(kdb5_dal_handle)); 596 if (dal_handle == NULL) { 597 goto cleanup; 598 } 599 dal_handle->db_context = ldap_context; 600 util_context->db_context = (void *) dal_handle; 601 602 db_retval = krb5_ldap_read_server_params(util_context, conf_section, KRB5_KDB_SRV_TYPE_OTHER); 603 if (db_retval) { 604 com_err(argv[0], db_retval, gettext("while reading ldap configuration")); 605 exit_status++; 606 goto cleanup; 607 } 608 609 if (cmd->opendb) { 610 db_retval = krb5_ldap_db_init(util_context, ldap_context); 611 if (db_retval) { 612 com_err(progname, db_retval, gettext("while initializing database")); 613 exit_status++; 614 goto cleanup; 615 } 616 db_inited = TRUE; 617 } 618 (*cmd->func)(cmd_argc, cmd_argv); 619 620 goto cleanup; 621 622 cleanup: 623 if (passwd) 624 memset(passwd, 0, sizeof(passwd)); 625 if (ldap_context && ldap_context->bind_pwd) 626 memset(ldap_context->bind_pwd, 0, sizeof(ldap_context->bind_pwd)); 627 628 if (util_context) { 629 if (gp_is_static == 0) 630 kadm5_free_config_params(util_context, &global_params); 631 krb5_ldap_close(util_context); 632 krb5_free_context(util_context); 633 } 634 635 if (cmd_argv) 636 free(cmd_argv); 637 if (prompt) 638 free(prompt); 639 if (conf_section) 640 free(conf_section); 641 if (dal_handle) 642 free(dal_handle); 643 644 if (usage_print) { 645 usage(); 646 } 647 648 return exit_status; 649 } 650