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