Add.
[shishi.git] / src / shishi.c
blob7f8f1e3b579caf00497a729c5bbece2ff0e36269
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 #include "data.h"
23 #include <argp.h>
25 const char *program_name = PACKAGE;
26 const char *argp_program_version = PACKAGE_STRING;
27 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
29 static void
30 parse_filename (char *arg, int *type, char **var)
32 if (strncasecmp (arg, TYPE_TEXT_NAME ",", strlen (TYPE_TEXT_NAME ",")) == 0)
34 (*type) = SHISHI_FILETYPE_TEXT;
35 arg += strlen (TYPE_TEXT_NAME ",");
37 else if (strncasecmp (arg, TYPE_DER_NAME ",", strlen (TYPE_DER_NAME ",")) ==
40 (*type) = SHISHI_FILETYPE_DER;
41 arg += strlen (TYPE_DER_NAME ",");
43 else if (strncasecmp (arg, TYPE_HEX_NAME ",", strlen (TYPE_HEX_NAME ",")) ==
46 (*type) = SHISHI_FILETYPE_HEX;
47 arg += strlen (TYPE_HEX_NAME ",");
49 else if (strncasecmp (arg, TYPE_BASE64_NAME ",",
50 strlen (TYPE_BASE64_NAME ",")) == 0)
52 (*type) = SHISHI_FILETYPE_BASE64;
53 arg += strlen (TYPE_BASE64_NAME ",");
55 else if (strncasecmp (arg, TYPE_BINARY_NAME ",",
56 strlen (TYPE_BINARY_NAME ",")) == 0)
58 (*type) = SHISHI_FILETYPE_BINARY;
59 arg += strlen (TYPE_BINARY_NAME ",");
61 else
62 (*type) = 0;
63 *var = strdup (arg);
66 static error_t
67 parse_opt (int key, char *arg, struct argp_state *state)
69 struct arguments *arguments = state->input;
71 switch (key)
73 case 'q':
74 arguments->silent = 1;
75 break;
77 case 'v':
78 arguments->verbose = 1;
79 break;
81 case OPTION_VERBOSE_LIBRARY:
82 arguments->verbose_library = 1;
83 break;
85 case 'o':
86 arguments->lib_options = arg;
87 break;
89 case 'w':
90 arguments->ticketwritefile = strdup (arg);
91 break;
93 case 'e':
94 arguments->etypes = strdup (arg);
95 break;
97 case 's':
98 arguments->systemcfgfile = strdup (arg);
99 break;
101 case 'c':
102 arguments->usercfgfile = strdup (arg);
103 break;
105 case 't':
106 arguments->ticketfile = strdup (arg);
107 break;
109 /* Client */
111 case OPTION_CLIENT_AP_OPTIONS:
113 char *ptrptr;
114 char *val;
115 int i;
117 arguments->apoptions = 0;
118 for (i = 0;
119 (val = strtok_r (i == 0 ? arg : NULL, ", \t\n\r", &ptrptr)); i++)
121 int option = shishi_ap_string2option (val);
122 if (option == 0)
123 fprintf (stderr, "Ignoring unknown AP option: `%s'\n", val);
124 arguments->apoptions |= option;
127 break;
129 /* Crypto */
131 case OPTION_CRYPTO_ALGORITHM:
132 if (arguments->command != COMMAND_CRYPTO)
133 argp_error (state, _("Option `%s' only valid with CRYPTO."),
134 state->argv[state->next - 1]);
135 arguments->algorithm = shishi_cipher_parse (arg);
136 if (arguments->algorithm == -1)
137 argp_error (state, _("Unknown encryption type in `%s'"),
138 state->argv[state->next - 1]);
139 break;
141 case OPTION_CRYPTO_ENCRYPT:
142 if (arguments->command != COMMAND_CRYPTO)
143 argp_error (state, _("Option `%s' only valid with CRYPTO."),
144 state->argv[state->next - 1]);
145 if (arguments->decrypt_p)
146 argp_error (state, _("Cannot both encrypt and decrypt."));
147 arguments->encrypt_p = 1;
148 break;
150 case OPTION_CRYPTO_DECRYPT:
151 if (arguments->command != COMMAND_CRYPTO)
152 argp_error (state, _("Option `%s' only valid with CRYPTO."),
153 state->argv[state->next - 1]);
154 if (arguments->encrypt_p)
155 argp_error (state, _("Cannot both encrypt and decrypt."));
156 arguments->decrypt_p = 1;
157 break;
159 case OPTION_CRYPTO_SALT:
160 if (arguments->command != COMMAND_CRYPTO)
161 argp_error (state, _("Option `%s' only valid with CRYPTO."),
162 state->argv[state->next - 1]);
163 arguments->salt = strdup (arg);
164 break;
166 case OPTION_CRYPTO_PARAMETER:
167 if (arguments->command != COMMAND_CRYPTO)
168 argp_error (state, _("Option `%s' only valid with CRYPTO."),
169 state->argv[state->next - 1]);
170 arguments->parameter = strdup (arg);
171 break;
173 case OPTION_AS_PASSWORD:
174 case OPTION_CRYPTO_PASSWORD:
175 case OPTION_KDC_PASSWORD:
176 case OPTION_SERVER_PASSWORD:
177 if (arguments->command != COMMAND_CRYPTO &&
178 arguments->command != COMMAND_AS &&
179 arguments->command != COMMAND_KDC &&
180 arguments->command != COMMAND_SERVER)
181 argp_error
182 (state,
183 _("Option `%s' only valid with CRYPTO, KDC/AS/TGS and SERVER."),
184 state->argv[state->next - 1]);
185 arguments->password = strdup (arg);
186 break;
188 case OPTION_CRYPTO_RANDOM:
189 if (arguments->command != COMMAND_CRYPTO)
190 argp_error (state, _("Option `%s' only valid with CRYPTO."),
191 state->argv[state->next - 1]);
192 arguments->random = 1;
193 break;
195 case OPTION_CRYPTO_READ_DATA_FILE:
196 if (arguments->command != COMMAND_CRYPTO)
197 argp_error (state, _("Option `%s' only valid with CRYPTO."),
198 state->argv[state->next - 1]);
199 parse_filename (arg, &arguments->inputtype, &arguments->inputfile);
200 if (arguments->inputtype == SHISHI_FILETYPE_TEXT ||
201 arguments->inputtype == SHISHI_FILETYPE_DER)
202 arguments->inputtype = SHISHI_FILETYPE_BINARY;
203 break;
205 case OPTION_CRYPTO_WRITE_DATA_FILE:
206 if (arguments->command != COMMAND_CRYPTO)
207 argp_error (state, _("Option `%s' only valid with CRYPTO."),
208 state->argv[state->next - 1]);
209 parse_filename (arg, &arguments->outputtype, &arguments->outputfile);
210 if (arguments->outputtype == SHISHI_FILETYPE_TEXT ||
211 arguments->outputtype == SHISHI_FILETYPE_DER)
212 arguments->outputtype = SHISHI_FILETYPE_BINARY;
213 break;
215 case OPTION_CRYPTO_READ_KEY_FILE:
216 if (arguments->command != COMMAND_CRYPTO)
217 argp_error (state, _("Option `%s' only valid with CRYPTO."),
218 state->argv[state->next - 1]);
219 arguments->readkeyfile = strdup (arg);
220 break;
222 case OPTION_CRYPTO_WRITE_KEY_FILE:
223 if (arguments->command != COMMAND_CRYPTO)
224 argp_error (state, _("Option `%s' only valid with CRYPTO."),
225 state->argv[state->next - 1]);
226 arguments->writekeyfile = strdup (arg);
227 break;
229 /* Authenticator */
231 case OPTION_AP_AUTHENTICATOR_READ_FILE:
232 if (arguments->command != COMMAND_AP)
233 argp_error (state, _("Option `%s' only valid with AP."),
234 state->argv[state->next - 1]);
235 parse_filename (arg, &arguments->authenticatorreadtype,
236 &arguments->authenticatorreadfile);
237 break;
239 case OPTION_AP_AUTHENTICATOR_DATA:
240 if (arguments->command != COMMAND_AP)
241 argp_error (state, _("Option `%s' only valid with AP."),
242 state->argv[state->next - 1]);
243 arguments->authenticatordata = strdup (arg);
244 break;
246 case OPTION_AP_AUTHENTICATOR_READ_DATA_FILE:
247 if (arguments->command != COMMAND_AP)
248 argp_error (state, _("Option `%s' only valid with AP."),
249 state->argv[state->next - 1]);
250 parse_filename (arg, &arguments->authenticatordatareadtype,
251 &arguments->authenticatordatareadfile);
252 if (arguments->authenticatordatareadtype == SHISHI_FILETYPE_TEXT ||
253 arguments->authenticatordatareadtype == SHISHI_FILETYPE_DER)
254 arguments->authenticatordatareadtype = SHISHI_FILETYPE_BINARY;
255 break;
257 case OPTION_AS_CLIENT_NAME:
258 case OPTION_CRYPTO_CLIENT_NAME:
259 case OPTION_KDC_CLIENT_NAME:
260 case OPTION_SERVER_CLIENT_NAME:
261 case OPTION_TGS_CLIENT_NAME:
262 if (arguments->command != COMMAND_CRYPTO &&
263 arguments->command != COMMAND_AS &&
264 arguments->command != COMMAND_KDC &&
265 arguments->command != COMMAND_TGS &&
266 arguments->command != COMMAND_SERVER)
267 argp_error (state,
269 ("Option `%s' only valid with CRYPTO, KDC/AS/TGS "
270 "and SERVER."), state->argv[state->next - 1]);
271 arguments->cname = strdup (arg);
272 break;
274 case 'r':
275 case OPTION_AP_REALM:
276 case OPTION_AS_REALM:
277 case OPTION_CLIENT_REALM:
278 case OPTION_CRYPTO_REALM:
279 case OPTION_KDC_REALM:
280 case OPTION_TGS_REALM:
281 if (arguments->command != COMMAND_AP &&
282 arguments->command != COMMAND_CLIENT &&
283 arguments->command != COMMAND_CRYPTO &&
284 arguments->command != COMMAND_AS &&
285 arguments->command != COMMAND_KDC &&
286 arguments->command != COMMAND_TGS)
287 argp_error (state, _("Option `%s' only valid with AP, CLIENT, CRYPTO "
288 "and KDC/AS/TGS."),
289 state->argv[state->next - 1]);
290 arguments->realm = strdup (arg);
291 break;
293 case OPTION_CRYPTO_KEY_VALUE:
294 case OPTION_KDC_KEY_VALUE:
295 case OPTION_SERVER_KEY_VALUE:
296 if (arguments->command != COMMAND_CRYPTO &&
297 arguments->command != COMMAND_KDC &&
298 arguments->command != COMMAND_SERVER)
299 argp_error (state,
300 _("Option `%s' only valid with CRYPTO and KDC/AS/TGS."),
301 state->argv[state->next - 1]);
302 arguments->keyvalue = strdup (arg);
303 break;
305 case OPTION_CRYPTO_KEY_USAGE:
306 if (arguments->command != COMMAND_CRYPTO)
307 argp_error (state, _("Option `%s' only valid with CRYPTO."),
308 state->argv[state->next - 1]);
309 arguments->keyusage = atoi (arg);
310 break;
312 case OPTION_CRYPTO_KEY_VERSION:
313 if (arguments->command != COMMAND_CRYPTO)
314 argp_error (state, _("Option `%s' only valid with CRYPTO."),
315 state->argv[state->next - 1]);
316 arguments->kvno = atoi (arg);
317 break;
319 case OPTION_AP_SERVER_NAME:
320 case OPTION_CLIENT_SERVER_NAME:
321 case OPTION_KDC_SERVER_NAME:
322 case OPTION_LIST_SERVER_NAME:
323 case OPTION_SERVER_SERVER_NAME:
324 case OPTION_TGS_SERVER_NAME:
325 arguments->sname = strdup (arg);
326 break;
328 case OPTION_KDC_FORCE_AS:
329 if (arguments->command != COMMAND_KDC)
330 argp_error (state, _("Option `%s' only valid with KDC."),
331 state->argv[state->next - 1]);
332 arguments->forceas_p = 1;
333 break;
335 case OPTION_KDC_FORCE_TGS:
336 if (arguments->command != COMMAND_KDC)
337 argp_error (state, _("Option `%s' only valid with KDC."),
338 state->argv[state->next - 1]);
339 arguments->forcetgs_p = 1;
340 break;
342 case OPTION_KDC_TICKET_GRANTER:
343 case OPTION_TGS_TICKET_GRANTER:
344 if (arguments->command != COMMAND_KDC &&
345 arguments->command != COMMAND_TGS)
346 argp_error (state, _("Option `%s' only valid with KDC/TGS."),
347 state->argv[state->next - 1]);
348 arguments->tgtname = strdup (arg);
349 break;
351 case OPTION_KDC_REQUEST:
352 if (arguments->command != COMMAND_KDC)
353 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
354 state->argv[state->next - 1]);
355 arguments->request_p = 1;
356 break;
358 case OPTION_AP_REQUEST_WRITE_FILE:
359 case OPTION_KDC_WRITE_AP_REQUEST_FILE:
360 if (arguments->command != COMMAND_KDC &&
361 arguments->command != COMMAND_AP)
362 argp_error (state,
363 _("Option `%s' only valid with AP and KDC/AS/TGS."),
364 state->argv[state->next - 1]);
365 parse_filename (arg, &arguments->apreqwritetype,
366 &arguments->apreqwritefile);
367 break;
369 case OPTION_AP_AUTHENTICATOR_WRITE_FILE:
370 case OPTION_KDC_WRITE_AUTHENTICATOR_FILE:
371 if (arguments->command != COMMAND_AP ||
372 arguments->command != COMMAND_KDC)
373 argp_error (state,
374 _("Option `%s' only valid with AP and KDC/AS/TGS."),
375 state->argv[state->next - 1]);
376 parse_filename (arg, &arguments->authenticatorwritetype,
377 &arguments->authenticatorwritefile);
378 break;
380 case OPTION_KDC_WRITE_REQUEST_FILE:
381 if (arguments->command != COMMAND_KDC)
382 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
383 state->argv[state->next - 1]);
384 parse_filename (arg, &arguments->kdcreqwritetype,
385 &arguments->kdcreqwritefile);
386 break;
388 case OPTION_KDC_READ_REQUEST_FILE:
389 if (arguments->command != COMMAND_KDC)
390 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
391 state->argv[state->next - 1]);
392 parse_filename (arg, &arguments->kdcreqreadtype,
393 &arguments->kdcreqreadfile);
394 break;
396 case OPTION_KDC_WRITE_RESPONSE_FILE:
397 if (arguments->command != COMMAND_KDC)
398 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
399 state->argv[state->next - 1]);
400 parse_filename (arg, &arguments->kdcrepwritetype,
401 &arguments->kdcrepwritefile);
402 break;
404 case OPTION_KDC_READ_RESPONSE_FILE:
405 if (arguments->command != COMMAND_KDC)
406 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
407 state->argv[state->next - 1]);
408 parse_filename (arg, &arguments->kdcrepreadtype,
409 &arguments->kdcrepreadfile);
410 break;
412 case OPTION_KDC_SENDRECV:
413 if (arguments->command != COMMAND_KDC)
414 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
415 state->argv[state->next - 1]);
416 arguments->sendrecv_p = 1;
417 break;
419 case OPTION_KDC_RESPONSE:
420 if (arguments->command != COMMAND_KDC)
421 argp_error (state, _("Option `%s' only valid with KDC/AS/TGS."),
422 state->argv[state->next - 1]);
423 arguments->response_p = 1;
424 break;
426 case ARGP_KEY_ARG:
427 if (state->arg_num != 0)
428 argp_error (state, _("Too many arguments: `%s'"), arg);
429 else
431 if (strcmp (arg, "as") == 0)
433 arguments->command = COMMAND_AS;
435 else if (strcmp (arg, "tgs") == 0)
437 arguments->command = COMMAND_TGS;
439 else if (strcmp (arg, "list") == 0)
441 arguments->command = COMMAND_LIST;
443 else if (strcmp (arg, "destroy") == 0)
445 arguments->command = COMMAND_DESTROY;
447 else if (strcmp (arg, "client") == 0)
449 arguments->command = COMMAND_CLIENT;
451 else if (strcmp (arg, "server") == 0)
453 arguments->command = COMMAND_SERVER;
455 else if (strcmp (arg, "ap") == 0)
457 arguments->command = COMMAND_AP;
459 else if (strcmp (arg, "crypto") == 0)
461 arguments->command = COMMAND_CRYPTO;
463 else if (strcmp (arg, "kdc") == 0)
465 arguments->command = COMMAND_KDC;
467 else
469 argp_error (state, _("Unknown command: '%s'"), arg);
471 break;
473 break;
475 default:
476 return ARGP_ERR_UNKNOWN;
479 return 0;
482 static struct argp_option options[] = {
484 {0, 0, 0, 0, "Authentication commands:", 10},
486 {"as", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
487 "Acquire ticket granting ticket using password via the Authentication "
488 "Service (AS) exchange."},
490 {"tgs", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
491 "Acquire ticket using the ticket granting ticket via the Ticket-Granting "
492 "Service (TGS) exchange."},
494 {0, 0, 0, 0, "Ticket management:", 20},
496 {"list", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
497 "List tickets."},
499 {"destroy", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
500 "Destroy tickets."},
502 {0, 0, 0, 0, "Utilities:", 30},
504 {"client", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
505 "Simple stdin/stdout client."},
507 {"server", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
508 "Simple stdin/stdout server."},
510 {0, 0, 0, 0, "Low-level commands:", 40},
512 {"ap", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
513 "Client/Server Authentication (AP-REQ and AP-REP)."},
515 {"crypto", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
516 "Cryptographic functions."},
518 {"kdc", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
519 "Key Distribution Center Services (AS and TGS)."},
521 {0, 0, 0, 0, "If no command is given, Shishi invokes the AS command "
522 "if no ticket granting ticket is found, otherwise the LIST command "
523 "is invoked.", 50},
525 /************** AS */
527 {0, 0, 0, 0, "Options for Authentication Service (AS-OPTIONS):", 100},
529 {"client-name", OPTION_AS_CLIENT_NAME, "NAME", 0,
530 "Client name. Default is login username."},
532 {"encryption-type", 'e', "ETYPE,[ETYPE...]", 0,
533 "Encryption types to use. ETYPE is either registered name or integer."},
535 {"realm", 'r', "REALM", 0,
536 "Realm of client and server. Default is DNS domain of local host."},
538 {"password", OPTION_AS_PASSWORD, "PASSWORD", 0,
539 "Password to decrypt response (discouraged). Default is to prompt user."},
541 /************** TGS */
543 {0, 0, 0, 0, "Options for Ticket Granting Service (TGS-OPTIONS):", 200},
545 {"client-name", OPTION_TGS_CLIENT_NAME, "NAME", 0,
546 "Client name. Default is login username. Used to locate ticket "
547 "granting ticket."},
549 {"encryption-type", 'e', "ETYPE,[ETYPE...]", 0,
550 "Encryption types to use. ETYPE is either registered name or integer."},
552 {"ticket-granter", OPTION_KDC_TICKET_GRANTER, "NAME", 0,
553 "Name of server field in the ticket to use as the ticket granter. "
554 "Defaults to \"krbtgt/REALM@REALM\" where REALM is server "
555 "realm (see --realm)."},
557 {"realm", 'r', "REALM", 0,
558 "Realm of server. Default is DNS domain of local host."},
560 {"server-name", OPTION_TGS_SERVER_NAME, "NAME", 0,
561 "Name of server."},
563 /************** LIST */
565 {0, 0, 0, 0, "Options for the List command (LIST-OPTIONS):", 300},
567 {"server-name", OPTION_LIST_SERVER_NAME, "NAME", 0,
568 "List only tickets for specified server."},
570 /************** DESTROY */
572 {0, 0, 0, 0, "Options for the Destroy command (DESTROY-OPTIONS):", 400},
574 {"server-name", OPTION_DESTROY_SERVER_NAME, "NAME", 0,
575 "Destroy only tickets for specified server."},
577 /************** CLIENT */
579 {0, 0, 0, 0, "Options for Network Client (CLIENT-OPTIONS):", 500},
581 {"options", OPTION_CLIENT_AP_OPTIONS, "OPTION[,OPTION...]", 0,
582 "Indicate AP-OPTIONS separated by comma (,) or whitespace. "
583 "Options are integers (ORed together) or the pre-defined strings "
584 "\"use-session-key\" indicating that the ticket is encrypted in the "
585 "server's TGT key rather than its own key (not implemented) or "
586 "\"mutual-required\" indicating that mutual authentication is required."},
588 {"realm", 'r', "REALM", 0,
589 "Realm of server. Defaults to DNS domain of local host."},
591 {"server-name", OPTION_CLIENT_SERVER_NAME, "NAME", 0,
592 "Name of server. Defaults to \"sample/REALM\" where REALM "
593 "is realm of server (see --realm)."},
595 /************** SERVER */
597 {0, 0, 0, 0, "Options for Network Server (SERVER-OPTIONS):", 600},
599 {"client-name", OPTION_KDC_CLIENT_NAME, "NAME", 0,
600 "Client name. Default is login username."},
602 {"key-value", OPTION_SERVER_KEY_VALUE, "KEY", 0,
603 "Cipher key of server."},
605 {"realm", 'r', "REALM", 0,
606 "Realm of server. Defaults to DNS domain of local host."},
608 {"server-name", OPTION_SERVER_SERVER_NAME, "NAME", 0,
609 "Name of server. Defaults to \"sample/REALM\" where REALM "
610 "is realm of server (see --realm)."},
612 {"password", OPTION_SERVER_PASSWORD, "PASSWORD", 0,
613 "Password to decrypt response (discouraged)."},
615 /************** AP */
617 {0, 0, 0, 0,
618 "Options for low-level Client/Server Authentication (AP-OPTIONS):", 700},
620 {"data", OPTION_AP_AUTHENTICATOR_DATA, "B64STRING", 0,
621 "Base64 encoded data to checksum in generated authenticator. "
622 "By default checksum is omitted (indicating no application payload)."},
624 {"read-ap-request-file", OPTION_AP_REQUEST_READ_FILE, "[TYPE,]FILE", 0,
625 "Read AP-REQ from FILE in format TYPE; TEXT (default) or DER. "
626 "Default is to generate it."},
628 {"read-data-file", OPTION_AP_AUTHENTICATOR_READ_DATA_FILE, "[TYPE,]FILE", 0,
629 "Read data to checksum in generated authenticator from FILE in format "
630 "TYPE, BASE64, HEX or BINARY (default). "
631 "By default checksum is omitted (indicating no application payload)."},
633 {"realm", 'r', "REALM", 0,
634 "Realm of server. Defaults to DNS domain of local host. Used for "
635 "locating the ticket to use."},
637 {"server-name", OPTION_AP_SERVER_NAME, "NAME", 0,
638 "Name of server. Defaults to \"krbtgt.DEFAULTREALM\" where DEFAULTREALM "
639 "is realm of server. Used for locating the ticket to use."},
641 {"write-authenticator-file", OPTION_AP_AUTHENTICATOR_WRITE_FILE,
642 "[TYPE,]FILE", 0,
643 "Write authenticator to FILE in format TYPE; TEXT (default) or DER. "
644 "Not written by default."},
646 {"write-ap-request-file", OPTION_AP_REQUEST_WRITE_FILE, "[TYPE,]FILE", 0,
647 "Write AP-REQ to FILE in format TYPE; TEXT (default) or DER. "
648 "Default is stdout."},
650 /************** CRYPTO */
652 {0, 0, 0, 0,
653 "Options for low-level cryptography (CRYPTO-OPTIONS):", 800},
655 {"algorithm", OPTION_CRYPTO_ALGORITHM, "ALGORITHM", 0,
656 "Cipher algorithm, expressed either as the etype integer or "
657 "the registered name."},
659 {"client-name", OPTION_KDC_CLIENT_NAME, "NAME", 0,
660 "Username. Default is login name."},
662 {"decrypt", OPTION_CRYPTO_DECRYPT, 0, 0,
663 "Decrypt data."},
665 {"encrypt", OPTION_CRYPTO_ENCRYPT, 0, 0,
666 "Encrypt data."},
668 {"key-usage", OPTION_CRYPTO_KEY_USAGE, "KEYUSAGE", 0,
669 "Encrypt or decrypt using specified key usage. Default is 0, which means no "
670 "key derivation are performed."},
672 {"key-value", OPTION_CRYPTO_KEY_VALUE, "KEY", 0,
673 "Base64 encoded key value."},
675 {"key-version", OPTION_CRYPTO_KEY_VERSION, "INTEGER", 0,
676 "Version number of key."},
678 {"password", OPTION_CRYPTO_PASSWORD, "PASSWORD", 0,
679 "Password used to generate key. --client-name and --realm also modify "
680 "the computed key value."},
682 {"random", OPTION_CRYPTO_RANDOM, 0, 0,
683 "Generate key from random data."},
685 {"read-key-file", OPTION_CRYPTO_READ_KEY_FILE, "FILE", 0,
686 "Read cipher key from FILE"},
688 {"read-data-file", OPTION_CRYPTO_READ_DATA_FILE, "[TYPE,]FILE", 0,
689 "Read data from FILE in TYPE, BASE64, HEX or BINARY (default)."},
691 {"realm", 'r', "REALM", 0,
692 "Realm of principal. Defaults to DNS domain of local host. "},
694 {"salt", OPTION_CRYPTO_SALT, "SALT", 0,
695 "Salt to use when --password is specified. Defaults to using the"
696 "username (--client-name) and realm (--realm)."},
698 {"parameter", OPTION_CRYPTO_PARAMETER, "STRING", 0,
699 "String-to-key parameter to use when --password is specified. This data "
700 "is specific for each encryption algorithm and rarely needed."},
702 {"write-key-file", OPTION_CRYPTO_WRITE_KEY_FILE, "FILE", 0,
703 "Append cipher key to FILE"},
705 {"write-data-file", OPTION_CRYPTO_WRITE_DATA_FILE, "[TYPE,]FILE", 0,
706 "Write data to FILE in TYPE, BASE64, HEX or BINARY (default)."},
708 /************** KDC */
710 {0, 0, 0, 0,
711 "Options for low-level Key Distribution Services (KDC-OPTIONS):", 900},
713 {"client-name", OPTION_KDC_CLIENT_NAME, "NAME", 0,
714 "Client name. Default is login username. Only for AS."},
716 {"encryption-type", 'e', "ETYPE,[ETYPE...]", 0,
717 "Encryption types to use. ETYPE is either registered name or integer."},
719 {"force-as", OPTION_KDC_FORCE_AS, 0, 0,
720 "Force AS mode. Default is to use TGS iff a TGT is found."},
722 {"force-tgs", OPTION_KDC_FORCE_TGS, 0, 0,
723 "Force TGS mode. Default is to use TGS iff a TGT is found."},
725 {"realm", 'r', "REALM", 0,
726 "Realm of server. Default is DNS domain of local host. For AS, this also "
727 "indicates realm of client."},
729 {"server", OPTION_KDC_SERVER, "HOST", 0,
730 "Send request to HOST. Default uses address from configuration file."},
732 {"server-name", OPTION_KDC_SERVER_NAME, "NAME", 0,
733 "Server name. Default is \"krbtgt/REALM\" where REALM is server "
734 "realm (see --realm)."},
736 {"ticket-granter", OPTION_KDC_TICKET_GRANTER, "NAME", 0,
737 "Service name in ticket to use for authenticating request. Only for TGS. "
738 "Defaults to \"krbtgt/REALM@REALM\" where REALM is server "
739 "realm (see --realm)."},
741 {"key-value", OPTION_KDC_KEY_VALUE, "KEY", 0,
742 "Cipher key to decrypt response (discouraged)."},
744 {"read-kdc-request-file", OPTION_KDC_READ_REQUEST_FILE, "[TYPE,]FILE", 0,
745 "Read KDC-REQ from FILE in format TYPE; TEXT (default) or DER. "
746 "Default is to generate it."},
748 {"read-kdc-response-file", OPTION_KDC_READ_RESPONSE_FILE, "[TYPE,]FILE", 0,
749 "Read KDC-REP from FILE in format TYPE; TEXT (default) or DER. "
750 "Default is to receive it from server."},
752 {"request", OPTION_KDC_REQUEST, 0, 0,
753 "Only generate the request."},
755 {"response", OPTION_KDC_RESPONSE, 0, 0,
756 "Only parse request and response and output ticket."},
758 {"sendrecv", OPTION_KDC_SENDRECV, 0, 0,
759 "Only send request and receive response."},
761 {"password", OPTION_CRYPTO_PASSWORD, "PASSWORD", 0,
762 "Password to decrypt response (discouraged). Only for AS."},
764 {"write-ap-request-file", OPTION_KDC_WRITE_AP_REQUEST_FILE, "[TYPE,]FILE",
766 "Write AP-REQ to FILE in TYPE, either TEXT (default) or DER. "
767 "Only for TGS. Not written by default."},
769 {"write-authenticator-file", OPTION_KDC_WRITE_AUTHENTICATOR_FILE,
770 "[TYPE,]FILE", 0,
771 "Write Authenticator to FILE in TYPE, either TEXT (default) or DER. "
772 "Only for TGS. Not written by default."},
774 {"write-kdc-request-file", OPTION_KDC_WRITE_REQUEST_FILE, "[TYPE,]FILE", 0,
775 "Write KDC-REQ to FILE in format TYPE; TEXT (default) or DER. "
776 "Not written by default."},
778 {"write-kdc-response-file", OPTION_KDC_WRITE_RESPONSE_FILE, "[TYPE,]FILE",
780 "Write KDC-REP to FILE in format TYPE; TEXT (default) or DER. "
781 "Not written by default."},
783 /************** OTHER */
785 {0, 0, 0, 0, "Other options:", 1000},
787 {"verbose", 'v', 0, 0,
788 "Produce verbose output.",},
790 {"verbose-library", OPTION_VERBOSE_LIBRARY, 0, 0,
791 "Produce verbose output in the library.",},
793 {"quiet", 'q', 0, 0,
794 "Don't produce any output."},
796 {"silent", 0, 0, OPTION_ALIAS},
798 {"system-configuration-file", 's', "FILE", 0,
799 "Read system wide configuration from file. Default is " SYSTEMCFGFILE
800 "."},
802 {"configuration-file", 'c', "FILE", 0,
803 "Read user configuration from file. Default is ~/.shishi/config."},
805 {"library-options", 'o', "STRING", 0,
806 "Parse STRING as a configuration file statement."},
808 {"ticket-file", 't', "FILE", 0,
809 "Read tickets from FILE. Default is $HOME/.shishi/tickets."},
811 {"ticket-write-file", 'w', "FILE", 0,
812 "Write tickets to FILE. Default is to write them back to ticket file."},
814 /************** EXAMPLES */
816 {0, 0, 0, 0, "Examples:", 2000},
818 {"shishi as", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
819 "Get a ticket granting ticket from the default KDC server for the "
820 "default user and realm."},
822 {"shishi tgs --server-name=imap", 0, 0, OPTION_DOC | OPTION_NO_USAGE,
823 "Get a ticket for the imap server."},
825 {"shishi list --server-name=krbtgt/JOSEFSSON.ORG@JOSEFSSON.ORG",
826 0, 0, OPTION_DOC | OPTION_NO_USAGE,
827 "List tickets for the Ticket Granting Service in the JOSEFSSON.ORG realm."},
832 static struct argp argp = {
833 options,
834 parse_opt,
835 "[COMMAND [COMMAND-OPTION...]]\n"
836 "as [AS-OPTION...]\n"
837 "tgs [TGS-OPTION...]\n"
838 "list [LIST-OPTION...]\n"
839 "destroy [DESTROY-OPTION...]\n"
840 "client [CLIENT-OPTION...]\n"
841 "server [SERVER-OPTION...]\n"
842 "ap [AP-OPTION...]\n" "crypto [CRYPTO-OPTION...]\n" "kdc [KDC-OPTION...]",
843 "Shishi -- A RFC 1510(bis) implementation"
847 main (int argc, char *argv[])
849 struct arguments arg;
850 Shishi *handle;
851 int rc;
853 setlocale (LC_ALL, "");
854 bindtextdomain (PACKAGE, LOCALEDIR);
855 textdomain (PACKAGE);
857 memset (&arg, 0, sizeof (arg));
858 argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &arg);
860 rc = shishi_init_with_paths (&handle, arg.ticketfile,
861 arg.systemcfgfile, arg.usercfgfile);
862 if (rc == SHISHI_HANDLE_ERROR)
863 error (1, 0, "Internal error: could not initialize shishi\n");
865 rc = shishi_cfg_clientkdcetype_set (handle, arg.etypes);
866 if (rc != SHISHI_OK)
867 error (1, 0, "Could not set encryption types: %s\n", shishi_strerror (rc));
869 rc = shishi_cfg (handle, arg.lib_options);
870 if (rc != SHISHI_OK)
871 error (1, 0, "Could not read library options: %s\n", shishi_strerror (rc));
873 if (arg.verbose_library)
875 rc = shishi_cfg (handle, "verbose");
876 if (rc != SHISHI_OK)
877 error (1, 0, "Could not make library verbose: %s\n",
878 shishi_strerror (rc));
881 if (arg.cname != NULL)
882 shishi_principal_default_set (handle, arg.cname);
884 if (arg.realm != NULL)
885 shishi_realm_default_set (handle, arg.realm);
887 if (arg.tgtname == NULL)
889 asprintf (&arg.tgtname, "krbtgt/%s",
890 shishi_realm_default (handle));
891 if (arg.tgtname == NULL)
892 error (1, 0, "Could not allocate TGT name.");
895 rc = 1;
897 switch (arg.command)
899 case COMMAND_AS:
901 Shishi_as *as;
902 Shishi_tkt *tkt;
904 rc = shishi_as (handle, &as);
905 if (rc == SHISHI_OK)
906 rc = shishi_as_sendrecv (as);
907 if (rc == SHISHI_OK)
908 rc = shishi_as_rep_process (as, NULL, arg.password);
909 if (rc != SHISHI_OK)
911 printf ("AS exchange failed: %s\n%s\n", shishi_strerror (rc),
912 shishi_strerror_details (handle));
913 if (rc == SHISHI_GOT_KRBERROR)
914 shishi_krberror_pretty_print (handle, stdout,
915 shishi_as_krberror (as));
916 break;
919 if (arg.verbose)
921 shishi_kdcreq_print (handle, stdout, shishi_as_req (as));
922 shishi_kdcrep_print (handle, stdout, shishi_as_rep (as));
923 shishi_enckdcreppart_print
924 (handle, stdout, shishi_tkt_enckdcreppart (shishi_as_tkt (as)));
927 tkt = shishi_as_tkt (as);
929 if (!arg.silent)
931 shishi_tkt_pretty_print (tkt, stdout);
932 shishi_tkt_lastreq_pretty_print (tkt, stdout);
935 rc = shishi_tkts_add (shishi_tkts_default (handle), tkt);
936 if (rc != SHISHI_OK)
937 printf ("Could not add ticket: %s", shishi_strerror (rc));
939 break;
941 case COMMAND_TGS:
943 Shishi_tgs *tgs;
944 Shishi_tkt *tgt;
945 Shishi_tkt *tkt;
947 tgt = shishi_tkts_find_for_clientserver
948 (shishi_tkts_default (handle),
949 shishi_principal_default (handle), arg.tgtname);
950 if (tgt == NULL)
952 printf ("TGT not found. Please use the AS command first.\n");
953 rc = !SHISHI_OK;
954 break;
957 rc = shishi_tgs (handle, &tgs);
958 shishi_tgs_tgtkt_set (tgs, tgt);
959 if (rc == SHISHI_OK)
960 rc = shishi_tgs_set_server (tgs, arg.sname ?
961 arg.sname : arg.tgtname);
962 if (rc == SHISHI_OK)
963 rc = shishi_tgs_req_build (tgs);
964 if (rc == SHISHI_OK)
965 rc = shishi_tgs_sendrecv (tgs);
966 if (rc == SHISHI_OK)
967 rc = shishi_tgs_rep_process (tgs);
968 if (rc != SHISHI_OK)
970 printf ("TGS exchange failed: %s\n%s\n", shishi_strerror (rc),
971 shishi_strerror_details (handle));
972 if (rc == SHISHI_GOT_KRBERROR)
973 shishi_krberror_pretty_print (handle, stdout,
974 shishi_tgs_krberror (tgs));
975 break;
978 if (arg.verbose)
980 shishi_authenticator_print
981 (handle, stdout, shishi_ap_authenticator (shishi_tgs_ap (tgs)));
982 shishi_apreq_print
983 (handle, stdout, shishi_ap_req (shishi_tgs_ap (tgs)));
984 shishi_kdcreq_print (handle, stdout, shishi_tgs_req (tgs));
985 shishi_kdcrep_print (handle, stdout, shishi_tgs_rep (tgs));
988 tkt = shishi_tgs_tkt (tgs);
990 if (!arg.silent)
992 shishi_tkt_lastreq_pretty_print (tkt, stdout);
993 shishi_tkt_pretty_print (tkt, stdout);
996 rc = shishi_tkts_add (shishi_tkts_default (handle), tkt);
997 if (rc != SHISHI_OK)
998 printf ("Could not add ticket: %s", shishi_strerror (rc));
1000 break;
1002 case COMMAND_LIST:
1003 if (!arg.silent)
1004 printf (_("Tickets in `%s':\n"), shishi_tkts_default_file (handle));
1006 rc = shishi_tkts_print_for_service (shishi_tkts_default (handle),
1007 stdout, arg.sname);
1008 if (rc != SHISHI_OK)
1009 fprintf (stderr, "Could not list tickets: %s", shishi_strerror (rc));
1010 break;
1012 case COMMAND_DESTROY:
1014 int i, removed = 0;
1015 for (i = 0; i < shishi_tkts_size (shishi_tkts_default (handle)); i++)
1017 if (arg.sname &&
1018 !shishi_tkt_server_p (shishi_tkts_nth
1019 (shishi_tkts_default (handle),
1020 i), arg.sname))
1021 continue;
1023 if (arg.verbose)
1025 printf ("Removing ticket:\n");
1026 shishi_tkt_pretty_print (shishi_tkts_nth
1027 (shishi_tkts_default
1028 (handle), i), stdout);
1031 rc = shishi_tkts_remove (shishi_tkts_default (handle), i);
1032 if (rc != SHISHI_OK)
1033 fprintf (stderr, "Could not destroy ticket %d:\n%s\n", i,
1034 shishi_strerror (rc));
1035 i--;
1036 removed++;
1038 if (removed == 0)
1039 printf ("No tickets removed.\n");
1040 else if (removed == 1)
1041 printf ("1 ticket removed.\n");
1042 else
1043 printf ("%d tickets removed.\n", removed);
1044 rc = SHISHI_OK;
1046 break;
1048 case COMMAND_CLIENT:
1049 rc = client (handle, arg);
1050 break;
1052 case COMMAND_SERVER:
1053 rc = server (handle, arg);
1054 break;
1056 case COMMAND_AP:
1057 rc = ap (handle, arg);
1058 break;
1060 case COMMAND_CRYPTO:
1061 rc = crypto (handle, arg);
1062 if (rc != SHISHI_OK)
1063 fprintf (stderr, "Operation failed:\n%s\n%s\n",
1064 shishi_strerror (rc), shishi_strerror_details (handle));
1065 break;
1067 case COMMAND_KDC:
1068 //rc = kdc (handle, arg);
1069 break;
1071 default:
1073 Shishi_tkt *tkt;
1075 tkt = shishi_tkts_get_for_server (shishi_tkts_default (handle),
1076 arg.tgtname);
1077 if (!tkt)
1079 printf ("Could not get ticket for `%s'.\n", arg.tgtname);
1080 rc = !SHISHI_OK;
1081 break;
1084 rc = shishi_tkt_pretty_print (tkt, stdout);
1085 if (rc != SHISHI_OK)
1086 fprintf (stderr, "Operation failed:\n%s\n%s\n",
1087 shishi_strerror (rc), shishi_strerror_details (handle));
1089 break;
1092 shishi_tkts_expire (shishi_tkts_default (handle));
1094 if (arg.ticketwritefile)
1095 shishi_tkts_default_file_set (handle, arg.ticketwritefile);
1097 shishi_done (handle);
1099 return rc == SHISHI_OK ? 0 : 1;