Use new xreadlink interface.
[shishi.git] / src / shisa.c
blob4bb76040072c4ea5a7cfb86d2314f8f45464192e
1 /* shisa.c --- Command line interface to Shishi database.
2 * Copyright (C) 2003, 2004, 2006 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi 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 2 of the License, or
9 * (at your option) any later version.
11 * Shishi 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.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <ctype.h>
32 /* Setup i18n. */
33 #ifdef HAVE_LOCALE_H
34 # include <locale.h>
35 #else
36 # define setlocale(Category, Locale) /* empty */
37 #endif
38 #include <gettext.h>
39 #define _(String) gettext (String)
40 #define gettext_noop(String) String
41 #define N_(String) gettext_noop (String)
43 /* Get asprintf. */
44 #include "xvasprintf.h"
46 /* Get xgethostname. */
47 #include "xgethostname.h"
49 /* Get set_program_name and program_name. */
50 #include "progname.h"
52 /* Get error. */
53 #include "error.h"
55 /* Shishi and Shisa library. */
56 #include <shisa.h>
57 #include <shishi.h>
59 /* Command line parameter parser via gengetopt. */
60 #include "shisa_cmd.h"
62 /* Global variables. */
63 Shishi *sh;
64 Shisa *dbh;
65 struct gengetopt_args_info args;
67 static void
68 printfield (const char *fieldname, const char *value)
70 printf ("\t\t%s %s.\n", fieldname, value);
73 static void
74 printtimefield (const char *fieldname, time_t t)
76 char *p = ctime (&t);
77 p[strlen (p) - 1] = '\0';
78 printfield (fieldname, t == (time_t) - 1 ? "N/A" : p);
81 static void
82 printintfield (const char *fieldname, int num)
84 char *p = xasprintf ("%d (0x%x)", num, num);
85 printfield (fieldname, p);
86 free (p);
89 static void
90 printuint32field (const char *fieldname, uint32_t num)
92 char *p = xasprintf ("%d (0x%x)", num, num);
93 printfield (fieldname, p);
94 free (p);
97 static void
98 print3field (const char *fieldname, const char *text, uint32_t num)
100 char *p = xasprintf ("%s (0x%x, %d)", text, num, num);
101 printfield (fieldname, p);
102 free (p);
105 static void
106 printdbkey (const char *realm, const char *principal, Shisa_key * dbkey)
108 Shishi_key *key;
109 int rc;
111 rc = shishi_key_from_value (sh, dbkey->etype, dbkey->key, &key);
112 if (rc == SHISHI_OK)
114 shishi_key_realm_set (key, realm);
115 shishi_key_principal_set (key, principal);
116 shishi_key_print (sh, stdout, key);
118 else
119 error (0, 0, "shishi_key_from_value (%d):\n%s",
120 rc, shishi_strerror (rc));
123 static int
124 dumplist_realm_principal (const char *realm, const char *principal)
126 Shisa_principal ph;
127 int rc;
129 if (args.dump_given || args.enabled_flag || args.disabled_flag)
131 rc = shisa_principal_find (dbh, realm, principal, &ph);
132 if (rc != SHISA_OK)
134 error (0, 0, "shishi_principal_find (%d):\n%s",
135 rc, shisa_strerror (rc));
136 return rc;
139 if (args.enabled_flag && ph.isdisabled)
140 return SHISA_OK;
142 if (args.disabled_flag && !ph.isdisabled)
143 return SHISA_OK;
146 printf ("\t%s\n", principal);
148 if (args.dump_given)
150 Shisa_key **keys;
151 size_t nkeys;
152 size_t i;
154 printfield (_("Account is"),
155 ph.isdisabled ? _("DISABLED") : _("enabled"));
156 printuint32field (_("Current key version"), ph.kvno);
157 if (ph.notusedbefore != (time_t) - 1)
158 printtimefield (_("Account not valid before"), ph.notusedbefore);
159 if (ph.lastinitialtgt != (time_t) - 1)
160 printtimefield (_("Last initial TGT request at"), ph.lastinitialtgt);
161 if (ph.lastinitialrequest != (time_t) - 1)
162 printtimefield (_("Last initial request at"), ph.lastinitialrequest);
163 if (ph.lasttgt != (time_t) - 1)
164 printtimefield (_("Last TGT request at"), ph.lasttgt);
165 if (ph.lastrenewal != (time_t) - 1)
166 printtimefield (_("Last ticket renewal at"), ph.lastrenewal);
167 if (ph.passwordexpire != (time_t) - 1)
168 printtimefield (_("Password expire on"), ph.passwordexpire);
169 if (ph.accountexpire != (time_t) - 1)
170 printtimefield (_("Account expire on"), ph.accountexpire);
172 rc = shisa_keys_find (dbh, realm, principal, NULL, &keys, &nkeys);
173 if (rc != SHISA_OK)
175 error (0, 0, "shishi_keys_find(%s, %s) (%d):\n%s",
176 realm, principal, rc, shisa_strerror (rc));
177 return rc;
180 for (i = 0; i < nkeys; i++)
181 if (keys[i])
183 printintfield (_("Key"), i);
185 print3field (_("\tEtype"), shishi_cipher_name (keys[i]->etype),
186 keys[i]->etype);
187 if (keys[i]->priority > 0)
188 printintfield (_("\tPriority"), keys[i]->priority);
189 if (args.keys_given)
190 printdbkey (realm, principal, keys[i]);
191 if (keys[i]->saltlen > 0)
192 printfield (_("\tSalt"), keys[i]->salt);
193 if (keys[i]->str2keyparamlen > 0)
194 printfield (_("\tS2K params"), keys[i]->str2keyparam);
195 if (args.keys_given)
196 if (keys[i]->password)
197 printfield (_("\tPassword"), keys[i]->password);
199 else
200 printfield (_("\tKey is"), _("MISSING"));
202 shisa_keys_free (dbh, keys, nkeys);
205 return SHISA_OK;
208 static int
209 dumplist_realm (const char *realm)
211 char **principals;
212 size_t nprincipals;
213 size_t i;
214 int rc;
216 printf ("%s\n", realm);
218 rc = shisa_enumerate_principals (dbh, realm, &principals, &nprincipals);
219 if (rc != SHISA_OK)
220 return rc;
222 for (i = 0; i < nprincipals; i++)
224 if (rc == SHISA_OK)
225 rc = dumplist_realm_principal (realm, principals[i]);
226 free (principals[i]);
228 if (nprincipals > 0)
229 free (principals);
231 return rc;
234 static int
235 dumplist (void)
237 int rc;
239 if (args.inputs_num == 1)
240 rc = dumplist_realm (args.inputs[0]);
241 else if (args.inputs_num == 2)
243 char *realm = args.inputs[0];
244 char *principal = args.inputs[1];
245 printf ("%s\n", realm);
246 rc = dumplist_realm_principal (realm, principal);
248 else
250 char **realms;
251 size_t nrealms;
252 size_t i;
254 rc = shisa_enumerate_realms (dbh, &realms, &nrealms);
255 if (rc != SHISA_OK)
256 return rc;
258 for (i = 0; i < nrealms; i++)
260 if (rc == SHISA_OK)
261 rc = dumplist_realm (realms[i]);
262 free (realms[i]);
264 if (nrealms > 0)
265 free (realms);
268 return rc;
271 static void
272 add (const char *realm, const char *principal,
273 Shisa_principal * ph, Shisa_key * key)
275 int rc;
277 if (principal == NULL)
278 printf (_("Adding realm `%s'...\n"), realm);
279 else
280 printf (_("Adding principal `%s@%s'...\n"), principal, realm);
282 rc = shisa_principal_add (dbh, realm, principal, ph, key);
283 if (rc != SHISA_OK)
284 error (EXIT_FAILURE, 0, "shisa_principal_add (%d):\n%s",
285 rc, shisa_strerror (rc));
287 if (args.keys_given)
288 printdbkey (realm, principal, key);
290 if (principal == NULL)
291 printf (_("Adding realm `%s'...done\n"), realm);
292 else
293 printf (_("Adding principal `%s@%s'...done\n"), principal, realm);
296 static void
297 delete (const char *realm, const char *principal)
299 int rc;
301 if (principal == NULL && args.force_flag)
303 char **principals;
304 size_t nprincipals;
305 size_t i;
307 rc = shisa_enumerate_principals (dbh, realm, &principals, &nprincipals);
308 if (rc != SHISA_OK)
309 error (EXIT_FAILURE, 0, "shisa_enumerate_principals (%d):\n%s",
310 rc, shisa_strerror (rc));
312 for (i = 0; i < nprincipals; i++)
313 if (principals[i])
315 delete (realm, principals[i]);
316 free (principals[i]);
319 if (nprincipals > 0)
320 free (principals);
323 if (principal == NULL)
324 printf (_("Removing realm `%s'...\n"), realm);
325 else
326 printf (_("Removing principal `%s@%s'...\n"), principal, realm);
328 rc = shisa_principal_remove (dbh, realm, principal);
329 if (rc != SHISA_OK)
330 error (EXIT_FAILURE, 0, "shisa_principal_remove (%d):\n%s",
331 rc, shisa_strerror (rc));
333 if (principal == NULL)
334 printf (_("Removing realm `%s'...done\n"), realm);
335 else
336 printf (_("Removing principal `%s@%s'...done\n"), principal, realm);
339 static void
340 apply_options (const char *realm,
341 const char *principal, Shisa_principal * ph, Shisa_key * dbkey)
343 char *passwd = args.password_arg;
344 char *salt = args.salt_arg;
345 char *str2keyparam = NULL;
346 size_t str2keyparamlen = 0;
347 Shishi_key *key;
348 int32_t etype;
349 int rc;
351 if (ph)
353 if (args.key_version_given)
354 ph->kvno = args.key_version_arg;
357 if (dbkey)
359 etype = shishi_cfg_clientkdcetype_fast (sh);
361 if (!salt && realm && principal)
363 char *name = xasprintf ("%s@%s", principal, realm);
365 rc = shishi_derive_default_salt (sh, name, &salt);
366 free (name);
367 if (rc != SHISHI_OK)
368 error (EXIT_FAILURE, 0, "shisa_derive_default_salt (%d):\n%s",
369 rc, shisa_strerror (rc));
372 if (args.string_to_key_parameter_given)
374 /* XXX */
377 if (args.password_given)
379 if (!passwd)
381 if (realm && principal)
382 rc = shishi_prompt_password (sh, &passwd,
383 _("Password for `%s@%s': "),
384 principal, realm);
385 else
386 rc = shishi_prompt_password (sh, &passwd, _("Password: "));
387 if (rc != SHISHI_OK)
388 error (EXIT_FAILURE, 0, _("Could not read password"));
391 rc = shishi_key_from_string (sh, etype,
392 passwd, strlen (passwd),
393 salt, salt ? strlen (salt) : 0,
394 str2keyparam, &key);
396 else
397 rc = shishi_key_random (sh, etype, &key);
399 if (rc != SHISHI_OK)
400 error (EXIT_FAILURE, 0, _("Could not create key (%d):\n%s"),
401 rc, shishi_strerror (rc));
403 if (realm && principal)
405 shishi_key_realm_set (key, realm);
406 shishi_key_principal_set (key, principal);
409 dbkey->kvno = args.key_version_arg;
410 dbkey->etype = etype;
411 dbkey->priority = args.priority_arg;
412 dbkey->key = shishi_key_value (key);
413 dbkey->keylen = shishi_key_length (key);
414 dbkey->salt = salt;
415 dbkey->saltlen = salt ? strlen (salt) : 0;
416 dbkey->str2keyparam = str2keyparam;
417 dbkey->str2keyparamlen = str2keyparamlen;
418 dbkey->password = passwd;
423 main (int argc, char *argv[])
425 const char *realm = NULL;
426 const char *principal = NULL;
427 Shisa_principal ph;
428 Shisa_key key;
429 int rc;
431 setlocale (LC_ALL, "");
432 bindtextdomain (PACKAGE, LOCALEDIR);
433 textdomain (PACKAGE);
434 set_program_name (argv[0]);
436 if (cmdline_parser (argc, argv, &args) != 0)
437 error (EXIT_FAILURE, 0, _("Try `%s --help' for more information."),
438 program_name);
440 rc = args.add_given + args.dump_given + args.list_given +
441 args.modify_given + args.remove_given +
442 args.key_add_given + args.key_remove_given;
444 if (rc > 1 || args.inputs_num > 2)
446 error (0, 0, _("too many arguments"));
447 error (EXIT_FAILURE, 0, _("Try `%s --help' for more information."),
448 program_name);
451 if (rc == 0 || args.help_given)
453 cmdline_parser_print_help ();
454 printf (_("\nMandatory arguments to long options are "
455 "mandatory for short options too.\n\n"));
456 printf (_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
457 return EXIT_SUCCESS;
460 rc = shisa_init_with_paths (&dbh, args.configuration_file_arg);
461 if (rc != SHISA_OK)
462 error (EXIT_FAILURE, 0, _("Initialization failed:\n%s"),
463 shisa_strerror (rc));
465 rc = shisa_cfg (dbh, args.library_options_arg);
466 if (rc != SHISA_OK)
467 error (EXIT_FAILURE, 0, _("Could not read library options `%s':\n%s"),
468 args.library_options_arg, shisa_strerror (rc));
470 rc = shishi_init (&sh);
471 if (rc != SHISHI_OK)
472 error (EXIT_FAILURE, 0, _("Shishi initialization failed:\n%s"),
473 shishi_strerror (rc));
475 rc = shishi_cfg_clientkdcetype_set (sh, args.encryption_type_arg);
476 if (rc != SHISHI_OK)
477 error (EXIT_FAILURE, 0, _("Could not set encryption type `%s':\n%s"),
478 args.encryption_type_arg, shishi_strerror (rc));
480 if ((args.inputs_num < 2 && (args.modify_given ||
481 args.key_add_given ||
482 args.key_remove_given)) ||
483 (args.inputs_num < 1 && (args.remove_given)))
485 error (0, 0, _("too few arguments"));
486 error (0, 0, _("Try `%s --help' for more information."), program_name);
487 return EXIT_FAILURE;
490 if (args.inputs_num > 0)
491 realm = args.inputs[0];
492 if (args.inputs_num > 1)
493 principal = args.inputs[1];
495 memset (&ph, 0, sizeof (ph));
496 memset (&key, 0, sizeof (key));
497 apply_options (realm, principal, &ph, &key);
499 if (args.list_given || args.dump_given)
500 rc = dumplist ();
501 else if (args.remove_given)
502 delete (realm, principal);
503 else if (args.add_given && (args.inputs_num == 1 || args.inputs_num == 2))
504 add (realm, principal, &ph, &key);
505 else if (args.add_given)
507 char *host;
508 char *tmp;
509 Shisa_key key2;
511 /* This is mostly meant for 'make install', as it set up the
512 default realm, and write a host key to stdout, which can be
513 redirected into $prefix/etc/shishi/shishi.keys. */
515 realm = shishi_realm_default (sh);
517 printf (_("Adding default realm `%s'...\n"), realm);
518 add (realm, NULL, NULL, NULL);
520 tmp = xasprintf ("krbtgt/%s", realm);
521 add (realm, tmp, &ph, &key);
522 free (tmp);
524 host = xgethostname ();
525 tmp = xasprintf ("host/%s", host);
526 free (host);
528 memset (&key2, 0, sizeof (key2));
529 apply_options (realm, tmp, NULL, &key2);
530 args.keys_given = 1;
532 add (realm, tmp, &ph, &key2);
533 free (tmp);
535 else if (args.modify_given)
537 printf (_("Modifying principal `%s@%s'...\n"), principal, realm);
539 rc = shisa_principal_update (dbh, realm, principal, &ph);
540 if (rc != SHISA_OK)
541 error (EXIT_FAILURE, 0, "shisa_principal_update (%d):\n%s",
542 rc, shisa_strerror (rc));
544 printf (_("Modifying principal `%s@%s'...done\n"), principal, realm);
546 else if (args.key_add_given)
548 printf (_("Adding key to `%s@%s'...\n"), principal, realm);
550 rc = shisa_key_add (dbh, realm, principal, &key);
551 if (rc != SHISA_OK)
552 error (EXIT_FAILURE, 0, "shisa_key_add (%d):\n%s",
553 rc, shisa_strerror (rc));
555 if (args.keys_given)
556 printdbkey (realm, principal, &key);
558 printf (_("Adding key to `%s@%s'...done\n"), principal, realm);
560 else if (args.key_remove_given)
562 printf (_("Removing key from `%s@%s'...\n"), principal, realm);
564 if (!args.password_given)
566 key.keylen = 0;
567 key.password = NULL;
570 rc = shisa_key_remove (dbh, realm, principal, &key);
571 if (rc != SHISA_OK)
572 error (EXIT_FAILURE, 0, "shisa_key_remove (%d):\n%s",
573 rc, shisa_strerror (rc));
575 printf (_("Removing key from `%s@%s'...done\n"), principal, realm);
578 shisa_done (dbh);
579 shishi_done (sh);
581 return EXIT_SUCCESS;