Fix API.
[shishi.git] / src / shishi.c
blob8eb4debe858ab0495f561e99541e729e55b7d05c
1 /* shishi.c command line interface to shishi
2 * Copyright (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #if HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #ifdef STDC_HEADERS
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <ctype.h>
31 #endif
33 #if HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
37 #ifdef HAVE_NETDB_H
38 #include <netdb.h>
39 #endif
41 #if defined HAVE_DECL_H_ERRNO && !HAVE_DECL_H_ERRNO
42 /* extern int h_errno; */
43 #endif
45 #ifdef HAVE_PWD_H
46 #include <pwd.h>
47 #endif
49 #ifdef HAVE_SYS_TYPES_H
50 #include <sys/types.h>
51 #endif
53 #ifdef HAVE_SYS_SELECT_H
54 #include <sys/select.h>
55 #endif
57 #ifdef HAVE_SYS_SOCKET_H
58 #include <sys/socket.h>
59 #endif
61 #ifdef HAVE_SYS_IOCTL_H
62 #include <sys/ioctl.h>
63 #endif
65 #ifdef HAVE_ERRNO_H
66 #include <errno.h>
67 #endif
69 #if HAVE_INTTYPES_H
70 # include <inttypes.h>
71 #else
72 # if HAVE_STDINT_H
73 # include <stdint.h>
74 # endif
75 #endif
77 #if TIME_WITH_SYS_TIME
78 # include <sys/time.h>
79 # include <time.h>
80 #else
81 # if HAVE_SYS_TIME_H
82 # include <sys/time.h>
83 # else
84 # include <time.h>
85 # endif
86 #endif
88 #if HAVE_STRING_H
89 # if !STDC_HEADERS && HAVE_MEMORY_H
90 # include <memory.h>
91 # endif
92 # include <string.h>
93 #endif
94 #if HAVE_STRINGS_H
95 # include <strings.h>
96 #endif
98 #ifdef HAVE_SIGNAL_H
99 #include <signal.h>
100 #endif
102 #ifdef HAVE_NETINET_IN_H
103 #include <netinet/in.h>
104 #endif
105 #ifdef HAVE_NETINET_IN6_H
106 #include <netinet/in6.h>
107 #endif
109 #ifdef HAVE_LOCALE_H
110 #include <locale.h>
111 #endif
113 #include "getdate.h"
114 #include "xalloc.h"
115 #include "error.h"
117 #include <argp.h>
118 #include <gettext.h>
119 #include <shishi.h>
121 #define _(String) gettext (String)
122 #define gettext_noop(String) String
123 #define N_(String) gettext_noop (String)
125 /* Long parameters only */
126 enum
128 OPTION_REQUEST = 300,
129 OPTION_SENDRECV,
130 OPTION_RESPONSE,
131 OPTION_WRITE_AP_REQUEST_FILE,
132 OPTION_WRITE_AUTHENTICATOR_FILE,
133 OPTION_WRITE_REQUEST_FILE,
134 OPTION_WRITE_RESPONSE_FILE,
135 OPTION_READ_REQUEST_FILE,
136 OPTION_READ_RESPONSE_FILE,
137 OPTION_SERVER,
138 OPTION_CLIENT,
139 OPTION_CLIENT_NAME,
140 OPTION_REALM,
141 OPTION_SERVER_NAME,
142 OPTION_TICKET_GRANTER,
143 OPTION_FORCE_AS,
144 OPTION_FORCE_TGS,
145 OPTION_CRYPTO_ENCRYPT,
146 OPTION_CRYPTO_DECRYPT,
147 OPTION_CRYPTO_KEY_VERSION,
148 OPTION_CRYPTO_KEY_USAGE,
149 OPTION_CRYPTO_KEY_VALUE,
150 OPTION_CRYPTO_READ_KEY_FILE,
151 OPTION_CRYPTO_WRITE_KEY_FILE,
152 OPTION_CRYPTO_READ_DATA_FILE,
153 OPTION_CRYPTO_WRITE_DATA_FILE,
154 OPTION_CRYPTO_RANDOM,
155 OPTION_CRYPTO_PARAMETER,
156 OPTION_CRYPTO_PASSWORD,
157 OPTION_CRYPTO_SALT,
158 OPTION_CRYPTO_STR2KEY,
159 OPTION_CRYPTO_DEBUG,
160 OPTION_CRYPTO_GENERATE_KEY,
161 OPTION_CRYPTO,
162 OPTION_VERBOSE_LIBRARY,
163 OPTION_LIST,
164 OPTION_DESTROY,
165 OPTION_RENEW,
166 OPTION_RENEWABLE,
167 OPTION_STARTTIME,
168 OPTION_ENDTIME,
169 OPTION_RENEW_TILL,
170 OPTION_CFG_SYSTEM,
171 OPTION_CFG_USER,
172 OPTION_WRITE_TICKET_FILE
175 #define TYPE_TEXT_NAME "text"
176 #define TYPE_DER_NAME "der"
177 #define TYPE_HEX_NAME "hex"
178 #define TYPE_BASE64_NAME "base64"
179 #define TYPE_BINARY_NAME "binary"
181 struct arguments
183 int silent, verbose, verbose_library;
184 char *etypes;
185 char *lib_options;
186 int command;
187 char *ticketfile;
188 char *ticketwritefile;
189 char *systemcfgfile;
190 char *usercfgfile;
191 const char *client;
192 const char *crealm;
193 const char *cname;
194 const char *sname;
195 const char *srealm;
196 const char *server;
197 char *tgtname;
198 int forceas_p;
199 int forcetgs_p;
200 char *servername;
201 int renewable;
202 time_t starttime;
203 char *endtime_str;
204 time_t endtime;
205 char *renew_till_str;
206 time_t renew_till;
207 /* crypto */
208 int algorithm;
209 int encrypt_p;
210 int decrypt_p;
211 char *password;
212 char *salt;
213 char *parameter;
214 int random;
215 int kvno;
216 char *keyvalue;
217 int keyusage;
218 char *readkeyfile;
219 char *writekeyfile;
220 char *inputfile;
221 int inputtype;
222 char *outputfile;
223 int outputtype;
226 const char *program_name = PACKAGE;
227 const char *argp_program_version = PACKAGE_STRING;
228 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
230 static int
231 crypto (Shishi * handle, struct arguments arg)
233 Shishi_key *key;
234 int rc;
236 if (arg.cname == NULL)
237 arg.cname = shishi_principal_default (handle);
239 if (arg.crealm == NULL)
240 arg.crealm = shishi_realm_default (handle);
242 if (arg.salt == NULL)
244 char *cname, *tok, *tokptr;
246 cname = xstrdup (arg.cname);
247 arg.salt = xstrdup (arg.crealm);
248 tok = strtok_r (cname, "/", &tokptr);
249 while (tok)
251 arg.salt =
252 xrealloc (arg.salt, strlen (arg.salt) + strlen (tok) + 1);
253 strcat (arg.salt, tok);
254 tok = strtok_r (NULL, "/", &tokptr);
256 free (cname);
259 rc = shishi_key (handle, &key);
260 if (rc != SHISHI_OK)
262 shishi_error_printf (handle, _("Cannot create key: %s"),
263 shishi_strerror (rc));
264 return rc;
267 shishi_key_type_set (key, arg.algorithm);
268 shishi_key_version_set (key, arg.kvno);
269 shishi_key_principal_set (key, arg.cname);
270 shishi_key_realm_set (key, arg.crealm);
272 if (arg.password)
274 rc = shishi_string_to_key (handle, arg.algorithm,
275 arg.password,
276 strlen (arg.password),
277 arg.salt,
278 strlen (arg.salt), arg.parameter, key);
279 if (rc != SHISHI_OK)
281 shishi_error_printf (handle, _("Error in string2key"));
282 return rc;
286 else if (arg.keyvalue)
288 rc = shishi_key_from_base64 (handle, arg.algorithm, arg.keyvalue, &key);
289 if (rc != SHISHI_OK)
291 fprintf (stderr, _("Could not create key: %s\n"),
292 shishi_strerror (rc));
293 return rc;
296 else if (arg.random)
298 char buf[BUFSIZ];
300 rc = shishi_randomize (handle, buf,
301 shishi_cipher_randomlen (arg.algorithm));
302 if (rc != SHISHI_OK)
303 return rc;
305 shishi_random_to_key (handle, arg.algorithm,
306 buf, shishi_cipher_randomlen (arg.algorithm),
307 key);
309 else if (arg.readkeyfile)
311 key = shishi_keys_for_server_in_file (handle, arg.readkeyfile,
312 arg.cname);
313 #if 0
314 shishi_key_from_file (handle, arg.writekeyfile, arg.algorithm, key,
315 keylen, arg.kvno, arg.cname, arg.realm);
316 #endif
318 if (key == NULL)
320 fprintf (stderr, _("Could not find key: %s\n"),
321 shishi_error (handle));
322 return 1;
325 else
327 fprintf (stderr, "Nothing to do.\n");
328 return SHISHI_OK;
331 if (arg.verbose ||
332 ((arg.password || arg.random || arg.keyvalue) &&
333 !(arg.encrypt_p || arg.decrypt_p)))
335 shishi_key_print (handle, stdout, key);
338 #if 0
339 currently broken if (arg.encrypt_p || arg.decrypt_p)
341 if (arg.inputfile)
343 infh = fopen (arg.inputfile, "r");
344 if (infh == NULL)
346 shishi_error_printf (handle, _("`%s': %s\n"),
347 arg.inputfile, strerror (errno));
348 return SHISHI_FOPEN_ERROR;
351 else
352 infh = stdin;
354 if (arg.outputfile)
356 outfh = fopen (arg.outputfile, "w");
357 if (outfh == NULL)
359 shishi_error_printf (handle, _("`%s': %s\n"),
360 arg.inputfile, strerror (errno));
361 return SHISHI_FOPEN_ERROR;
364 else
365 outfh = stdout;
367 outlen = fread (out, sizeof (out[0]),
368 sizeof (out) / sizeof (out[0]), infh);
369 if (outlen == 0)
371 fprintf (stderr, _("Error reading `%s'\n"), arg.inputfile);
372 return !SHISHI_OK;
374 if (arg.verbose)
375 printf (_("Read %d bytes...\n"), outlen);
377 if (arg.encrypt_p)
378 rc = shishi_encrypt (handle, key, arg.keyusage,
379 out, outlen, &in, &inlen);
380 else
381 rc = shishi_decrypt (handle, key, arg.keyusage,
382 in, inlen, &out, &outlen);
383 if (rc != SHISHI_OK)
385 shishi_error_printf (handle, _("Error ciphering\n"));
386 return rc;
389 if (arg.outputtype == SHISHI_FILETYPE_HEX)
391 for (i = 0; i < inlen; i++)
393 if ((i % 16) == 0)
394 fprintf (outfh, "\n");
395 fprintf (outfh, "%02x ", in[i]);
397 fprintf (outfh, "\n");
399 else if (arg.outputtype == SHISHI_FILETYPE_BINARY)
401 i = fwrite (in, sizeof (in[0]), inlen, outfh);
402 if (i != inlen)
404 fprintf (stderr, _("Short write (%d < %d)...\n"), i, inlen);
405 return 1;
407 printf (_("Wrote %d bytes...\n"), inlen);
410 if (arg.outputfile)
412 rc = fclose (outfh);
413 if (rc != 0)
415 shishi_error_printf (handle, _("`%s': %s\n"),
416 arg.outputfile, strerror (errno));
417 return SHISHI_FCLOSE_ERROR;
421 if (arg.inputfile)
423 rc = fclose (infh);
424 if (rc != 0)
426 shishi_error_printf (handle, _("`%s': %s\n"),
427 arg.inputfile, strerror (errno));
428 return SHISHI_FCLOSE_ERROR;
432 #endif
434 if (arg.writekeyfile)
436 shishi_key_to_file (handle, arg.writekeyfile, key);
439 return 0;
442 static void
443 parse_filename (char *arg, int *type, char **var)
445 if (strncasecmp (arg, TYPE_TEXT_NAME ",", strlen (TYPE_TEXT_NAME ",")) == 0)
447 (*type) = SHISHI_FILETYPE_TEXT;
448 arg += strlen (TYPE_TEXT_NAME ",");
450 else if (strncasecmp (arg, TYPE_DER_NAME ",", strlen (TYPE_DER_NAME ",")) ==
453 (*type) = SHISHI_FILETYPE_DER;
454 arg += strlen (TYPE_DER_NAME ",");
456 else if (strncasecmp (arg, TYPE_HEX_NAME ",", strlen (TYPE_HEX_NAME ",")) ==
459 (*type) = SHISHI_FILETYPE_HEX;
460 arg += strlen (TYPE_HEX_NAME ",");
462 else if (strncasecmp (arg, TYPE_BASE64_NAME ",",
463 strlen (TYPE_BASE64_NAME ",")) == 0)
465 (*type) = SHISHI_FILETYPE_BASE64;
466 arg += strlen (TYPE_BASE64_NAME ",");
468 else if (strncasecmp (arg, TYPE_BINARY_NAME ",",
469 strlen (TYPE_BINARY_NAME ",")) == 0)
471 (*type) = SHISHI_FILETYPE_BINARY;
472 arg += strlen (TYPE_BINARY_NAME ",");
474 else
475 (*type) = 0;
476 *var = strdup (arg);
479 static error_t
480 parse_opt (int key, char *arg, struct argp_state *state)
482 struct arguments *arguments = state->input;
484 switch (key)
486 case 'q':
487 arguments->silent = 1;
488 break;
490 case 'v':
491 arguments->verbose = 1;
492 break;
494 case OPTION_VERBOSE_LIBRARY:
495 arguments->verbose_library = 1;
496 break;
498 case 'o':
499 arguments->lib_options = arg;
500 break;
502 case OPTION_WRITE_TICKET_FILE:
503 arguments->ticketwritefile = strdup (arg);
504 break;
506 case 'E':
507 arguments->etypes = strdup (arg);
508 break;
510 case OPTION_CFG_SYSTEM:
511 arguments->systemcfgfile = strdup (arg);
512 break;
514 case OPTION_CFG_USER:
515 arguments->usercfgfile = strdup (arg);
516 break;
518 case 'c':
519 arguments->ticketfile = strdup (arg);
520 break;
522 case OPTION_CRYPTO_ENCRYPT:
523 arguments->command = OPTION_CRYPTO;
524 if (arguments->decrypt_p)
525 argp_error (state, _("Cannot both encrypt and decrypt."));
526 arguments->encrypt_p = 1;
527 break;
529 case OPTION_CRYPTO_DECRYPT:
530 arguments->command = OPTION_CRYPTO;
531 if (arguments->encrypt_p)
532 argp_error (state, _("Cannot both encrypt and decrypt."));
533 arguments->decrypt_p = 1;
534 break;
536 case OPTION_CRYPTO_KEY_VALUE:
537 arguments->keyvalue = strdup (arg);
538 break;
540 case OPTION_CRYPTO_KEY_USAGE:
541 if (arguments->command != OPTION_CRYPTO)
542 argp_error (state, _("Option `%s' only valid with CRYPTO."),
543 state->argv[state->next - 1]);
544 arguments->keyusage = atoi (arg);
545 break;
547 case OPTION_CRYPTO_KEY_VERSION:
548 if (arguments->command != OPTION_CRYPTO)
549 argp_error (state, _("Option `%s' only valid with CRYPTO."),
550 state->argv[state->next - 1]);
551 arguments->kvno = atoi (arg);
552 break;
554 case OPTION_CRYPTO_PARAMETER:
555 if (arguments->command != OPTION_CRYPTO)
556 argp_error (state, _("Option `%s' only valid with CRYPTO."),
557 state->argv[state->next - 1]);
558 arguments->parameter = strdup (arg);
559 break;
561 case OPTION_CRYPTO_PASSWORD:
562 arguments->password = strdup (arg);
563 break;
565 case OPTION_CRYPTO_RANDOM:
566 if (arguments->command != OPTION_CRYPTO)
567 argp_error (state, _("Option `%s' only valid with CRYPTO."),
568 state->argv[state->next - 1]);
569 arguments->random = 1;
570 break;
572 case OPTION_CRYPTO_READ_DATA_FILE:
573 if (arguments->command != OPTION_CRYPTO)
574 argp_error (state, _("Option `%s' only valid with CRYPTO."),
575 state->argv[state->next - 1]);
576 parse_filename (arg, &arguments->inputtype, &arguments->inputfile);
577 if (arguments->inputtype == SHISHI_FILETYPE_TEXT ||
578 arguments->inputtype == SHISHI_FILETYPE_DER)
579 arguments->inputtype = SHISHI_FILETYPE_BINARY;
580 break;
582 case OPTION_CRYPTO_READ_KEY_FILE:
583 if (arguments->command != OPTION_CRYPTO)
584 argp_error (state, _("Option `%s' only valid with CRYPTO."),
585 state->argv[state->next - 1]);
586 arguments->readkeyfile = strdup (arg);
587 break;
589 case OPTION_CRYPTO_SALT:
590 if (arguments->command != OPTION_CRYPTO)
591 argp_error (state, _("Option `%s' only valid with CRYPTO."),
592 state->argv[state->next - 1]);
593 arguments->salt = strdup (arg);
594 break;
596 case OPTION_CRYPTO_STR2KEY:
597 arguments->command = OPTION_CRYPTO;
598 if (arg)
600 if (arguments->password)
601 argp_error (state, _("Password specified twice."));
602 arguments->password = strdup (arg);
604 break;
606 case OPTION_CRYPTO_WRITE_DATA_FILE:
607 if (arguments->command != OPTION_CRYPTO)
608 argp_error (state, _("Option `%s' only valid with CRYPTO."),
609 state->argv[state->next - 1]);
610 parse_filename (arg, &arguments->outputtype, &arguments->outputfile);
611 if (arguments->outputtype == SHISHI_FILETYPE_TEXT ||
612 arguments->outputtype == SHISHI_FILETYPE_DER)
613 arguments->outputtype = SHISHI_FILETYPE_BINARY;
614 break;
616 case OPTION_CRYPTO_WRITE_KEY_FILE:
617 if (arguments->command != OPTION_CRYPTO)
618 argp_error (state, _("Option `%s' only valid with CRYPTO."),
619 state->argv[state->next - 1]);
620 arguments->writekeyfile = strdup (arg);
621 break;
623 case OPTION_CLIENT_NAME:
624 arguments->cname = strdup (arg);
625 break;
627 case 'e':
628 case OPTION_ENDTIME:
629 arguments->endtime_str = strdup (arg);
630 break;
632 case OPTION_REALM:
633 arguments->crealm = strdup (arg);
634 break;
636 case 'R':
637 case OPTION_RENEW:
638 arguments->command = OPTION_RENEW;
639 break;
641 case OPTION_RENEW_TILL:
642 arguments->renew_till_str = strdup (arg);
643 /* fall through */
645 case OPTION_RENEWABLE:
646 arguments->renewable = 1;
647 break;
649 case 's':
650 case OPTION_STARTTIME:
651 arguments->starttime = get_date (arg, NULL);
652 if (arguments->starttime == -1)
653 argp_error (state, _("invalid --starttime date `%s'"), arg);
654 break;
656 case OPTION_SERVER_NAME:
657 arguments->sname = strdup (arg);
658 break;
660 case OPTION_FORCE_AS:
661 arguments->forceas_p = 1;
662 break;
664 case OPTION_FORCE_TGS:
665 arguments->forcetgs_p = 1;
666 break;
668 case OPTION_TICKET_GRANTER:
669 arguments->tgtname = strdup (arg);
670 break;
672 case 'l':
673 case OPTION_LIST:
674 arguments->command = OPTION_LIST;
675 break;
677 case 'd':
678 case OPTION_DESTROY:
679 arguments->command = OPTION_DESTROY;
680 break;
682 case ARGP_KEY_ARG:
683 if (arguments->server && arguments->client)
684 argp_error (state, _("Too many arguments: `%s'"), arg);
685 if (arguments->client)
686 arguments->server = strdup (arg);
687 else
688 arguments->client = strdup (arg);
689 break;
691 default:
692 return ARGP_ERR_UNKNOWN;
695 return 0;
698 static struct argp_option options[] = {
700 {0, 0, 0, 0, "If no command is given, Shishi try to make sure you have a "
701 "ticket granting ticket for the default realm, and then display it.", 0},
703 {"client-name", OPTION_CLIENT_NAME, "NAME", 0,
704 "Client name. Default is login username.", 10},
706 {"destroy", 'd', 0, 0,
707 "Destroy tickets in local cache, subject to --client-name and "
708 "--server-name limiting.", 0},
710 {"encryption-type", 'E', "ETYPE,[ETYPE...]", 0,
711 "Encryption types to use. ETYPE is either registered name or integer.",
714 {"force-as", OPTION_FORCE_AS, 0, 0,
715 "Force AS mode. Default is to use TGS iff a TGT is found.", 0},
717 {"force-tgs", OPTION_FORCE_TGS, 0, 0,
718 "Force TGS mode. Default is to use TGS iff a TGT is found.", 0},
720 {"endtime", 'e', "STRING", 0,
721 "Specify when ticket validity should expire. The time syntax may be "
722 "relative (to the start time), such as \"20 hours\", or absolute, "
723 "such as \"2001-02-03 04:05:06 CET\". The default is 8 hours after "
724 "the start time.", 0},
726 {"list", 'l', 0, 0,
727 "List tickets in local cache, subject to --server-name limiting.", 0},
729 {"renew", 'R', 0, 0,
730 "Renew ticket. Use --server-name to specify ticket, default is the "
731 "most recent renewable ticket granting ticket for the default realm.", 0},
733 {"renewable", OPTION_RENEWABLE, 0, 0,
734 "Get a renewable ticket.", 0},
736 {"renew-till", OPTION_RENEW_TILL, "STRING", 0,
737 "Specify renewable life of ticket. Implies --renewable. Accepts same "
738 "time syntax as --endtime. If --renewable is specified, the default is 1 "
739 "week after the start time.", 0},
741 {"realm", OPTION_REALM, "REALM", 0,
742 "Realm of server. Default is DNS domain of local host. For AS, this also "
743 "indicates realm of client.", 0},
745 {"server", OPTION_SERVER, "[FAMILY:]ADDRESS:SERVICE/TYPE", 0,
746 "Send all requests to HOST instead of using normal logic to locate "
747 "KDC addresses (discouraged).", 0},
749 {"server-name", OPTION_SERVER_NAME, "NAME", 0,
750 "Server name. Default is \"krbtgt/REALM\" where REALM is server "
751 "realm (see --realm).", 0},
753 {"starttime", 's', "STRING", 0,
754 "Specify when ticket should start to be valid. Accepts same time syntax "
755 "as --endtime. The default is to become valid immediately.", 0},
757 {"ticket-granter", OPTION_TICKET_GRANTER, "NAME", 0,
758 "Service name in ticket to use for authenticating request. Only for TGS. "
759 "Defaults to \"krbtgt/REALM@REALM\" where REALM is server "
760 "realm (see --realm).", 0},
761 #if 0
762 {"key-value", OPTION_CRYPTO_KEY_VALUE, "KEY", 0,
763 "Cipher key to decrypt response (discouraged).", 0},
764 #endif
766 /************** CRYPTO */
768 {0, 0, 0, 0,
769 "Options for low-level cryptography (CRYPTO-OPTIONS):", 100},
771 {"client-name", OPTION_CLIENT_NAME, "NAME", 0,
772 "Username. Default is login name.", 0},
773 #if 0
774 {"decrypt", OPTION_CRYPTO_DECRYPT, 0, 0,
775 "Decrypt data.", 0},
777 {"encrypt", OPTION_CRYPTO_ENCRYPT, 0, 0,
778 "Encrypt data.", 0},
780 {"key-usage", OPTION_CRYPTO_KEY_USAGE, "KEYUSAGE", 0,
781 "Encrypt or decrypt using specified key usage. Default is 0, which "
782 "means no key derivation are performed.", 0},
784 {"key-value", OPTION_CRYPTO_KEY_VALUE, "KEY", 0,
785 "Base64 encoded key value.", 0},
786 #endif
787 {"key-version", OPTION_CRYPTO_KEY_VERSION, "INTEGER", 0,
788 "Version number of key. Default is 0.", 0},
790 {"random", OPTION_CRYPTO_RANDOM, 0, 0,
791 "Generate key from random data.", 0},
792 #if 0
793 {"read-key-file", OPTION_CRYPTO_READ_KEY_FILE, "FILE", 0,
794 "Read cipher key from FILE", 0},
796 {"read-data-file", OPTION_CRYPTO_READ_DATA_FILE, "[TYPE,]FILE", 0,
797 "Read data from FILE in TYPE, BASE64, HEX or BINARY (default).", 0},
798 #endif
799 {"realm", OPTION_REALM, "REALM", 0,
800 "Realm of principal. Defaults to DNS domain of local host. ", 0},
802 {"salt", OPTION_CRYPTO_SALT, "SALT", 0,
803 "Salt to use for --string-to-key. Defaults to concatenation of "
804 "realm and (unwrapped) client name.", 0},
806 {"string-to-key", OPTION_CRYPTO_STR2KEY, "[PASSWORD]", OPTION_ARG_OPTIONAL,
807 "Convert password into Kerberos key. Note that --client-name, --realm, "
808 "and --salt influence the generated key.", 0},
810 {"parameter", OPTION_CRYPTO_PARAMETER, "STRING", 0,
811 "String-to-key parameter. This data is specific for each encryption "
812 "algorithm and rarely needed.", 0},
813 #if 0
814 {"write-key-file", OPTION_CRYPTO_WRITE_KEY_FILE, "FILE", 0,
815 "Append cipher key to FILE", 0},
817 {"write-data-file", OPTION_CRYPTO_WRITE_DATA_FILE, "[TYPE,]FILE", 0,
818 "Write data to FILE in TYPE, BASE64, HEX or BINARY (default).", 0},
819 #endif
820 /************** OTHER */
822 {0, 0, 0, 0, "Other options:", 200},
824 {"verbose", 'v', 0, 0,
825 "Produce verbose output.", 0},
827 {"verbose-library", OPTION_VERBOSE_LIBRARY, 0, 0,
828 "Produce verbose output in the library.", 0},
830 {"quiet", 'q', 0, 0,
831 "Don't produce any output.", 0},
833 {"silent", 0, 0, OPTION_ALIAS,
834 NULL, 0},
836 {"system-configuration-file", OPTION_CFG_SYSTEM, "FILE", 0,
837 "Read system wide configuration from file. Default is " SYSTEMCFGFILE
838 ".", 0},
840 {"configuration-file", OPTION_CFG_USER, "FILE", 0,
841 "Read user configuration from file. Default is ~/.shishi/config.", 0},
843 {"library-options", 'o', "STRING", 0,
844 "Parse STRING as a configuration file statement.", 0},
846 {"ticket-file", 'c', "FILE", 0,
847 "Read tickets from FILE. Default is $HOME/.shishi/tickets.", 0},
849 {"ticket-write-file", OPTION_WRITE_TICKET_FILE, "FILE", 0,
850 "Write tickets to FILE. Default is to write them back to ticket file.",
853 {"CLIENT", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
854 "Set client name and realm from NAME. The --client-name and --realm "
855 "parameters can be used to override part of NAME.", 0},
857 {"SERVER", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
858 "Set server name and realm from NAME. The --server-name and "
859 "--server-realm parameters can be used to override part of SERVER.", 0},
861 /************** EXAMPLES */
863 {0, 0, 0, 0, "Examples:", 300},
865 {"shishi", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
866 "Get a ticket granting ticket from the default KDC server for the "
867 "default user and realm.", 0},
869 {"shishi jas/admin@ACCOUNTING", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
870 "Get a ticket for jas/admin in the ACCOUNTING realm.", 0},
872 {"shishi --list --server-name=krbtgt/JOSEFSSON.ORG@JOSEFSSON.ORG",
873 0, 0, OPTION_DOC | OPTION_NO_USAGE,
874 "List tickets for the Ticket Granting Service in the JOSEFSSON.ORG realm.",
877 {NULL, 0, 0, 0, NULL, 0}
880 static struct argp argp = {
881 options,
882 parse_opt,
883 "[CLIENT [SERVER]] [OPTION...]\n"
884 "--list [CLIENT [SERVER]]\n"
885 "--destroy [CLIENT [SERVER]]\n"
886 "--string-to-key [CLIENT] [OPTION...]\n",
887 "Shishi -- A Kerberos 5 implementation",
888 NULL,
889 NULL,
890 NULL
894 main (int argc, char *argv[])
896 struct arguments arg;
897 Shishi *handle;
898 int rc;
899 int32_t *etype;
901 setlocale (LC_ALL, "");
902 bindtextdomain (PACKAGE, LOCALEDIR);
903 textdomain (PACKAGE);
905 memset (&arg, 0, sizeof (arg));
906 arg.algorithm = -1;
907 argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &arg);
909 rc = shishi_init_with_paths (&handle, arg.ticketfile,
910 arg.systemcfgfile, arg.usercfgfile);
911 if (rc == SHISHI_HANDLE_ERROR)
912 error (1, 0, "Internal error: could not initialize shishi\n");
914 rc = shishi_cfg_clientkdcetype_set (handle, arg.etypes);
915 if (rc != SHISHI_OK)
916 error (1, 0, "Could not set encryption types: %s\n",
917 shishi_strerror (rc));
919 if (arg.algorithm == -1 && shishi_cfg_clientkdcetype (handle, &etype) > 0)
920 arg.algorithm = *etype;
922 if (arg.client)
924 rc = shishi_parse_name (handle, arg.client,
925 (char **) (arg.cname ? NULL : &arg.cname),
926 (char **) (arg.crealm ? NULL : &arg.crealm));
928 if (rc != SHISHI_OK)
929 error (1, 0, "Could not parse principal \"%s\": %s\n", arg.client,
930 shishi_strerror (rc));
933 if (arg.server)
935 rc = shishi_parse_name (handle, arg.server,
936 (char **) (arg.sname ? NULL : &arg.sname),
937 (char **) (arg.srealm ? NULL : &arg.srealm));
939 if (rc != SHISHI_OK)
940 error (1, 0, "Could not parse principal \"%s\": %s\n", arg.server,
941 shishi_strerror (rc));
944 rc = shishi_cfg (handle, arg.lib_options);
945 if (rc != SHISHI_OK)
946 error (1, 0, "Could not read library options: %s\n",
947 shishi_strerror (rc));
949 if (arg.verbose_library)
951 rc = shishi_cfg (handle, "verbose");
952 if (rc != SHISHI_OK)
953 error (1, 0, "Could not make library verbose: %s\n",
954 shishi_strerror (rc));
957 if (!arg.starttime)
958 arg.starttime = time (NULL);
960 if (arg.endtime_str)
962 arg.endtime = get_date (arg.endtime_str, &arg.starttime);
963 if (arg.endtime == -1)
964 error (1, 0, _("invalid --endtime date `%s'"), arg.endtime_str);
967 if (arg.renew_till_str)
969 arg.renew_till = get_date (arg.renew_till_str, &arg.starttime);
970 if (arg.renew_till == -1)
971 error (1, 0, _("invalid --renew-till date `%s'"), arg.renew_till_str);
974 if (arg.cname)
975 shishi_principal_default_set (handle, arg.cname);
977 if (arg.crealm)
978 shishi_realm_default_set (handle, arg.crealm);
980 if (!arg.tgtname)
982 asprintf (&arg.tgtname, "krbtgt/%s", shishi_realm_default (handle));
983 if (arg.tgtname == NULL)
984 error (1, 0, "Could not allocate TGT name.");
987 rc = 1;
989 switch (arg.command)
991 case OPTION_LIST:
992 if (!arg.silent)
993 printf (_("Tickets in `%s':\n"), shishi_tkts_default_file (handle));
995 rc = shishi_tkts_print_for_service (shishi_tkts_default (handle),
996 stdout, arg.sname);
997 if (rc != SHISHI_OK)
998 fprintf (stderr, "Could not list tickets: %s", shishi_strerror (rc));
999 break;
1001 case OPTION_DESTROY:
1003 int i, removed = 0;
1004 for (i = 0; i < shishi_tkts_size (shishi_tkts_default (handle)); i++)
1006 if (arg.sname &&
1007 !shishi_tkt_server_p (shishi_tkts_nth
1008 (shishi_tkts_default (handle),
1009 i), arg.sname))
1010 continue;
1012 if (arg.verbose)
1014 printf ("Removing ticket:\n");
1015 shishi_tkt_pretty_print (shishi_tkts_nth
1016 (shishi_tkts_default
1017 (handle), i), stdout);
1020 rc = shishi_tkts_remove (shishi_tkts_default (handle), i);
1021 if (rc != SHISHI_OK)
1022 fprintf (stderr, "Could not destroy ticket %d:\n%s\n", i,
1023 shishi_strerror (rc));
1024 i--;
1025 removed++;
1027 if (removed == 0)
1028 printf ("No tickets removed.\n");
1029 else if (removed == 1)
1030 printf ("1 ticket removed.\n");
1031 else
1032 printf ("%d tickets removed.\n", removed);
1033 rc = SHISHI_OK;
1035 break;
1037 case OPTION_CRYPTO:
1038 rc = crypto (handle, arg);
1039 if (rc != SHISHI_OK)
1040 fprintf (stderr, "Operation failed:\n%s\n%s\n",
1041 shishi_strerror (rc), shishi_error (handle));
1042 break;
1044 case OPTION_RENEW:
1046 Shishi_tkt *tkt;
1047 Shishi_tkts_hint hint;
1048 Shishi_tgs *tgs;
1050 /* This doesn't work */
1052 memset (&hint, 0, sizeof (hint));
1053 hint.client = (char *) arg.cname;
1054 hint.server = (char *) (arg.sname ? arg.sname : arg.tgtname);
1055 hint.starttime = arg.starttime;
1056 hint.endtime = arg.endtime;
1057 hint.renew_till = arg.renew_till;
1058 hint.renewable = arg.renewable;
1060 tkt = shishi_tkts_find (shishi_tkts_default (handle), &hint);
1061 if (!tkt)
1063 printf ("Could not get ticket for `%s'.\n", hint.server);
1064 rc = !SHISHI_OK;
1066 else
1068 rc = shishi_tkt_pretty_print (tkt, stdout);
1069 if (rc != SHISHI_OK)
1070 fprintf (stderr, "Pretty printing ticket failed:\n%s\n%s\n",
1071 shishi_strerror (rc),
1072 shishi_error (handle));
1075 /* Get ticket using TGT ... */
1076 rc = shishi_tgs (handle, &tgs);
1077 shishi_tgs_tgtkt_set (tgs, tkt);
1078 if (rc == SHISHI_OK)
1079 rc = shishi_tgs_set_server (tgs, hint.server);
1080 rc = shishi_kdcreq_options_add (handle, shishi_tgs_req (tgs),
1081 SHISHI_KDCOPTIONS_RENEWABLE |
1082 SHISHI_KDCOPTIONS_RENEW);
1083 if (rc == SHISHI_OK)
1084 rc = shishi_asn1_write (handle, shishi_tgs_req (tgs),
1085 "req-body.rtime",
1086 shishi_generalize_time
1087 (handle, hint.renew_till), 0);
1088 if (rc == SHISHI_OK)
1089 rc = shishi_tgs_req_build (tgs);
1090 if (rc == SHISHI_OK)
1091 rc = shishi_tgs_sendrecv (tgs);
1092 if (rc == SHISHI_OK)
1093 rc = shishi_tgs_rep_process (tgs);
1094 if (rc != SHISHI_OK)
1096 printf ("TGS exchange failed: %s\n%s\n", shishi_strerror (rc),
1097 shishi_error (handle));
1098 if (rc == SHISHI_GOT_KRBERROR)
1099 shishi_krberror_pretty_print (handle, stdout,
1100 shishi_tgs_krberror (tgs));
1101 break;
1104 tkt = shishi_tgs_tkt (tgs);
1105 if (!tkt)
1107 printf ("No ticket in TGS-REP?!: %s\n",
1108 shishi_error (handle));
1109 break;
1112 shishi_tkt_pretty_print (tkt, stdout);
1114 rc = shishi_tkts_add (shishi_tkts_default (handle), tkt);
1115 if (rc != SHISHI_OK)
1116 printf ("Could not add ticket: %s", shishi_strerror (rc));
1118 break;
1120 default:
1122 Shishi_tkt *tkt;
1123 Shishi_tkts_hint hint;
1125 memset (&hint, 0, sizeof (hint));
1126 hint.client = (char *) arg.cname;
1127 hint.server = (char *) (arg.sname ? arg.sname : arg.tgtname);
1128 hint.starttime = arg.starttime;
1129 hint.endtime = arg.endtime;
1130 hint.renew_till = arg.renew_till;
1131 hint.renewable = arg.renewable;
1133 tkt = shishi_tkts_get (shishi_tkts_default (handle), &hint);
1134 if (!tkt)
1136 printf ("Could not get ticket for `%s'.\n", hint.server);
1137 rc = !SHISHI_OK;
1139 else
1141 rc = shishi_tkt_pretty_print (tkt, stdout);
1142 if (rc != SHISHI_OK)
1143 fprintf (stderr, "Pretty printing ticket failed:\n%s\n%s\n",
1144 shishi_strerror (rc),
1145 shishi_error (handle));
1148 break;
1151 shishi_tkts_expire (shishi_tkts_default (handle));
1153 if (arg.ticketwritefile)
1154 shishi_tkts_default_file_set (handle, arg.ticketwritefile);
1156 shishi_done (handle);
1158 return rc == SHISHI_OK ? 0 : 1;