dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / snmp / apps / encode_keychange.c
blob9dc2bdd201680c13aff69af2a6b4e54262bf0a69
1 /*
2 * encode_keychange.c
4 * Collect information to build a KeyChange encoding, per the textual
5 * convention given in RFC 2274, Section 5. Compute the value and
6 * dump to stdout as a string of hex nibbles.
9 * Passphrase material may come from many sources. The following are
10 * checked in order (see get_user_passphrases()):
11 * - Prompt always if -f is given.
12 * - Commandline arguments.
13 * - PASSPHRASE_FILE.
14 * - Prompts on stdout. Use -P to turn off prompt tags.
17 * FIX Better name?
18 * FIX Change encode_keychange() to take random bits?
19 * FIX QUITFUN not quite appropriate here...
20 * FIX This is slow...
23 #include <net-snmp/net-snmp-config.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #if HAVE_STRING_H
33 #include <string.h>
34 #else
35 #include <strings.h>
36 #endif
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
39 #endif
41 #if HAVE_WINSOCK_H
42 #include <winsock.h>
43 #endif
45 #include <net-snmp/net-snmp-includes.h>
47 #include <stdlib.h>
50 * Globals, &c...
52 char *local_progname;
53 char *local_passphrase_filename;
55 #define NL "\n"
57 #define USAGE "Usage: %s [-fhPvV] -t (md5|sha1) [-O \"<old_passphrase>\"][-N \"<new_passphrase>\"][-E [0x]<engineID>]"
59 #define OPTIONLIST "E:fhN:O:Pt:vVD"
61 #define PASSPHRASE_DIR ".snmp"
63 * Rooted at $HOME.
65 #define PASSPHRASE_FILE "passphrase.ek"
67 * Format: two lines containing old and new passphrases, nothing more.
69 * XXX Add creature comforts like: comments and
70 * tokens identifying passphrases, separate directory check,
71 * check in current directory (?), traverse a path of
72 * directories (?)...
73 * FIX Better name?
77 int forcepassphrase = 0, /* Always prompt for passphrases. */
78 promptindicator = 1, /* Output an indicator that input
79 * is requested. */
80 visible = 0, /* Echo passphrases to terminal. */
81 verbose = 0; /* Output progress to stderr. */
82 size_t engineid_len = 0;
84 u_char *engineid = NULL; /* Both input & final binary form. */
85 char *newpass = NULL, *oldpass = NULL;
87 char *transform_type_input = NULL;
89 const oid *transform_type = NULL; /* Type of HMAC hash to use. */
94 * Prototypes.
96 void usage_to_file(FILE * ofp);
97 void usage_synopsis(FILE * ofp);
98 int get_user_passphrases(void);
99 int snmp_ttyecho(const int fd, const int echo);
100 char *snmp_getpassphrase(const char *prompt, int fvisible);
102 #ifdef WIN32
103 #define HAVE_GETPASS 1
104 char *getpass(const char *prompt);
105 int isatty(int);
106 int _cputs(const char *);
107 int _getch(void);
108 #endif
110 /*******************************************************************-o-******
113 main(int argc, char **argv)
115 int rval = SNMPERR_SUCCESS;
116 size_t oldKu_len = SNMP_MAXBUF_SMALL,
117 newKu_len = SNMP_MAXBUF_SMALL,
118 oldkul_len = SNMP_MAXBUF_SMALL,
119 newkul_len = SNMP_MAXBUF_SMALL, keychange_len = SNMP_MAXBUF_SMALL;
121 char *s = NULL;
122 u_char oldKu[SNMP_MAXBUF_SMALL],
123 newKu[SNMP_MAXBUF_SMALL],
124 oldkul[SNMP_MAXBUF_SMALL],
125 newkul[SNMP_MAXBUF_SMALL], keychange[SNMP_MAXBUF_SMALL];
127 int i;
128 int arg = 1;
130 local_progname = argv[0];
131 local_passphrase_filename = (char *) malloc(sizeof(PASSPHRASE_DIR) +
132 sizeof(PASSPHRASE_FILE) +
134 if (!local_passphrase_filename) {
135 fprintf(stderr, "%s: out of memory!", local_progname);
136 exit(-1);
138 sprintf(local_passphrase_filename, "%s/%s", PASSPHRASE_DIR,
139 PASSPHRASE_FILE);
144 * Parse.
146 for (; (arg < argc) && (argv[arg][0] == '-'); arg++) {
147 switch (argv[arg][1]) {
148 case 'D':
149 snmp_set_do_debugging(1);
150 break;
151 case 'E':
152 engineid = (u_char *) argv[++arg];
153 break;
154 case 'f':
155 forcepassphrase = 1;
156 break;
157 case 'N':
158 newpass = argv[++arg];
159 break;
160 case 'O':
161 oldpass = argv[++arg];
162 break;
163 case 'P':
164 promptindicator = 0;
165 break;
166 case 't':
167 transform_type_input = argv[++arg];
168 break;
169 case 'v':
170 verbose = 1;
171 break;
172 case 'V':
173 visible = 1;
174 break;
175 case 'h':
176 rval = 0;
177 default:
178 usage_to_file(stdout);
179 exit(rval);
183 if (!transform_type_input) {
184 fprintf(stderr, "The -t option is mandatory.\n");
185 usage_synopsis(stdout);
186 exit(1000);
192 * Convert and error check transform_type.
194 if (!strcmp(transform_type_input, "md5")) {
195 transform_type = usmHMACMD5AuthProtocol;
197 } else if (!strcmp(transform_type_input, "sha1")) {
198 transform_type = usmHMACSHA1AuthProtocol;
200 } else {
201 fprintf(stderr,
202 "Unrecognized hash transform: \"%s\".\n",
203 transform_type_input);
204 usage_synopsis(stderr);
205 QUITFUN(rval = SNMPERR_GENERR, main_quit);
208 if (verbose) {
209 fprintf(stderr, "Hash:\t\t%s\n",
210 (transform_type == usmHMACMD5AuthProtocol)
211 ? "usmHMACMD5AuthProtocol" : "usmHMACSHA1AuthProtocol");
217 * Build engineID. Accept hex engineID as the bits
218 * "in-and-of-themselves", otherwise create an engineID with the
219 * given string as text.
221 * If no engineID is given, lookup the first IP address for the
222 * localhost and use that (see setup_engineID()).
224 if (engineid && (tolower(*(engineid + 1)) == 'x')) {
225 engineid_len = hex_to_binary2(engineid + 2,
226 strlen((char *) engineid) - 2,
227 (char **) &engineid);
228 DEBUGMSGTL(("encode_keychange", "engineIDLen: %d\n",
229 engineid_len));
230 } else {
231 engineid_len = setup_engineID(&engineid, (char *) engineid);
235 #ifdef SNMP_TESTING_CODE
236 if (verbose) {
237 fprintf(stderr, "EngineID:\t%s\n",
239 * XXX =
240 */ dump_snmpEngineID(engineid, &engineid_len));
242 #endif
246 * Get passphrases from user.
248 rval = get_user_passphrases();
249 QUITFUN(rval, main_quit);
251 if (strlen(oldpass) < USM_LENGTH_P_MIN) {
252 fprintf(stderr, "Old passphrase must be greater than %d "
253 "characters in length.\n", USM_LENGTH_P_MIN);
254 QUITFUN(rval = SNMPERR_GENERR, main_quit);
256 } else if (strlen(newpass) < USM_LENGTH_P_MIN) {
257 fprintf(stderr, "New passphrase must be greater than %d "
258 "characters in length.\n", USM_LENGTH_P_MIN);
259 QUITFUN(rval = SNMPERR_GENERR, main_quit);
262 if (verbose) {
263 fprintf(stderr,
264 "Old passphrase:\t%s\nNew passphrase:\t%s\n",
265 oldpass, newpass);
271 * Compute Ku and Kul's from old and new passphrases, then
272 * compute the keychange string & print it out.
274 rval = sc_init();
275 QUITFUN(rval, main_quit);
278 rval = generate_Ku(transform_type, USM_LENGTH_OID_TRANSFORM,
279 (u_char *) oldpass, strlen(oldpass),
280 oldKu, &oldKu_len);
281 QUITFUN(rval, main_quit);
284 rval = generate_Ku(transform_type, USM_LENGTH_OID_TRANSFORM,
285 (u_char *) newpass, strlen(newpass),
286 newKu, &newKu_len);
287 QUITFUN(rval, main_quit);
290 DEBUGMSGTL(("encode_keychange", "EID (%d): ", engineid_len));
291 for (i = 0; i < (int) engineid_len; i++)
292 DEBUGMSGTL(("encode_keychange", "%02x", (int) (engineid[i])));
293 DEBUGMSGTL(("encode_keychange", "\n"));
295 DEBUGMSGTL(("encode_keychange", "old Ku (%d) (from %s): ", oldKu_len,
296 oldpass));
297 for (i = 0; i < (int) oldKu_len; i++)
298 DEBUGMSGTL(("encode_keychange", "%02x", (int) (oldKu[i])));
299 DEBUGMSGTL(("encode_keychange", "\n"));
301 rval = generate_kul(transform_type, USM_LENGTH_OID_TRANSFORM,
302 engineid, engineid_len,
303 oldKu, oldKu_len, oldkul, &oldkul_len);
304 QUITFUN(rval, main_quit);
307 DEBUGMSGTL(("encode_keychange", "generating old Kul (%d) (from Ku): ",
308 oldkul_len));
309 for (i = 0; i < (int) oldkul_len; i++)
310 DEBUGMSGTL(("encode_keychange", "%02x", (int) (oldkul[i])));
311 DEBUGMSGTL(("encode_keychange", "\n"));
313 rval = generate_kul(transform_type, USM_LENGTH_OID_TRANSFORM,
314 engineid, engineid_len,
315 newKu, newKu_len, newkul, &newkul_len);
316 QUITFUN(rval, main_quit);
318 DEBUGMSGTL(("encode_keychange", "generating new Kul (%d) (from Ku): ",
319 oldkul_len));
320 for (i = 0; i < (int) newkul_len; i++)
321 DEBUGMSGTL(("encode_keychange", "%02x", newkul[i]));
322 DEBUGMSGTL(("encode_keychange", "\n"));
324 rval = encode_keychange(transform_type, USM_LENGTH_OID_TRANSFORM,
325 oldkul, oldkul_len,
326 newkul, newkul_len, keychange, &keychange_len);
327 QUITFUN(rval, main_quit);
331 binary_to_hex(keychange, keychange_len, &s);
332 printf("%s%s\n", (verbose) ? "KeyChange string:\t" : "", /* XXX stdout */
337 * Cleanup.
339 main_quit:
340 snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
341 NULL);
344 SNMP_ZERO(oldpass, strlen(oldpass));
345 SNMP_ZERO(newpass, strlen(newpass));
347 SNMP_ZERO(oldKu, oldKu_len);
348 SNMP_ZERO(newKu, newKu_len);
350 SNMP_ZERO(oldkul, oldkul_len);
351 SNMP_ZERO(newkul, newkul_len);
353 SNMP_ZERO(s, strlen(s));
355 return rval;
357 } /* end main() */
362 /*******************************************************************-o-******
364 void
365 usage_synopsis(FILE * ofp)
367 fprintf(ofp, USAGE "\n\
369 -E [0x]<engineID> EngineID used for kul generation.\n\
370 -f Force passphrases to be read from stdin.\n\
371 -h Help.\n\
372 -N \"<new_passphrase>\" Passphrase used to generate new Ku.\n\
373 -O \"<old_passphrase>\" Passphrase used to generate old Ku.\n\
374 -P Turn off prompt indicators.\n\
375 -t md5 | sha1 HMAC hash transform type.\n\
376 -v Verbose.\n\
377 -V Visible. Echo passphrases to terminal.\n\
378 " NL, local_progname);
380 } /* end usage_synopsis() */
382 void
383 usage_to_file(FILE * ofp)
385 char *s;
387 usage_synopsis(ofp);
389 fprintf(ofp, "\n\
390 Only -t is mandatory. The transform is used to convert P=>Ku, convert\n\
391 Ku=>Kul, and to hash the old Kul with the random bits.\n\
393 Passphrase will be taken from the first successful source as follows:\n\
394 a) Commandline options,\n\
395 b) The file \"%s/%s\",\n\
396 c) stdin -or- User input from the terminal.\n\
398 -f will require reading from the stdin/terminal, ignoring a) and b).\n\
399 -P will prevent prompts for passphrases to stdout from being printed.\n\
401 <engineID> is intepreted as a hex string when preceeded by \"0x\",\n\
402 otherwise it is created to contain \"text\". If nothing is given,\n\
403 <engineID> is constructed from the first IP address for the local host.\n\
404 " NL, (s = getenv("HOME")) ? s : "$HOME", local_passphrase_filename);
408 * FIX -- make this possible?
409 * -r [0x]<random_bits> Random bits used in KeyChange XOR.
411 * <engineID> and <random_bits> are intepreted as hex strings when
412 * preceeded by \"0x\", otherwise <engineID> is created to contain \"text\"
413 * and <random_bits> are the same as the ascii input.
415 * <random_bits> will be generated by SCAPI if not given. If value is
416 * too long, it will be truncated; if too short, the remainder will be
417 * filled in with zeros.
420 } /* end usage() */
424 * this defined for HPUX aCC because the aCC doesn't drop the
427 * snmp_parse_args.c functionality if compile with -g, PKY
430 void
431 usage(void)
433 usage_to_file(stdout);
440 /*******************************************************************-o-******
441 * get_user_passphrases
443 * Returns:
444 * SNMPERR_SUCCESS Success.
445 * SNMPERR_GENERR Otherwise.
448 * Acquire new and old passphrases from the user:
450 * + Always prompt if 'forcepassphrase' is set.
451 * + Use given arguments if they are defined.
452 * + Otherwise read file format from PASSPHRASE_FILE.
453 * Sanity check existence and permissions of the path.
454 * ASSUME for now that PASSPHRASE_FILE is rooted only at $HOME.
455 * + Otherwise prompt user for passphrase(s).
456 * Echo input if 'visible' is set.
457 * Turning off 'promptindicator' makes piping in input cleaner.
459 * NOTE Only using forcepassphrase mandates taking both passphrases
460 * from the same source. Otherwise processing continues until both
461 * passphrases are defined.
464 get_user_passphrases(void)
466 int rval = SNMPERR_SUCCESS;
467 size_t len;
469 char *obuf = NULL, *nbuf = NULL;
471 char path[SNMP_MAXBUF], buf[SNMP_MAXBUF], *s = NULL;
473 struct stat statbuf;
474 FILE *fp;
479 * Allow prompts to the user to override all other sources.
480 * Nothing to do otherwise if oldpass and newpass are already defined.
482 if (forcepassphrase)
483 goto get_user_passphrases_prompt;
484 if (oldpass && newpass)
485 goto get_user_passphrases_quit;
490 * Read passphrases out of PASSPHRASE_FILE. Sanity check the
491 * path for existence and access first. Refuse to read
492 * if the permissions are wrong.
494 s = getenv("HOME");
495 snprintf(path, sizeof(path), "%s/%s", s, PASSPHRASE_DIR);
496 path[ sizeof(path)-1 ] = 0;
499 * Test directory.
501 if (stat(path, &statbuf) < 0) {
502 fprintf(stderr, "Cannot access directory \"%s\".\n", path);
503 QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit);
504 #ifndef WIN32
505 } else if (statbuf.st_mode & (S_IRWXG | S_IRWXO)) {
506 fprintf(stderr,
507 "Directory \"%s\" is accessible by group or world.\n",
508 path);
509 QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit);
510 #endif /* !WIN32 */
514 * Test file.
516 snprintf(path, sizeof(path), "%s/%s", s, local_passphrase_filename);
517 path[ sizeof(path)-1 ] = 0;
518 if (stat(path, &statbuf) < 0) {
519 fprintf(stderr, "Cannot access file \"%s\".\n", path);
520 QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit);
521 #ifndef WIN32
522 } else if (statbuf.st_mode & (S_IRWXG | S_IRWXO)) {
523 fprintf(stderr,
524 "File \"%s\" is accessible by group or world.\n", path);
525 QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit);
526 #endif /* !WIN32 */
530 * Open the file.
532 if ((fp = fopen(path, "r")) == NULL) {
533 fprintf(stderr, "Cannot open \"%s\".", path);
534 QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit);
538 * Read 1st line.
540 if (!fgets(buf, sizeof(buf), fp)) {
541 if (verbose) {
542 fprintf(stderr, "Passphrase file \"%s\" is empty...\n", path);
544 goto get_user_passphrases_prompt;
546 } else if (!oldpass) {
547 len = strlen(buf);
548 if (buf[len - 1] == '\n')
549 buf[--len] = '\0';
550 oldpass = (char *) calloc(1, len + 1);
551 if (oldpass)
552 memcpy(oldpass, buf, len + 1);
555 * Read 2nd line.
557 if (!fgets(buf, sizeof(buf), fp)) {
558 if (verbose) {
559 fprintf(stderr, "Only one line in file \"%s\"...\n", path);
562 } else if (!newpass) {
563 len = strlen(buf);
564 if (buf[len - 1] == '\n')
565 buf[--len] = '\0';
566 newpass = (char *) calloc(1, len + 1);
567 if (newpass)
568 memcpy(newpass, buf, len + 1);
571 if (oldpass && newpass)
572 goto get_user_passphrases_quit;
577 * Prompt the user for passphrase entry. Visible prompts
578 * may be omitted, and invisible entry may turned off.
580 get_user_passphrases_prompt:
581 if (forcepassphrase) {
582 oldpass = newpass = NULL;
585 if (!oldpass) {
586 oldpass = obuf
587 = snmp_getpassphrase((promptindicator) ? "Old passphrase: " :
588 "", visible);
590 if (!newpass) {
591 newpass = nbuf
592 = snmp_getpassphrase((promptindicator) ? "New passphrase: " :
593 "", visible);
599 * Check that both passphrases were defined.
601 if (oldpass && newpass) {
602 goto get_user_passphrases_quit;
603 } else {
604 rval = SNMPERR_GENERR;
608 get_user_passphrases_quit:
609 SNMP_ZERO(buf, SNMP_MAXBUF);
611 if (obuf != oldpass) {
612 SNMP_ZERO(obuf, strlen(obuf));
613 SNMP_FREE(obuf);
615 if (nbuf != newpass) {
616 SNMP_ZERO(nbuf, strlen(nbuf));
617 SNMP_FREE(nbuf);
620 return rval;
622 } /* end get_user_passphrases() */
624 /*******************************************************************-o-******
625 * snmp_ttyecho
627 * Parameters:
628 * fd Descriptor of terminal on which to toggle echoing.
629 * echo TRUE if echoing should be on; FALSE otherwise.
631 * Returns:
632 * Previous value of echo setting.
635 * FIX Put HAVE_TCGETATTR in autoconf?
637 #ifndef HAVE_GETPASS
638 #ifdef HAVE_TCGETATTR
639 #include <termios.h>
641 snmp_ttyecho(const int fd, const int echo)
643 struct termios tio;
644 int was_echo;
647 if (!isatty(fd))
648 return (-1);
649 tcgetattr(fd, &tio);
650 was_echo = (tio.c_lflag & ECHO) != 0;
651 if (echo)
652 tio.c_lflag |= (ECHO | ECHONL);
653 else
654 tio.c_lflag &= ~(ECHO | ECHONL);
655 tcsetattr(fd, TCSANOW, &tio);
657 return (was_echo);
659 } /* end snmp_ttyecho() */
661 #else
662 #include <sgtty.h>
664 snmp_ttyecho(const int fd, const int echo)
666 struct sgttyb ttyparams;
667 int was_echo;
670 if (!isatty(fd))
671 was_echo = -1;
672 else {
673 ioctl(fd, TIOCGETP, &ttyparams);
674 was_echo = (ttyparams.sg_flags & ECHO) != 0;
675 if (echo)
676 ttyparams.sg_flags = ttyparams.sg_flags | ECHO;
677 else
678 ttyparams.sg_flags = ttyparams.sg_flags & ~ECHO;
679 ioctl(fd, TIOCSETP, &ttyparams);
682 return (was_echo);
684 } /* end snmp_ttyecho() */
685 #endif /* HAVE_TCGETATTR */
686 #endif /* HAVE_GETPASS */
691 /*******************************************************************-o-******
692 * snmp_getpassphrase
694 * Parameters:
695 * *prompt (May be NULL.)
696 * bvisible TRUE means echo back user input.
698 * Returns:
699 * Pointer to newly allocated, null terminated string containing
700 * passphrase -OR-
701 * NULL on error.
704 * Prompt stdin for a string (or passphrase). Return a copy of the
705 * input in a null terminated string.
707 * FIX Put HAVE_GETPASS in autoconf.
709 char *
710 snmp_getpassphrase(const char *prompt, int bvisible)
712 int ti = 0;
713 size_t len;
715 char *bufp = NULL;
716 static char buffer[SNMP_MAXBUF];
718 FILE *ofp = stdout;
722 * Query stdin for a passphrase.
724 #ifdef HAVE_GETPASS
725 if (isatty(0)) {
726 return getpass((prompt) ? prompt : "");
728 #endif
730 fputs((prompt) ? prompt : "", ofp);
732 if (!bvisible) {
733 ti = snmp_ttyecho(0, 0);
736 fgets(buffer, sizeof(buffer), stdin);
738 if (!bvisible) {
739 ti = snmp_ttyecho(0, ti);
740 fputs("\n", ofp);
745 * Copy the input and zero out the read-in buffer.
747 len = strlen(buffer);
748 if (buffer[len - 1] == '\n')
749 buffer[--len] = '\0';
751 bufp = (char *) calloc(1, len + 1);
752 if (bufp)
753 memcpy(bufp, buffer, len + 1);
755 SNMP_ZERO(buffer, SNMP_MAXBUF);
758 return bufp;
760 } /* end snmp_getpassphrase() */
762 #ifdef WIN32
765 snmp_ttyecho(const int fd, const int echo)
767 return 0;
771 * stops at the first newline, carrier return, or backspace.
772 * WARNING! _getch does NOT read <Ctrl-C>
774 char *
775 getpass(const char *prompt)
777 static char pbuf[128];
778 int ch, lim;
780 _cputs(prompt);
781 for (ch = 0, lim = 0; ch != '\n' && lim < sizeof(pbuf)-1;) {
782 ch = _getch(); /* look ma, no echo ! */
783 if (ch == '\r' || ch == '\n' || ch == '\b')
784 break;
785 pbuf[lim++] = ch;
787 pbuf[lim] = '\0';
788 puts("\n");
790 return pbuf;
792 #endif /* WIN32 */